我对vsprintf
的问题是我无法直接获取输入参数,我必须先逐个获取输入并将其保存在void**
中,然后将此void**
传递给{ {1}},它对于windows来说都很好,但是当我来到64位linux时,gcc无法编译,因为它不允许从vsprintf()
转换为void**
,有没有人可以给我一些帮助我应该如何在Linux下这样做?
我可以在GCC中动态创建va_list吗?
va_list
答案 0 :(得分:6)
有一种方法可以做到这一点,但Linux上的gcc
具体。它适用于32位和64位版本的Linux(已测试)。
免责声明:我不支持使用此代码。它不是便携式的,它是黑客的,而且坦率地说是一个岌岌可危的平衡大象。我只是在证明可以使用va_list
动态创建gcc
,这是原始问题所要求的。
话虽如此,下面的文章详细介绍了va_list
如何与amd64 ABI配合使用:Amd64 and Va_arg。
了解va_list
结构的内部结构后,我们可以将va_arg
宏从我们自己构建的va_list
中读取:
#if (defined( __linux__) && defined(__x86_64__))
// AMD64 byte-aligns elements to 8 bytes
#define VLIST_CHUNK_SIZE 8
#else
#define VLIST_CHUNK_SIZE 4
#define _va_list_ptr _va_list
#endif
typedef struct {
va_list _va_list;
#if (defined( __linux__) && defined(__x86_64__))
void* _va_list_ptr;
#endif
} my_va_list;
void my_va_start(my_va_list* args, void* arg_list)
{
#if (defined(__linux__) && defined(__x86_64__))
/* va_args will read from the overflow area if the gp_offset
is greater than or equal to 48 (6 gp registers * 8 bytes/register)
and the fp_offset is greater than or equal to 304 (gp_offset +
16 fp registers * 16 bytes/register) */
args->_va_list[0].gp_offset = 48;
args->_va_list[0].fp_offset = 304;
args->_va_list[0].reg_save_area = NULL;
args->_va_list[0].overflow_arg_area = arg_list;
#endif
args->_va_list_ptr = arg_list;
}
void my_va_end(my_va_list* args)
{
free(args->_va_list_ptr);
}
typedef struct {
ArgFormatType type; // OP defined this enum for format
union {
int i;
// OTHER TYPES HERE
void* p;
} data;
} va_data;
现在,我们可以使用va_list
方法或类似方法生成process()
指针(对于64位和32位构建都是相同的):
void* create_arg_pointer(va_data* arguments, unsigned int num_args) {
int i, arg_list_size = 0;
void* arg_list = NULL;
for (i=0; i < num_args; ++i)
{
unsigned int native_data_size, padded_size;
void *native_data, *vdata;
switch(arguments[i].type)
{
case ArgType_int:
native_data = &(arguments[i].data.i);
native_data_size = sizeof(arguments[i]->data.i);
break;
// OTHER TYPES HERE
case ArgType_string:
native_data = &(arguments[i].data.p);
native_data_size = sizeof(arguments[i]->data.p);
break;
default:
// error handling
continue;
}
// if needed, pad the size we will use for the argument in the va_list
for (padded_size = native_data_size; 0 != padded_size % VLIST_CHUNK_SIZE; padded_size++);
// reallocate more memory for the additional argument
arg_list = (char*)realloc(arg_list, arg_list_size + padded_size);
// save a pointer to the beginning of the free space for this argument
vdata = &(((char *)(arg_list))[arg_list_size]);
// increment the amount of allocated space (to provide the correct offset and size for next time)
arg_list_size += padded_size;
// set full padded length to 0 and copy the actual data into the location
memset(vdata, 0, padded_size);
memcpy(vdata, native_data, native_data_size);
}
return arg_list;
}
最后,我们可以使用它:
va_data data_args[2];
data_args[0].type = ArgType_int;
data_args[0].data.i = 42;
data_args[1].type = ArgType_string;
data_args[1].data.p = "hello world";
my_va_list args;
my_va_start(&args, create_arg_pointer(data_args, 2));
vprintf("format string %d %s", args._va_list);
my_va_end(&args);
你有它。它的工作大多数与普通的va_start
和va_end
宏相同,但允许您传递自己动态生成的字节对齐指针,而不是依赖于调用用于设置堆栈框架的约定。
答案 1 :(得分:3)
我已经尝试过使用其他地方提到的libffi而且它有效。 以下是链接,希望它可以帮助其他类似问题。 再次感谢我来到这里的所有帮助!
链接: http://www.atmark-techno.com/~yashi/libffi.html - 给出了一个简单的例子 http://www.swig.org/Doc1.3/Varargs.html - printf()和其他给出的例子
答案 2 :(得分:2)
va_list
的类型不是void **
或类似于64位gcc
的类型(在Intel x86 / 64计算机上)。在Mac OS X 10.7.4和RHEL 5上,stdarg.h
中没有标头/usr/include
。请考虑以下代码:
#include <stdarg.h>
#include <stdio.h>
int main(void)
{
printf("sizeof(va_list) = %zu\n", sizeof(va_list));
return 0;
}
使用64位编译的RHEL 5和Mac OS X 10.7上的输出为:
sizeof(va_list) = 24
使用32位编译时,每个平台上的输出为:
sizeof(va_list) = 4
(您可能会认为我很惊讶地发现32位和64位版本之间存在这么大的差异。我希望32位版本的值介于12到24之间。)
所以,类型是不透明的;你甚至找不到能够告诉你任何事情的标题;它比64位机器上的单个指针大得多。
即使您的代码在某些计算机上运行,但它仍然非常非常有效地保证无处不在。
GCC 4.7.1手册未提及允许您在运行时构建va_list
的任何函数。
答案 3 :(得分:0)
以下课程对我有用:
buffer_size
它可以像这样使用:
class VaList
{
va_list _arguments;
public:
explicit inline VaList(const void * pDummy, ...)
{
va_start(_arguments, pDummy);
}
inline operator va_list &()
{
return _arguments;
}
inline operator const va_list &() const
{
return _arguments;
}
inline ~VaList()
{
va_end(_arguments);
}
};
第一个虚拟参数可以是任何类型的指针,仅用于计算以下参数的地址。
当然也可以在C语言中完成同样的操作,但不能那么巧妙(使用指针而不是引用)!
使用VaList构造动态va_list的简单示例:
void v(const char * format, const va_list & arguments)
{
vprintf(format, const_cast<va_list &>(arguments));
}
...
v("%d\n", VaList("", 1)); // Uses VaList::operator va_list &()
v("%d %d\n", VaList(nullptr, 2, 3)); // Uses VaList::operator va_list &()
vprintf("%s %s %s\n", VaList("", "Works", "here", "too!"));
const VaList args(NULL, 4, 5, "howdy", "there");
v("%d %d %s %s\n", args); // Uses VaList::operator const va_list &() const
和用法:
static void VectorToVaList(const std::vector<int> & v, va_list & t)
{
switch (v.size())
{
case 1: va_copy(t, VaList("", v[0])); return;
case 2: va_copy(t, VaList("", v[0], v[1])); return;
case 3: va_copy(t, VaList("", v[0], v[1], v[2])); return;
case 4: va_copy(t, VaList("", v[0], v[1], v[2], v[3])); return;
// etc
}
throw std::out_of_range("Out of range vector size!");
}
答案 4 :(得分:-1)
如果您要解决的问题是插入将任意类型传递给va_list
样式的函数,请考虑使用union
:
#include <iostream>
#include <cstdarg>
union ARG
{
int d;
char* s;
double f;
};
int main()
{
printf("%d %s %f \n", 1, "two", 3.1415 );
// Output: 1 two 3.141500
char format[ 1024 ] = "%d %s %f\n";
ARG args[ 5 ] = { };
args[ 0 ].d = 1;
args[ 1 ].s = "two";
args[ 2 ].f = 3.1415;
printf( format, args[ 0 ], args[ 1 ], args[ 2 ], args[ 3 ], args[ 4 ] );
// Output: 1 two 3.141500
return 0;
}
关于我的解决方案,您会注意到一些事情:
format
和args
的例程并不是一件容易的事。对此进行了测试: -Ubuntu,g ++ -Android NDK
我做了更多测试,并确认@PeterCoordes关于此答案的评论不适用于双精度。