簡體   English   中英

動態創建va_list

[英]Create va_list dynamically

我有一個功能

void foo(int cnt, va_list ap);

我需要使用它,但是要求非常嚴格, va_list數量會有所不同,並且在運行時會有所變化。 我想做的是:

創建一個va_list (需要char* )形式

QList<Contact*>

其中Contact是已定義的類

class Contact
{
   public:
      QString getName();
   private: 
      QString m_name;

}; 

我想在循環va_list填充例如:

for (int idx = 0; idx<contacts.count(); idx++)
{
    contacts.at(idx)->getName(); // this i would like to pass to va_list

}

有人知道我該怎么做嗎?

您想要做的是模擬調用堆棧,以便可以將構造的va_list傳遞給foo()。 這是編譯器特有的(警告,甚至32位和64位編譯器之間也存在差異)。 以下代碼僅用於娛樂目的!!! 因為(即使它在您的系統上也可以使用)它很容易損壞。 有了它,我使用了一個平面內存緩沖區,並用一個計數和一堆字符串填充它。 您可以適當地用指向您的字符串的指針填充它,並將它們傳遞給其他人。

它似乎確實可以在我的系統Windows 7 w / Visual Studio 2008上運行,僅適用於32位應用程序。

*錯誤的想法代碼如下! *

#define PSEUDOSTACKSIZE ( sizeof(int) + 999 * sizeof(const char*) )
#pragma pack( push,1 )
union PSEUDOSTACK
{
    int count;
    char data[PSEUDOSTACKSIZE];
};
#pragma pack( pop )

void foo( int count, va_list args )
{
    for ( int i = 0; i < count; i++ )
    {
        char *s = va_arg( args, char* );
        printf( "%s\n", s);
    }
}

void bar( PSEUDOSTACK data, ... ) 
{ 
    va_list args; 
    va_start(args, data.count); 
    foo( data.count, args);
    va_end(args); 
} 
// And later on, the actual test case code.
PSEUDOSTACK barData;
barData.count = 999;
char *p = barData.data + sizeof(int);
for ( int i = 0; i < 999; i++, p += sizeof(char*) )
{
    *reinterpret_cast<char**>(p) = "ThisIsABadIdea";
}
bar( barData );

我現在想着這個主意,羞愧地垂頭喪氣。

...嗯...可能不是便攜式的...肯定不是很好...但可以解決您的問題...

  • va_list是(至少對於Visual C ++而言)只是char的#define *
  • →參數不必在堆棧上
  • →僅要求參數在內存中是連續的
  • →無需使用匯編程序和/或復制(請參閱我的“有趣的回答” :-)
  • →無需擔心清理
  • 高效!
  • 在w2k3 sp2 32bit + vc ++ 2010上測試

#include <stdarg.h>
#include <string>
#include <vector>
#include <iostream>

#define N 6 // test argument count

void foo(int n, va_list args);

int main(int, char*[])
{
    std::vector<std::wstring> strings;
    std::wstring s(L"a");
    int i(0);

    // create unique strings...
    for (; i != N; ++i)
    {
        strings.push_back(s);
        ++s.front();
    }
    foo(N, reinterpret_cast<va_list>(strings.data()));
    return 0;
}

void foo(int n, va_list args)
{
    int i(0);

    for (; i != n; ++i)
        std::wcout << va_arg(args, std::wstring) << std::endl;
}

您的問題被標記為C ++,並且有很好的方法(例如流)完全避免在C ++中使用varargs。

這是為什么va_args會引起疼痛的一個很好的例子。 如果您完全有機會更改foo的簽名,那是您的最佳選擇。 使用std::vector<std::string>而不是va_list可以解決您的問題。

如果foo在外部庫中,則無法更改,我的下一個建議是找到其他庫。

如果這些都不是一個選項,則似乎應該有一種方法可以使用va_list遞歸建立呼叫列表,但是我不知道該如何進行工作。

如果列表中的元素數量有限,我將根據元素數量進行手動分派。

