如何使宏的第一次调用与所有后续调用不同?

发布于 2024-08-25 13:36:01 字数 346 浏览 5 评论 0原文

这可能非常简单,但我无法找到一个好的答案。 如何制作一个宏,首先代表某个值,然后代表另一个值?

我知道这很糟糕,但我需要它第一次隐式声明一个变量,然后什么都不做。 我正在实现的其他宏需要此变量。

我应该利用“参数预扫描”吗?

您需要知道的是我正在生成代码:

#define INC_X x++ //should be declared if needed to
#define PRINT_X printf("VALUE OF X: %d\n", x)

int func() {
[...]
INC_X;
[...]
INC_X;
[...]
PRINT_X;
[...]
}

That may be really simple but I'm unable to find a good answer.
How can I make a macro representing first a certain value and then a different one?

I know that's nasty but I need it to implicitly declare a variable the first time and then do nothing.
This variable is required by other macros that I'm implementing.

Should I leverage "argument prescan"?

The thing you need to know is the fact I'm generating the code:

#define INC_X x++ //should be declared if needed to
#define PRINT_X printf("VALUE OF X: %d\n", x)

int func() {
[...]
INC_X;
[...]
INC_X;
[...]
PRINT_X;
[...]
}

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

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

发布评论

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

评论(7

伴我心暖 2024-09-01 13:36:01

据我所知,这是不可能的。我知道宏的扩展无法控制另一个宏(或其本身)随后扩展的方式。 C99 引入了 _Pragma,以便可以在宏中完成#pragma 操作,但没有 #define#undef 的等效项。

As far as I know, this is impossible. I know of no way for the expansion of a macro to control the way another macro -- or itself -- will be expanded after. C99 introduced _Pragma so that #pragma things can be done in macros, but there is no equivalent for #define or #undef.

梦里梦着梦中梦 2024-09-01 13:36:01
#include <stdio.h>
#define FOO &s[ (!c) ? (c++, 0) : (4) ]
static int c = 0;
const char s[] = { 'f', 'o', 'o', '\0', 'b', 'a', 'r', '\0' };

int main() {
 puts(FOO);
 puts(FOO);
 return 0;
}

以上有帮助吗?

#include <stdio.h>
#define FOO &s[ (!c) ? (c++, 0) : (4) ]
static int c = 0;
const char s[] = { 'f', 'o', 'o', '\0', 'b', 'a', 'r', '\0' };

int main() {
 puts(FOO);
 puts(FOO);
 return 0;
}

Does the above help?

桜花祭 2024-09-01 13:36:01

从它的外观来看,您可以尝试 Boost.Preprocessor 是否包含您正在寻找的内容。
查看本教程

http://www.boostpro.com/tmpbook/preprocessor.html

来自优秀的 C++ 模板元编程书籍。

From the look of it, you could try if Boost.Preprocessor contains what you are looking for.
Look at this tutorial

http://www.boostpro.com/tmpbook/preprocessor.html

from the excellent C++ Template Metaprogramming book.

静水深流 2024-09-01 13:36:01

通过编辑,我将尝试给出答案。它要求您的编译器支持 __FUNCTION__,MSVC 和 GCC 都支持这一点。

首先,编写一组函数,将字符串映射到内存中的整数,所有这些函数都存储在结构的某个全局实例中。这留给读者作为练习,从功能上来说它是一个哈希图,但我将调用结果实例“global_x_map”。函数get_int_ptr定义为返回一个指向指定字符串对应的int的指针,如果它不存在则创建它并将其初始化为0。< code>reset_int_ptr 现在只是将 0 分配给计数器,稍后您会看到为什么我不直接编写 *_inc_x_tmp = 0;

#define INC_X do {\
    int *_inc_x_tmp = get_int_ptr(&global_x_map, __FILE__ "{}" __FUNCTION__); \
    /* maybe some error-checking here, but not sure what you'd do about it */ \
    ++*_inc_x_tmp; \
} while(0)

#define PRINT_X do {\
    int *_inc_x_tmp = get_int_ptr(&global_x_map, __FILE__ "{}" __FUNCTION__); \
    printf("%d\n", *_inc_x_tmp); \
    reset_int_ptr(&global_x_map, _inc_x_tmp); \
} while(0)

我选择分隔符“{}”是因为它不会出现在损坏的 C 函数名称中 - 如果您的编译器出于某种原因可能将其放入损坏的函数名称中,那么您当然必须更改它。使用无法出现在您的平台上的文件名中的内容也可以。

请注意,使用宏的函数是不可重入的,因此它与定义自动变量不太一样。不过,我认为可以使其可重入。将 __LINE__ 作为额外参数传递给 get_int_ptr。创建条目后,存储 __LINE__ 的值。

现在,映射不仅应该为每个函数存储一个int,而且还应该存储一个整数堆栈。当使用第一个看到的行值调用它时,它应该将一个新的 int 压入堆栈,并在之后每当使用任何其他行值调用该函数时返回指向该 int 的指针。当调用reset_int_ptr时,它应该弹出堆栈,而不是将计数器设置为0,以便将来的调用将返回之前的int。

当然,只有当对 INC_X 的“第一次”调用始终相同,每次执行函数时仅调用一次,并且该调用不会与另一个调用出现在同一行时,这才有效。如果它在循环、if() 块等中,就会出错。但如果它在一个块内,那么声明自动变量也会出错。它也仅在始终调用 PRINT_X 时才有效(检查您的早期错误退出),否则您不会恢复堆栈。

