我目前正在寻找计算传递给函数的参数的总大小,以字节为单位.从理论上讲,人们可以sizeof(x)为每一个论点写出来.但是,如果想要为很多功能执行此操作,这将浪费大量时间.我试图找出参数的空间量,这样我就可以分配适量的内存来存储它们并存储它们(适用于各种函数,混合类型).
我正在寻找一个表达式,可以确定非变量函数的所有参数的大小,无论它们的名称如何,无论有多少(在合理范围内,我现在只支持大约64个参数).它可以是一个函数,一个预处理器宏,我对实现是不可知的.我也有兴趣处理可变参数函数,但我很确定这是不可能的,因为当你进入可变参数函数时,你已经丢失了有关数据类型的所有信息.
目前,我发现有三种方法可能会让我这样做.第一个是基于Laurent Deniau的arg计数的概念.从理论上讲,我可以使用一个宏来生成函数头,并做一些类似花哨的步法来获取args的数量并调度到处理每个具有N个参数的个别情况的宏.(见:丑陋).基本上,我只是使用宏对所有函数名称进行别名,然后在每个函数名称上使用sizeof.问题是,我需要为我想要表示的每个参数长度创建一个宏.而且我真的不喜欢做64(或更多)的事情去做一份工作.
第二种方法是尝试遵循Ben Klemer的"更好的可变参数"的方法.我不会使用他的所有方法,但我会尝试生成一个结构,表示函数的arg签名到结构中.然后,我可以尝试获得结构元素的大小(甚至结构本身,如果我所关心的是对空间的保守估计).这有一些问题.首先,它可能只适用于C99兼容的东西(仍在检查).其次,它为每个实现的功能创建了一个额外的结构.这不完全是一个问题,但它仍然存在这样的问题:他的构造结构的方法最终与函数的名称相同(所以你仍然需要引用名称来使用它们).我可能会解决这个问题.
可能的第三种方法是递归宏,但我不确定编译器有多开心.从理论上讲,通过调用表单中的宏来递归地从VA_ARGS中弹出元素是可能的.很明显,当VA_ARG为空时需要暂停规则(以及确保你没有被浮动+符号捕获的东西),但是你明白了.POPPER(arg, ...) POPPER(VA_ARGS) + sizeof(arg)
这些东西中的任何一个都可以让我这样做:
在Visual Studio 2005上,我有一个看起来像这样的宏(例如!!):
#define MY_CALL(FUN, ...) \
if(prepare(x, y)) { \
FUN(__VA_ARGS__); \
}
/**/
Run Code Online (Sandbox Code Playgroud)
只要函数至少有一个参数,我就没事了.
当函数接受零参数时,预处理器"帮助"删除"尾随逗号",扩展如下:
if(prepare(x y)) { funct(); }
Run Code Online (Sandbox Code Playgroud)
太棒了,不是吗?
我该如何修复这个宏,以便__VA_ARGS__在Visual C++(VS 2005)上使用零?
我知道在C99(以及通过GNU扩展)中添加了可变参数宏.我一直想知道在ANSI C中是否有一个不错的选择.
我想出了类似的东西,但它仍然有点尴尬:
void log_info(const char *file, int line, const char *fmt, ...)
{
#ifdef DEBUG
va_list ap;
char newfmt[1024] = { 0 };
va_start(ap, fmt);
sprintf(newfmt, "[INFO] (%s:%d): %s\n", file, line, fmt);
vfprintf(stderr, newfmt, ap);
va_end(ap);
#endif
}
Run Code Online (Sandbox Code Playgroud)
所以这可以像这样调用:
log_info(__FILE__, __LINE__, "info message: %s %d", "helloworld", 12);
Run Code Online (Sandbox Code Playgroud)
这种方法没有任何问题,但是我想知道是否有更好的方法呢?例如.无需每次都指定文件/行.
我很感激任何反馈.:)
编辑:通过ANSI C这里我的意思是C89.
编辑:下面的答案很好,但我相信它需要运行打印命令twise它可能会带来一些线程安全问题.另一种选择可能是使用define来最小化输入(也非常难看):
#define __FL__ __FILE__, __LINE__
Run Code Online (Sandbox Code Playgroud)
然后运行命令,如:
log_info(__FL__, "info message: %s %d", "helloworld", 12);
Run Code Online (Sandbox Code Playgroud) 我有一个内联的可变参数函数,
inline int foo(...)
我需要foo()调用一个宏(让我们称之为MACRO),这也是可变参数.
基本上我需要foo()将其所有输入参数传递给MACRO.foo()由于__VA_ARGS__选项,重新定义另一个宏将是一个简单的解决方案,但我还需要foo()返回一个值.
注意:我正在尝试连接已编写代码的两部分,我不允许更改它们.foo(...)用于代码的第一部分,并MACRO在第二部分中定义.我应该做的唯一事情是定义一个foo()使用MACRO而我不能,因为它们都是可变的.
我正在使用一些日志记录宏,它们应该打印出__PRETTY_FUNCTION__宏提供的信息,如果需要,最多可以输出两个参数的名称和值.我的代码的简化版本看起来像
template<typename Value1, typename Value2>
void Log(std::string const& function,
std::string const& variable_1 = "", Value1 value_1 = Value1(0),
std::string const& variable_2 = "", Value2 value_2 = Value2(0)) {
std::cout << function << " "
<< variable_1 << " " << value_1 << " "
<< variable_2 << " " << value_2 << std::endl;
}
#define LOG0() Log(__PRETTY_FUNCTION__)
#define VARIABLE(value) #value, value
#define LOG1(value) Log(__PRETTY_FUNCTION__, VARIABLE(value))
#define LOG2(value, value1) Log(__PRETTY_FUNCTION__, VARIABLE(value), VARIABLE(value1))
#define LOG(arg0, arg1, arg2, arg, ...) arg …Run Code Online (Sandbox Code Playgroud) 我发现了一个问题,显示如何根据参数的数量 重载宏:在参数数量上重载宏
但正如他们所说,它不能使用MSVC,因为MSVC扩展__VA_ARGS__为单个标记而不是参数列表(arg1, arg2, arg3).
他们指出另一个问题,其中给出了一个解决方法:MSVC没有正确扩展__VA_ARGS__ 但是根本没有解释,所以我不能适应我自己的情况,因为我无法理解它.
你能解释一下这个解决方法的工作原理吗?
看完VA_NARG后
我尝试使用宏根据C中的参数数量实现函数重载.现在的问题是:
void hello1(char *s) { ... }
void hello2(char *s, char *t) { ... }
// PP_NARG(...) macro returns number of arguments :ref to link above
// does not work
#define hello(...) hello ## PP_NARG(__VA_ARGS__)
int main(void)
{
hello("hi"); // call hello1("hi");
hello("foo","bar"); // call hello2("foo","bar");
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我从C-faq那里读到了这个.但仍然无法让它工作......
如果我这样做,海湾合作委员会会抱怨:
#define M(obj,met, ..., contents) obj##_##met(const void * self, __VA_ARGS__) { \
contents \
}
Run Code Online (Sandbox Code Playgroud)
给我这两个理由:
error: missing ')' in macro parameter list
warning: __VA_ARGS__ can only appear in the expansion of a C99 variadic macro
Run Code Online (Sandbox Code Playgroud)
显然,C99样式的可变参数宏在省略号之后立即期望右括号,有效地要求可变参数列表是宏的最后一个参数.我需要它在中间来产生我在上面的宏中描述的速记符号.GCC是否支持此功能,使用另一种(非C99)可变参数宏样式?我可以模仿它在其他地方做吗?我不希望最后的变量列表,它会让我的符号混乱.我只能使用GCC.
考虑以下代码(实例):
#define TEST_VA(mX, ...) TEST
#define STRINGIFY_IMPL(mX) #mX
#define STRINGIFY(mX) STRINGIFY_IMPL(mX)
#include <iostream>
int main()
{
std::cout << STRINGIFY(TEST_VA(1)) << std::endl;
std::cout << STRINGIFY(TEST_VA()) << std::endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
clang++ 3.4抱怨:
main.cpp:9:37: warning: must specify at least one argument for '...' parameter of variadic macro [-Wgnu-zero-variadic-macro-arguments]
std::cout << STRINGIFY(TEST_VA(1)) << std::endl;
^
main.cpp:1:9: note: macro 'TEST_VA' defined here
#define TEST_VA(mX, ...) TEST
^
main.cpp:10:33: warning: must specify at least one argument for '...' parameter of variadic macro …Run Code Online (Sandbox Code Playgroud) va_arg\ va_start\ va_list\va_end宏在 x64 中如何工作?
i386 中的调用约定在堆栈上传递参数,因此宏只是增加一些指向堆栈基址的指针并转发它。然而,在 x64 中,所有参数都是通过寄存器传递的......那么那里会发生什么呢?被调用函数如何知道哪些寄存器用于传递参数以确保它不会破坏它们?
variadic-macros ×10
c ×6
macros ×4
c++ ×3
c++11 ×2
c99 ×2
gcc ×2
visual-c++ ×2
c++14 ×1
c89 ×1
clang ×1
preprocessor ×1
sizeof ×1
warnings ×1
x86-64 ×1