使用varargs参数动态调用C函数
我正在用C编写一个第三方库(在HP/Mercury Loadrunner中),该库允许它的一个函数使用varargs样式的变量大小参数列表。我想调用这个函数,但我不知道前面会有多少个参数 我的一位前任开发了一个函数,该函数在某种程度上起到了作用,但这里的问题是,该函数假设了最坏的情况(超过3000个参数),并为此编写了手动代码 为了说明这一点,下面是代码的开头。我们调用的函数是使用varargs参数动态调用C函数,c,C,我正在用C编写一个第三方库(在HP/Mercury Loadrunner中),该库允许它的一个函数使用varargs样式的变量大小参数列表。我想调用这个函数,但我不知道前面会有多少个参数 我的一位前任开发了一个函数,该函数在某种程度上起到了作用,但这里的问题是,该函数假设了最坏的情况(超过3000个参数),并为此编写了手动代码 为了说明这一点,下面是代码的开头。我们调用的函数是web\u submit\u data()。它将HTTP发布一组表单数据。此实现是在处理具有任意数量字段的动态生成表单时
web\u submit\u data()
。它将HTTP发布一组表单数据。此实现是在处理具有任意数量字段的动态生成表单时实现的。
(从原始索引中清理了一点,原始索引也是手工编码的。)
现在我找到了一个可以工作的外部库(),但我更希望a)完全依赖处理器,b)尝试教Loadrunner如何链接外部源 编辑: 原始函数使用硬编码索引,而不是使用变量。如果结果太不可预测,仍然可以恢复到这一点。然而,由于我不太可能使用不同的编译器或硬件/操作系统来运行它,我怀疑这是否值得 另外:我无法控制web_submit_data()的实现。因此,仅仅把问题降低一个层次并不能解决问题
另一件需要注意的事情是:
web\u submit\u data()
的规范使用一个名为LAST的常量来标记参数列表的末尾。最初的实现没有使用它。想必调用站点会这样做。在运行时,没有可移植的方法为C中的变量参数函数建立参数列表。有一些依赖于实现,您找到的dyncall库看起来不错,而且可能比大多数库更具可移植性。注意:代码已经依赖于编译器(尽管可能不依赖于处理器),因为调用web\u submit\u data
假设过程调用中的参数子表达式是按从左到右的顺序求值的,但C语言未指定参数求值的顺序
请参阅以供参考:
因此,非可移植的解决方案可能不会让情况变得更糟。您能否重新构造代码,使其不再必要?也许您可以使用传入缓冲区并使其更具确定性:
struct form_field
{
char[FIELD_NAME_MAX] name;
char[FIELD_VALUE_MAX] val;
};
web_submit_data_buffer_gazillion_items( const char *bufferName, const char *bufferValue)
{
/*
loop over bufferName somehow, either with a known size or terminating record,
and build an array of form_field records
*/
//loop
{
// build array of records
}
web_submit_data(record_array, array_len);
}
对不起,这实在是太充实了-我妻子叫我进来吃早餐。:-) 用预处理器写一次,不要回头看
#define WEB_SUBMIT_BUFFER(name, val) \
do { \
const int size = 129; \
int i = 0; \
int j = 11; \
web_submit_data(&(name)[i++ * size], \
&(name)[i++ * size], \
/* etc ad nauseum */ \
} while (0)
或者,如果每个特定调用的参数数量是固定的,则编写一个脚本来生成预处理器定义,以隐藏该调用有多可怕
#define WEB_SUBMIT_BUFFER_32(name, val) \
do { \
const int size = 129; \
int i = 0; \
int j = 11; \
web_submit_data(&(name)[i++ * size], \
&(name)[i++ * size], \
/* 32 times */ \
} while (0)
#define WEB_SUBMIT_BUFFER_33(name, val) ...
#define WEB_SUBMIT_BUFFER_34(name, val) /* etc */
请注意,您发布的代码示例具有未定义的行为-分隔函数参数的逗号不是序列点(这些逗号不是逗号运算符),因此在函数调用参数列表中多次修改
i
和/或j
会导致未定义的行为
这并不是说标准中没有规定函数调用参数的求值顺序-因此,即使您使用函数来求值参数(函数调用本身是序列点),对i
和j
进行了修改,您将以不确定的顺序传递指针
另外,我看不出
web\u submit\u data()
如何知道它传递了多少个参数-我看不到最后有一个计数或确定的哨兵参数。但我想你的例子可能就是这样——一个可能没有完整、准确细节的例子。另一方面,这是web\u submit\u data()
的问题,对吗 由于向采用变量参数的函数传递的参数通常比函数预期的多(请参见脚注#1),因此可以执行以下操作:
// you didn't give a clear specification of what you want/need, so this
// example may not be quite what you want as I've had to guess at
// some of the specifications. Hopefully the comments will make clear
// what I may have assumed.
//
// NOTE: while I have compiled this example, I have not tested it,
// so there is a distinct possiblity of bugs (particularly
// off-by-one errors). Check me on this stuff, please.
// I made these up so I could compile the example
#define ITEMDATA ((char const*) NULL)
#define ENDITEM ((char const*) 0xffffffff)
void web_submit_data_wrapper( const char*bufferName,
const char* bufferValue,
size_t headerCount, // number of header pointers to pass (8 in your example)
size_t itemStartIndex, // index where items start in the buffers (11 in your example)
size_t itemCount, // number of items to pass (unspecified in your example)
size_t dataSize ) // size of each header or item (129 in your example)
{
// kMaxVarArgs would be 3000 or a gazillion in your case
// size_t const kMaxVarArgs = 20; // I'd prefer to use this in C++
#define kMaxVarArgs (20)
typedef char const* char_ptr_t;
typedef char_ptr_t char_ptr_array_t[kMaxVarArgs];
char_ptr_array_t varargs = {0};
size_t idx = 0;
// build up the array of pararmeters we'll pass to the variable arg list
// first the headers
while (headerCount--) {
varargs[idx++] = &bufferName[idx * dataSize];
}
// mark the end of the header data
varargs[idx++] = ITEMDATA;
// now the "items"
while (itemCount--) {
varargs[idx++] = &bufferName[itemStartIndex * dataSize];
varargs[idx++] = &bufferValue[itemStartIndex * dataSize];
varargs[idx++] = ENDITEM;
++itemStartIndex;
}
// the thing after the last item
// (I'm not sure what this is from your example)
varargs[idx] = &bufferName[itemStartIndex * dataSize];
// now call the target function - the fact that we're passing more arguments
// than necessary should not matter due to the way VA_ARGS are handled
// but see the Footnote in the SO answer for a disclaimer
web_submit_data(
varargs[0],
varargs[1],
varargs[2],
//... ad nasuem until
varargs[kMaxVarArgs-1]
);
}
脚注#1:如果你想一想
stdargs.h
中的宏是如何工作的,这一点就很清楚了。然而,我并不认为这种技术符合标准。事实上,在最近的历史中,stackoverflow的答案我已经张贴在我的位置;事实上,我们已经发现本免责声明不符合标准(通常是由时刻警惕的人提出的)。因此,使用此技术的风险由您自己承担,并验证、验证、验证)。有两种方法可以传递数量可变的参数:传递给接受“…”的函数或传递给接受VAU列表的函数
您不能动态地定义“…”接口的参数数量,但您应该能够为va_list one定义参数数量。谷歌搜索va_start、va_end和va_list。在CamelBones中,我用来调用objc_msgSend(),这是一个varargs函数。有效。变长参数基本上只是指向传递给所需函数的一组压缩数据的指针。被调用函数负责解释此压缩数据 架构安全的方法是使用va_列表宏(n-alexander提到过),否则您可能会遇到各种数据类型在内存中填充的问题 设计varargs函数的正确方法是实际上有两个版本,一个版本接受“…”,后者依次提取va_列表并将其传递给接受va_列表的函数。这样,如果需要,您可以动态构造参数,并且可以调用函数的va_列表版本 大多数标准IO函数都有varargs版本:vprintf用于printf,vsprintf用于sprintf。。。你明白了。看看您的库是否实现了一个名为“vweb_submit_data”的函数或类似的函数。如果他们没有,给他们发电子邮件,告诉他们修复他们的库 3000行相同的东西(即使是预处理器诱导的)让我
// you didn't give a clear specification of what you want/need, so this
// example may not be quite what you want as I've had to guess at
// some of the specifications. Hopefully the comments will make clear
// what I may have assumed.
//
// NOTE: while I have compiled this example, I have not tested it,
// so there is a distinct possiblity of bugs (particularly
// off-by-one errors). Check me on this stuff, please.
// I made these up so I could compile the example
#define ITEMDATA ((char const*) NULL)
#define ENDITEM ((char const*) 0xffffffff)
void web_submit_data_wrapper( const char*bufferName,
const char* bufferValue,
size_t headerCount, // number of header pointers to pass (8 in your example)
size_t itemStartIndex, // index where items start in the buffers (11 in your example)
size_t itemCount, // number of items to pass (unspecified in your example)
size_t dataSize ) // size of each header or item (129 in your example)
{
// kMaxVarArgs would be 3000 or a gazillion in your case
// size_t const kMaxVarArgs = 20; // I'd prefer to use this in C++
#define kMaxVarArgs (20)
typedef char const* char_ptr_t;
typedef char_ptr_t char_ptr_array_t[kMaxVarArgs];
char_ptr_array_t varargs = {0};
size_t idx = 0;
// build up the array of pararmeters we'll pass to the variable arg list
// first the headers
while (headerCount--) {
varargs[idx++] = &bufferName[idx * dataSize];
}
// mark the end of the header data
varargs[idx++] = ITEMDATA;
// now the "items"
while (itemCount--) {
varargs[idx++] = &bufferName[itemStartIndex * dataSize];
varargs[idx++] = &bufferValue[itemStartIndex * dataSize];
varargs[idx++] = ENDITEM;
++itemStartIndex;
}
// the thing after the last item
// (I'm not sure what this is from your example)
varargs[idx] = &bufferName[itemStartIndex * dataSize];
// now call the target function - the fact that we're passing more arguments
// than necessary should not matter due to the way VA_ARGS are handled
// but see the Footnote in the SO answer for a disclaimer
web_submit_data(
varargs[0],
varargs[1],
varargs[2],
//... ad nasuem until
varargs[kMaxVarArgs-1]
);
}