使用Clang和g ++上的打包参数指针创建`va_list` [英] Creating a `va_list` Using a Pointer of Packed Arguments on Clang and g++
问题描述
我正在为研究架构开发周期精确的模拟器。我已经有一个交叉编译器,可以生成程序集(基于MIPS)。为了进行调试,我们有一个 printf
内在函数,当在模拟器中运行该函数时,它最终会调用一个内置方法,该方法可以访问包装在连续数组中的参数列表(例如
I am working on a cycle-accurate simulator for a research architecture. I already have a cross-compiler that generates assembly (based on MIPS). For debug purposes, we have a printf
intrinsic which ultimately, when run in the simulator, calls a builtin method that has access to a list of arguments packed in a contiguous array (such as would be created by this code):
template <typename type> inline static void insert(char buffer[], size_t* i, type value) {
memcpy(buffer+*i,&value, sizeof(type)); *i+=sizeof(type);
}
int main(int /*argc*/, char* /*argv*/[]) {
char buffer[512]; size_t i=0;
insert<double>(buffer,&i, 3.14);
insert<int>(buffer,&i, 12345);
insert<char const*>(buffer,&i, "Hello world!");
return 0;
}
在MSVC中,可以创建 va_list
并按以下方式调用 vprintf
:
In MSVC, one can then create a va_list
and call vprintf
like so:
union { va_list list; char* arguments; } un;
un.arguments = buffer;
vprintf(format_string, un.list);
目标体系结构是x86-64,它基于x86,因此产生的结果显然正确( MSVC提供的 va_list
只是 char *
)的typedef。
The target architecture is x86-64, which is based on x86, so this produces apparently correct results (the va_list
provided by MSVC is just a typedef for char*
).
但是,在g ++(大概是Clang;我还没有尝试过)上,代码出现了段错误。发生这种情况是因为基础类型(由编译器提供:在gcc 4.9.2中,它似乎是从 __ gnuc_va_list
进行类型定义的,而后者又是从 __ builtin_va_list
,大概是编译器的内在函数)是不同的(由于编译器错误,您只需进入 un.list = buffer;
禁止)。
However, on g++ (and presumably Clang; I haven't tried), the code segfaults. This happens because the underlying type (it's compiler-provided: in gcc 4.9.2, it appears to be typedefed from __gnuc_va_list
, which is in turn typedefed from __builtin_va_list
, presumably a compiler intrinsic) is different (as the compiler error you get it you just go un.list=buffer;
forbodes).
我的问题是:将这一系列打包参数转换为的最干净方法是什么? va_list
在x86-64模式下g ++和Clang都可以使用吗?
My question is: what is the cleanest way to convert this array of packed arguments into a va_list
that is usable by both g++ and Clang in x86-64 mode?
我目前的想法是最好分别解析每个格式说明符,然后将其与相应的参数一起转发给 printf
。这并不那么健壮(就支持 printf
的所有功能;在单一体系结构上工作仅足以满足我们的目的),也不是特别引人注目,
My current thinking is that it may be better to parse out each format specifier individually, then forward it off with the appropriate argument to printf
. This isn't as robust (in the sense of supporting all features of printf
; working on a single architecture only is robust enough for our purposes), nor is it particularly compelling, though.
推荐答案
对于基线答案,下面是一些简单的代码(经过合理测试,但没有保证),可以实现解析我提到的-format-string方法。我将其发布到了公共领域。
For a baseline answer, here is some simple code (reasonably well tested, but no guarantees) that implements the parse-the-format-string method I mentioned. I release it into the public domain.
如果有人写出的答案可以真正解决我所问的问题(这样做,但是要使用 va_list
If someone writes an answer that actually solves the problem I asked (doing this, but using va_list
; i.e., a much cleaner solution) then I will accept that answer instead.
static void printf_buffer(char const*__restrict format_string, char*__restrict argument_buffer) {
int num_chars = 0;
PARSE_CHAR:
switch (*format_string) {
case '\0': return;
case '%': {
int i = 1;
char c;
PARSE_SPECIFIER:
c = format_string[i++];
switch (c) {
case 'd': case 'i':
case 'u': case 'o': case 'x': case 'X':
case 'f': case 'F': case 'e': case 'E': case 'g': case 'G': case 'a': case 'A':
case 'c': case 's': case 'p':
goto PRINT_SPECIFIER;
case 'n':
assert(i==2,"\"%%n\" must contain no intermediary characters!");
**reinterpret_cast<int**>(argument_buffer) = num_chars;
argument_buffer += sizeof(int*);
goto DONE_SPECIFIER;
case '%':
assert(i==2,"\"%%%%\" must contain no intermediary characters!");
putchar('%'); ++num_chars;
goto DONE_SPECIFIER;
case '\0': assert(false,"Expected specifier before end of string!");
default: goto PARSE_SPECIFIER;
}
PRINT_SPECIFIER: {
char* temp = new char[i+1];
strncpy(temp,format_string,i); temp[i]='\0';
#define PRINTBRK(TYPE) num_chars+=printf(temp,*reinterpret_cast<TYPE*>(argument_buffer)); argument_buffer+=sizeof(TYPE); break;
switch (c) {
case 'd': case 'i': PRINTBRK(int)
case 'u': case 'o': case 'x': case 'X': PRINTBRK(unsigned int)
case 'f': case 'F': case 'e': case 'E': case 'g': case 'G': case 'a': case 'A': PRINTBRK(double)
case 'c': PRINTBRK(char)
case 's': PRINTBRK(char const*)
case 'p': PRINTBRK(void*)
default: assert(false,"Implementation error!");
}
#undef PRINTBRK
delete [] temp;
}
DONE_SPECIFIER:
format_string += i;
break;
}
default:
putchar(*format_string); ++format_string; ++num_chars;
break;
}
goto PARSE_CHAR;
}
此处是完整源代码的链接,包括封闭测试:链接。预期输出:
Here is a link to the full source, including an enclosing test: link. Expected output:
double: 3.1400, float: +3.1400, getting characters: ->, percent: %, int: 12345, string: "Hello world!"
Printed 54 characters before the marked point:
<-
这篇关于使用Clang和g ++上的打包参数指针创建`va_list`的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!