簡體   English   中英

使用Clang和g ++上的壓縮參數指針創建`va_list`

[英]Creating a `va_list` Using a Pointer of Packed Arguments on Clang and g++

我正在研究用於研究架構的周期精確模擬器。 我已經有一個生成程序集的交叉編譯器(基於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)。


我的問題是:將這個打包參數數組轉換為可在x86-64模式下由g ++和Clang使用va_list的最簡潔方法是什么?

我目前的想法是,分別解析每個格式說明符可能會更好,然后使用適當的參數將其轉發給printf 這不是那么強大(在支持printf所有功能的意義上;在單一架構上工作僅對我們的目的而言足夠強大),但它也不是特別引人注目。

對於基線答案,這里有一些簡單的代碼(經過合理測試,但沒有保證),它實現了我提到的parse-the-format-string方法。 我將其發布到公共領域。

如果有人寫了一個答案,實際上解決了我問的問題(這樣做,但使用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;
}

以下是完整源代碼的鏈接,包括封閉測試: 鏈接 預期產量:

double: 3.1400, float: +3.1400, getting characters: ->, percent: %, int:      12345, string: "Hello world!"
Printed 54 characters before the marked point:
                                                      <-
struct buffer {
  const char* ptr = 0;
  size_t count = 0;
};
template<class T>
T const* get_arg( buffer& b ) {
  T const* r = reinterpret_cast<T const*>(b.ptr);
  b.ptr += sizeof(T);
  b.count -= sizeof(T);
  return r;
}
template<class...Ts, size_t...Is>
void print( const char* format, std::index_sequence<Is...>, buffer& b ) {
  std::tuple<Ts const*...> tup;
  using discard=int[];
  (void)discard{0,(
    std::get<Is>(tup) = get_arg<Ts>(b)
  ,void(),0)...};
  printf( format, (*std::get<Is>(tup))... );
}
template<class...Ts>
void print( const char* format, buffer& b ) {
  print(format, std::index_sequence_for<Ts...>{}, b)
}

上面給出了一組類型<Ts...>和一個buffer ,將調用printf( format, ts... ) ,其中ts...是從buffer提取的數據。

下一步是一次提取一個%[flags][width][.precision][length]specifier格式命令。 獲取僅包含其中一個命令的子字符串,並將其提供給上面的命令。

計算那里有多少*條目,並根據該數字要求那么多的int

最后,length和說明符被映射到C ++類型。

所需的技術映射運行時的值來編譯時間索引(或C ++類型)中可以看出這里其他的斑點中。

這有一個缺點,即產生了超過150個函數。

作為附帶好處,您實際上可以檢查緩沖區是否有足夠的數據,如果用完而不是讀取壞內存,則拋出或退出。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM