技巧:使用宏填充数组值(代码生成)

发布于 2024-11-08 19:33:50 字数 1269 浏览 0 评论 0原文

C++ 模板只是伪装的宏吗?

我正在阅读上述主题,突然我想到了这个想法:为什么不尝试编写一些可以在我们的真实代码中使用的棘手的宏(而不仅仅是作为在现实生活中无用的谜题)?

所以我想到的第一件事是:用宏填充数组值:

int f(int &i) { return ++i; }

#define e100     r5(m20)
#define m20      m5,m5,m5,m5
#define m5       r5(e1)
#define e1       f(i)  //avoiding ++i right here, to avoid UB!
#define r5(e)    e,e,e,e,e

int main() {
        int i=0;           //this is used in the macro e1
        int a[] = {e100};  //filling array values with macros!
        int n  = sizeof(a)/sizeof(int);
        cout << "count = " << n << endl;
        for(int i = 0 ; i < n ; i++ ) 
            cout << a[i] << endl;
        return 0;
}

输出:

count = 100
1
2
3
4
.
.
.
100

在线演示:http://www.ideone.com/nUYrq ideone.com/nUYrq

我们能否在紧凑性或通用性(可能两者兼而有之)方面进一步改进这个解决方案?我们可以去掉宏中需要的变量i吗?或者有其他改进吗?

我还想知道这在 C++ 和 C 中是否都是有效的代码(当然忽略打印部分)?

编辑:

我意识到对 f() 的调用顺序似乎仍然未指定。但我不确定,因为我认为数组初始化中的逗号可能与逗号运算符相同(通常)。但如果是的话,我们可以避免它吗?标准的哪一部分说它未指定

Are C++ Templates just Macros in disguise?

I was reading the above topic, and suddenly this idea came to my mind: why not try writing some tricky macros which can be used in our real code, (not just only as puzzles which are useless in real life)?

So the first thing came to mind is : filling array values with macros:

int f(int &i) { return ++i; }

#define e100     r5(m20)
#define m20      m5,m5,m5,m5
#define m5       r5(e1)
#define e1       f(i)  //avoiding ++i right here, to avoid UB!
#define r5(e)    e,e,e,e,e

int main() {
        int i=0;           //this is used in the macro e1
        int a[] = {e100};  //filling array values with macros!
        int n  = sizeof(a)/sizeof(int);
        cout << "count = " << n << endl;
        for(int i = 0 ; i < n ; i++ ) 
            cout << a[i] << endl;
        return 0;
}

Output:

count = 100
1
2
3
4
.
.
.
100

Online demo : http://www.ideone.com/nUYrq

Can we further improve this solution in terms of compactness or genericity (possibly both)? Can we get rid of the variable i which we need in the macro? Or any other improvement?

I would also like to know if that is valid code both in C++ and C (of course ignoring printing part)?

EDIT:

I realized that the order of calls to f() seems still unspecified. I'm not sure though, as I think comma in array initialization is not probably same as comma operator (in general). But if it is, can we avoid it and what part of the Standard says its unspecified?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

睫毛溺水了 2024-11-15 19:33:50

如果您想深入研究预处理器编程,我只能推荐 Boost。预处理器库作为构建块,您将避免从头开始重写。

例如,为了创建您的表格,我会使用 (ideone):

#include <iostream>

#include <boost/preprocessor/repetition/enum.hpp>

#define ORDER(z, n, text) n

int main() {
  int const a[] = { BOOST_PP_ENUM(100, ORDER, ~) };
  std::size_t const n = sizeof(a)/sizeof(int);

  std::cout << "count = " << n << "\n";

  for(std::size_t i = 0 ; i != n ; ++i ) 
    std::cout << a[i] << "\n";

  return 0;
}

并将所有麻烦留给 Boost :)

注意:这枚举从 0 到 99,而不是 1 到 100,还有其他可用于执行算术的操作;)

编辑: 这是如何工作的?

首先,我只能推荐 BOOST_PP_ENUM 的文档条目

BOOST_PP_ENUM 是一个宏,它有 3 个参数: (n, MACRO, data)

  • n:一个整数
  • MACRO:接受 3 个参数的宏:(z, i, data)
  • data:为方便起见,将传递给 (z, i, data) 的一些数据code>macro

然后它将被 n 次连续的 MACRO 调用所取代,并以逗号分隔:

MACRO(z, 0, data), MACRO(z, 1, data), ... , MACRO(z, n-1, data)

您可以对 MACRO 执行任何您想要的操作。

恐怕我从未使用过 z 参数,它是在内部使用的,理论上你可以使用它来加速该过程。

If you wish to delve into Preprocessor programming, I can only recommend the Boost.Preprocessor library as a building block, you'll avoid having to rewrite things from scratch.

For example, in order to create your table, I would have used (ideone):

#include <iostream>

#include <boost/preprocessor/repetition/enum.hpp>

#define ORDER(z, n, text) n

int main() {
  int const a[] = { BOOST_PP_ENUM(100, ORDER, ~) };
  std::size_t const n = sizeof(a)/sizeof(int);

  std::cout << "count = " << n << "\n";

  for(std::size_t i = 0 ; i != n ; ++i ) 
    std::cout << a[i] << "\n";

  return 0;
}

And leave all the cruft to Boost :)

Note: this enumerates from 0 to 99, not 1 to 100, there are other operations available to perform arithmetic ;)

EDIT: How does this work ?

First, I can only recommend the doc entry for BOOST_PP_ENUM

