gpt4 book ai didi

c++ - 在 Clang 和 g++ 上使用打包参数指针创建 `va_list`

转载 作者:太空狗 更新时间:2023-10-29 23:02:06 24 4
gpt4 key购买 nike

我正在为研究架构开发一个周期精确的模拟器。我已经有一个生成程序集的交叉编译器(基于 MIPS)。出于调试目的,我们有一个 printf 内在函数,当在模拟器中运行时,它最终会调用一个内置方法,该方法可以访问打包在连续数组中的参数列表(例如由此创建的参数列表)代码):

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:

union { va_list list; char* arguments; } un;
un.arguments = buffer;
vprintf(format_string, un.list);

目标架构是x86-64,它是基于x86的,所以这产生了明显正确的结果(MSVC提供的va_list只是char*的typedef ).

但是,在 g++ 上(大概还有 Clang;我没试过),代码会出现段错误。发生这种情况是因为底层类型(它由编译器提供:在 gcc 4.9.2 中,它似乎是从 __gnuc_va_list 类型定义的,而后者又是从 __builtin_va_list 类型定义的,大概是一个编译器固有的)是不同的(因为你得到它的编译器错误你只是去 un.list=buffer; forbodes)。


我的问题是:将这个压缩参数数组转换为 va_list 在 x86-64 模式下 g++ 和 Clang 都可用的最简洁的方法是什么

我目前的想法是,最好单独解析每个格式说明符,然后将其与适当的参数一起转发给 printf。这不是那么健壮(在支持 printf 的所有功能的意义上;仅在单一架构上工作就足以满足我们的目的),但也不是特别引人注目。

最佳答案

对于基准答案,这里有一些简单的代码(经过合理测试,但没有保证)实现了我提到的解析格式字符串方法。我将它发布到公共(public)领域。

如果有人写的答案实际上解决了我提出的问题(这样做,但使用 va_list;即,一个更简洁的解决方案),那么我会接受那个答案。

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;
}

这是完整源代码的链接,包括一个封闭的测试:link .预期输出:

double: 3.1400, float: +3.1400, getting characters: ->, percent: %, int:      12345, string: "Hello world!"
Printed 54 characters before the marked point:
<-

关于c++ - 在 Clang 和 g++ 上使用打包参数指针创建 `va_list`,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29618457/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com