2010-07-28 6 views
13

Tôi có một chức năngTạo va_list động

void foo(int cnt, va_list ap); 

tôi cần phải sử dụng nó, nhưng yêu cầu là khá nghiêm ngặt, số lượng va_list khác nhau và nó sẽ thay đổi trong thời gian chạy. Những gì tôi muốn làm là:

tạo ra một hình thức va_list (mà hy vọng char*)

QList<Contact*> 

nơi Contact là một lớp được định nghĩa

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

}; 

và tôi muốn để cư trú trong vòng lặp va_list ví dụ:

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

} 

Có ai có một đầu mối về cách tôi có thể làm điều này?

+1

Bạn có thể thay đổi chức năng 'foo'? – adf88

+2

có thể trùng lặp của http://stackoverflow.com/questions/988290/populating-a-va-list – Hasturkun

+0

chắc chắn trùng lặp về câu trả lời đúng. – bmargulies

Trả lời

0

Nếu số lượng phần tử trong danh sách bị giới hạn, tôi sẽ chuyển cho công văn thủ công tùy thuộc vào số lượng phần tử.

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); 
} 
+1

Có lý do cho việc này không phải là cách hiệu quả nhất để làm điều này là: Tôi có thể có 1-999 phần tử Liên hệ và điều này sẽ trông khá xấu xí. Tôi đã hy vọng tìm được giải pháp hiệu quả hơn. Rgds Lukasz – user404251

+0

Tôi không chắc chắn là * hiệu quả hơn * để gọi hàm có thông số 999. –

+0

Bạn có chắc chắn rằng hàm foo trong thư viện có thể xử lý va_list với 999 tham số không? –

0

Tùy thuộc vào trình biên dịch loại va_list, macro va_start và va_end là gì. Bạn không thể làm điều này một cách tiêu chuẩn. Bạn sẽ phải sử dụng xây dựng trình biên dịch cụ thể.

Có thể bạn có thể thay đổi chức năng 'foo'? Nếu vậy, sau đó làm cho nó inversely - chuyển đổi va_list để QList và làm cho 'foo' chấp nhận QList.

// EDIT

Sau đó xem những gì các loại va_list là, những gì va_start và va_end macro là trong trình biên dịch cụ thể của bạn. Sau đó, xây dựng va_list của bạn theo cách mà các macro này sẽ hoạt động trên đó.

+0

Tôi thực sự có thể mong đợi điều đó. Tôi phát triển ứng dụng QT cho Windows IDE: Visual Studio 2005. – user404251

+0

Tôi không thể thực hiện việc này, nếu chức năng này được LIB tiếp xúc :). Tôi không có mã nguồn cho nó. – user404251

+0

Tôi không thể làm điều này, đây là chức năng được LIB tiếp xúc :). Tôi không có mã nguồn cho nó. – user404251

3

Câu hỏi của bạn được gắn thẻ C++ và có nhiều cách hay (như luồng) để tránh biến dạng hoàn toàn trong C++.

Đây là ví dụ tuyệt vời về lý do va_args có thể gây đau. Nếu bạn có bất kỳ cơ hội nào để thay đổi chữ ký của foo, đó là lựa chọn tốt nhất của bạn. Lấy std::vector<std::string> thay vì va_list sẽ giải quyết vấn đề của bạn ngay tại đó.

Nếu foo là trong một thư viện bên ngoài bạn không thể thay đổi, đề nghị tiếp theo của tôi sẽ là để tìm một thư viện khác nhau.

Nếu không có tùy chọn nào thì có vẻ như có cách để đệ quy xây dựng danh sách cuộc gọi bằng cách sử dụng va_list, nhưng tôi không thể tìm ra cách để thực hiện công việc đó.

5

Những gì bạn đang muốn làm là để mô phỏng các cuộc gọi stack để bạn có thể vượt qua một va_list xây dựng để foo(). Điều này khá cụ thể đối với trình biên dịch (và cảnh báo, có sự khác nhau giữa các trình biên dịch 32 và 64 bit). Mã sau đây là dành cho MỤC ĐÍCH GIẢI TRÍ CHỈ !!! là (nếu nó hoạt động trên hệ thống của bạn) nó dễ bị vỡ. Với nó, tôi sử dụng một bộ đệm bộ nhớ phẳng và cư nó với một số và một loạt các chuỗi ký tự. Bạn có thể điền vào nó như là thích hợp với con trỏ đến dây của bạn và đưa chúng xuống.

Dường như nó hoạt động trên hệ thống của tôi, Windows 7 w/Visual Studio 2008, chỉ dành cho các ứng dụng 32 bit.

* BAD IDEA CODE THEO D !!!I !!! *

#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); 

Bây giờ tôi sẽ cố hết sức xấu hổ khi nghĩ về một ý tưởng như vậy.

+0

Không chỉ ý tưởng tồi, thậm chí có thể xung đột với tiêu chuẩn C (C99: 7.15.1.1.2, 7.15.1.3.2 và có thể 7.15.1.4.2), có thể là hành vi không xác định. Không thực sự chắc chắn, mặc dù. – Aconcagua

-1

< chỉ để cho vui >

  • cho phép lập luận tùy ý đếm
  • may mắn sizeof (std :: wstring) là bội số của sizeof (int)
  • thử nghiệm trên 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); 
} 
 

</chỉ để cho vui >

3

... hmmm ... có lẽ không di động ... chắc chắn không đẹp ... nhưng có thể giải quyết vấn đề Yor ...

  • va_list là (ít nhất là cho Visual C++) chỉ là một #define cho char *
  • → tranh luận không cần phải được trên stack
  • → đối số được chỉ cần được liên tục trong bộ nhớ
  • → không cần phải sử dụng lắp ráp và/hoặc sao chép (xem tôi 'chỉ cho câu trả lời thú vị ':-)
  • → không cần phải lo lắng về việc dọn dẹp
  • hiệu quả!
  • thử nghiệm trên 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; 
} 
 
0

gì bạn đang cố gắng để sử dụng là alloca. Một đối tượng va_list không thể lưu trữ các biến, lệnh gọi hàm lưu trữ chúng và bạn chỉ có thể truy cập nó qua va_list. Các biến này chỉ hợp lệ trong suốt cuộc gọi và sau đó chúng nhận được ovverwriten.

NÀY SẼ KHÔNG LÀM VIỆC:

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

Để cấp phát bộ nhớ trên stack, mà không cần phải viết một chức năng variadic sử dụng alloca. Nó hoạt động nhiều hơn hoặc ít hơn như malloc, nhưng bạn không phải gọi free, nó tự động giải phóng chính nó khi bạn rời khỏi phạm vi.

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

Đó là về cơ bản giống như viết

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

Nhưng với alloca 3 không cần phải là một hằng số. Một lần nữa, bạn chỉ có thể sử dụng nó bên trong phạm vi bao quanh, do đó, hãy không trả về từ hàm này.

nếu những gì bạn muốn từ một va_list là nhiều loại trong một danh sách xem xét viết một liên minh như thế này:

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