BOOST_PP_ENUM is a macro which takes 3 arguments: (n, MACRO, data)

  • n: an integer
  • MACRO: a macro accepting 3 arguments: (z, i, data)
  • data: some data, of your convenience, to be passed to macro

It will then be replaced by n successive invocations of MACRO separated by commas:

MACRO(z, 0, data), MACRO(z, 1, data), ... , MACRO(z, n-1, data)

It is up to you to do whatever you wish with your MACRO.

I am afraid I have never used the z argument, it is used internally, and you could in theory use it to speed up the process.

七堇年 2024-11-15 19:33:50

P99 有一个宏可以完全满足您的需求

#include "p99_map.h"

int Ara[] = { P99_POSS(100) };

它的优点是它完全是编译时间,无需使用函数进行动态初始化等根本不。

对于您来说,它可能有一个缺点,即它使用 C99 功能,特别是具有可变长度参数的宏。

P99 has a macro that does exactly what you want

#include "p99_map.h"

int Ara[] = { P99_POSS(100) };

It has the advantage that it is entirely compile time, no dynamic initialization with functions etc at all.

For you, it probably has the disadvantage that it uses C99 features, in particular macros with variable length arguments.

愛上了 2024-11-15 19:33:50

不,这不是有效的代码;该行为仍然未定义。由于数组初始化元素之间没有序列点,因此对 f() 的调用可以按任何顺序发生。

可以生成序列。 Boost.Preprocessor 就是这样做的,并使用这样的序列来发出更有趣的东西。

No, this is not valid code; the behaviour is still undefined. Since there are no sequence points between array-initialisation elements, the calls to f() may occur in any order.

It is possible to generate sequences. Boost.Preprocessor does so, and uses such sequences to emit much more interesting stuff.

后知后觉 2024-11-15 19:33:50

我认为 template 仍将提供卓越的解决方案,该解决方案将是明确的且不易出错。看下面的代码;许多事情仅在编译时计算,它应该生成高效的代码。

template<int VALUE, int INDEX, int SIZE, bool ALLOW>
struct Assign
{
  static void Element (int *p) 
  {
    Assign<VALUE + 1, INDEX + 1, SIZE, (INDEX < SIZE)>::Element(p);
    p[INDEX] = VALUE;
  }
};
template<int VALUE, int INDEX, int SIZE>
struct Assign<VALUE, INDEX, SIZE, false>
{
  static void Element (int *p) { p[INDEX] = VALUE; }
};

template<int START, int SIZE>
void Initialize (int (&a)[SIZE])
{
  Assign<START, 0, SIZE, true>::Element(a);
}

乍一看可能有点复杂,但可以理解。它仍然可以变得更通用。用法如下简单:

int a[100];
Initialize<1>(a);  // '1' is the starting value

这可以用于任何int a[N]。这是代码的输出

I think still the template would provide superior solution which will be definite and less error prone. See the following code; many of things are calculated at compile time only and it should generate efficient code.

template<int VALUE, int INDEX, int SIZE, bool ALLOW>
struct Assign
{
  static void Element (int *p) 
  {
    Assign<VALUE + 1, INDEX + 1, SIZE, (INDEX < SIZE)>::Element(p);
    p[INDEX] = VALUE;
  }
};
template<int VALUE, int INDEX, int SIZE>
struct Assign<VALUE, INDEX, SIZE, false>
{
  static void Element (int *p) { p[INDEX] = VALUE; }
};

template<int START, int SIZE>
void Initialize (int (&a)[SIZE])
{
  Assign<START, 0, SIZE, true>::Element(a);
}

It might be little complex at first glance, but understandable. It can be still made more general. Usage will be as simple as following:

int a[100];
Initialize<1>(a);  // '1' is the starting value

This can be used for any int a[N]. Here is the output of the code.

2024-11-15 19:33:50

一些简单的代码生成怎么样?

#include <fstream> 

int main() {

    std::ofstream fout("sequence_macros.hpp");

    for(int i=1; i<=100; ++i)
    {
        fout << "#define e" << i << "(a) ";
        fout << "(a+0)";
        for(int j=1; j<i; ++j)
        {
            fout << ",(a+" << j << ")";
        }
        fout << '\n';
    }
}

然后使用它:

#include <iostream>
#include "sequence_macros.hpp"

int main()
{
   // Create an array with 157 elements, in
   // sequence, starting at 25
   int x[] = {e100(25),e50(25+100),e7(25+100+50)};
   int sz = sizeof(x) / sizeof(*x);
   for(int i=0; i<sz; ++i)
      std::cout << x[i] << '\n';
}

生成代码: http://www.ideone.com/iQjrj

使用代码: http://ideone.com/SQikz

How about some simple code generation.

#include <fstream> 

int main() {

    std::ofstream fout("sequence_macros.hpp");

    for(int i=1; i<=100; ++i)
    {
        fout << "#define e" << i << "(a) ";
        fout << "(a+0)";
        for(int j=1; j<i; ++j)
        {
            fout << ",(a+" << j << ")";
        }
        fout << '\n';
    }
}

Then putting it to use:

#include <iostream>
#include "sequence_macros.hpp"

int main()
{
   // Create an array with 157 elements, in
   // sequence, starting at 25
   int x[] = {e100(25),e50(25+100),e7(25+100+50)};
   int sz = sizeof(x) / sizeof(*x);
   for(int i=0; i<sz; ++i)
      std::cout << x[i] << '\n';
}

Generating the code: http://www.ideone.com/iQjrj

Using the code: http://ideone.com/SQikz

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文