void call_foo(int count, ...) {
    va_list args;
    va_start(args, count);
    foo(count, args);
    va_end(args);
}

switch (contacts.count()) {
    case 0: return call_foo(contacts.count());
    case 1: return call_foo(contacts.count(),
                            contacts.at(0)->getName());
    case 2: return call_foo(contacts.count(),
                            contacts.at(0)->getName(),
                            contacts.at(1)->getName());
    case 3: return call_foo(contacts.count(),
                            contacts.at(0)->getName(),
                            contacts.at(1)->getName(),
                            contacts.at(2)->getName());
    default: /* ERROR HERE, ADD MORE CASES */ return call_foo(0);
}

您要使用的是alloca va_list對象無法存儲變量,函數調用將其存儲,並且您只能通過va_list訪問它。 這些變量僅在調用期間有效,並且之后會被ovverwriten獲取。

這將不起作用:

va_list func(int dummy, ...)
{
   va_list result;
   va_start(result, dummy);
   return result;
}

要在堆棧上分配內存,而不必編寫可變參數,請使用alloca 它的工作原理與malloc差不多,但是您不必調用free ,當您離開范圍時,它會自動釋放自己。

int * local = (int *) alloca(3 * sizeof(int));
local[0] = 10;
local[1] = 20;
local[2] = 30;

基本上和寫作是一樣的

int local[3];
local[0] = 10;
local[1] = 20;
local[2] = 30;

但是使用alloca 3並不需要是一個常數。 同樣的,你只能用它的封閉的范圍內,所以從函數返回。

如果您想要從va_list中獲得的是一個列表中的多種類型,請考慮編寫這樣的並集:

union variant
{
    int          i;
    unsigned int u;
    float        f;
    double       d;
    const char * s;
    void *       v;
};

取決於編譯器的是什么va_list類型,什么是va_start和va_end宏。 您無法以標准方式執行此操作。 您將必須使用特定於編譯器的構造。

也許您可以更改“ foo”功能? 如果是這樣,則進行相反的處理-將va_list轉換為QList並使'foo'接受QList。

//編輯

然后查看va_list類型是什么,特定編譯器中的va_start和va_end宏是什么。 然后以這樣的方式構建您的va_list,以使這些宏可以在其上運行。

<只是為了好玩>

  • 允許任意參數計數
  • 幸運的是sizeof(std :: wstring)是sizeof(int)的倍數
  • 在w2k3 sp2 32bit + visual c ++ 2010上測試

#include <stdarg.h>
#include <string>
#include <vector>
#include <iostream>

#define N 6 // test argument count

void foo(int n, ...);

int main(int, char*[])
{
    std::vector strings;
    std::wstring s(L"a");
    int i(0);

    // create unique strings...
    for (; i != N; ++i)
    {
        strings.push_back(s);
        ++s.front();
    }

    int n_stack_strings(N*sizeof(std::wstring)),    // space needed for strings
        n_stack(sizeof(int)+n_stack_strings);   // overall stack space...needed for cleanup

    __asm sub esp, n_stack_strings  ; reserve stack space

    std::wstring* p_stack(0);

    __asm mov p_stack, esp  ; get stack pointer

    std::wstring* p(p_stack);
    std::vector<std::wstring>::iterator string(strings.begin());

    // copy to stack
    for (; string != strings.end(); ++string, ++p)
        new (p) std::wstring(*string);
    __asm push N    ; argument count...arguments right to left (__cdecl)
    __asm call foo
    // cleanup
    for (p = p_stack; p != p_stack+N; ++p)
        p->~basic_string();
    __asm add esp, n_stack  ; caller has to cleanup the stack (__cdecl)
    return 0;
}

void foo(int n, ...)
{
    int i(0);
    va_list marker;

    va_start(marker, n);
    for (; i != n; ++i)
        std::wcout << va_arg(marker, std::wstring) << std::endl;
    va_end(marker);
}

</ just for fun>

暫無
暫無

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

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