这听起来像是一个疯狂的工程量,但本质上这就是 Perl 实现动态作用域变量的方式:它为每个符号名称都有一个堆栈。不同之处在于,与带有 RAII 的 C++ 一样,Perl 在作用域退出时自动弹出该堆栈。

如果您需要它既是线程安全的又是可重入的,那么请将 global_x_map 设为线程本地而不是全局。

编辑:如果您在头文件中定义了静态函数,则 __FILE__ "{}" __FUNCTION__ 标识符仍然不是唯一的 - 不同 TU 中的不同版本将在不可重入中使用相同的计数器版本。不过,我认为在可重入版本中这是可以的。如果 __FILE__ 是基本名称而不是完整路径,您也会遇到问题,因为同名文件中定义的同名静态函数可能会发生冲突。这甚至破坏了可重入版本。最后,这些都没有经过测试。

With the edit, I'll have a go at an answer. It requires your compiler to support __FUNCTION__, which MSVC and GCC both do.

First, write a set of functions which maps strings to integers in memory, all stored in some global instance of a structure. This is left as an exercise for the reader, functionally it's a hashmap, but I'll call the resulting instance "global_x_map". The function get_int_ptr is defined to return a pointer to the int corresponding to the specified string, and if it doesn't already exist to create it and initialize it to 0. reset_int_ptr just assigns 0 to the counter for now, you'll see later why I didn't just write *_inc_x_tmp = 0;.

#define INC_X do {\
    int *_inc_x_tmp = get_int_ptr(&global_x_map, __FILE__ "{}" __FUNCTION__); \
    /* maybe some error-checking here, but not sure what you'd do about it */ \
    ++*_inc_x_tmp; \
} while(0)

#define PRINT_X do {\
    int *_inc_x_tmp = get_int_ptr(&global_x_map, __FILE__ "{}" __FUNCTION__); \
    printf("%d\n", *_inc_x_tmp); \
    reset_int_ptr(&global_x_map, _inc_x_tmp); \
} while(0)

I've chose the separator "{}" on the basis that it won't occur in a mangled C function name - if your compiler for some reason might put that in a mangled function name then of course you'd have to change it. Using something which can't appear in a file name on your platform would also work.

Note that functions which use the macro are not re-entrant, so it is not quite the same as defining an automatic variable. I think it's possible to make it re-entrant, though. Pass __LINE__ as an extra parameter to get_int_ptr. When the entry is created, store the value of __LINE__.

Now, the map should store not just an int for each function, but a stack of ints. When it's called with that first-seen line value, it should push a new int onto the stack, and return a pointer to that int thereafter whenever it's called for that function with any other line value. When reset_int_ptr is called, instead of setting the counter to 0, it should pop the stack, so that future calls will return the previous int.

This only works of course if the "first" call to INC_X is always the same, is called only once per execution of the function, and that call doesn't appear on the same line as another call. If it's in a loop, if() block, etc, it goes wrong. But if it's inside a block, then declaring an automatic variable would go wrong too. It also only works if PRINT_X is always called (check your early error exits), otherwise you don't restore the stack.

This may all sound like a crazy amount of engineering, but essentially it is how Perl implements dynamically scoped variables: it has a stack for each symbol name. The difference is that like C++ with RAII, Perl automatically pops that stack on scope exit.

If you need it to be thread-safe as well as re-entrant, then make global_x_map thread-local instead of global.

Edit: That __FILE__ "{}" __FUNCTION__ identifier still isn't unique if you have static functions defined in header files - the different versions in different TUs will use the same counter in the non-re-entrant version. It's OK in the re-entrant version, though, I think. You'll also have problems if __FILE__ is a basename, not a full path, since you could get collisions for static functions of the same name defined in files of the same name. That scuppers even the re-entrant version. Finally, none of this is tested.

城歌 2024-09-01 13:36:01

让宏在执行结束时 #define 一些标志并首先检查该标志怎么样?

#def printFoo
   #ifdef backagain
       bar
   #else
       foo
       #def backagain

需要添加一些 \ 字符才能使其工作 - 与内联 func() 相比,您可能不想实际执行此操作

What about having the macro #define some flag at the end of it's execution and check for that flag first?

#def printFoo
   #ifdef backagain
       bar
   #else
       foo
       #def backagain

Need to add some \ chars to make it work - and you probably don't want to actually do this compared to an inline func()

下壹個目標 2024-09-01 13:36:01

迄今为止提出的一些方法的替代方法是使用函数指针。它可能不是您想要的,但它们仍然是一个强大的工具。

void foo (void);
void bar (void);

void (*_func_foo)(void) = foo;

void foo (void) {
    puts ("foo\n");
}

void bar (void) {
    puts ("bar"\n");
}

#define FOO()  _func_foo(); \
               _func_foo = bar;

int main (void) {
    FOO();
    FOO();
    FOO();
    return 0;
}

An alternative to some of the methods proposed thus far would be to use function pointers. It might not be quite what you are looking for, but they can still be a powerful tool.

void foo (void);
void bar (void);

void (*_func_foo)(void) = foo;

void foo (void) {
    puts ("foo\n");
}

void bar (void) {
    puts ("bar"\n");
}

#define FOO()  _func_foo(); \
               _func_foo = bar;

int main (void) {
    FOO();
    FOO();
    FOO();
    return 0;
}
薆情海 2024-09-01 13:36:01
#define FOO __COUNTER__ ? bar : foo

编辑:删除所有不需要的代码

#define FOO __COUNTER__ ? bar : foo

Edit: removed all unneeded code

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