2010-12-26 33 views
7

Tôi đang cố gắng chuyển đổi một số mã để nó biên dịch trên gcc quá (ngay bây giờ, nó chỉ biên dịch trên MSVC).Làm thế nào để tạo một va_list trên GCC?

Mã tôi bị kẹt ở trong chức năng định dạng giả chấp nhận như nhập chuỗi định dạng và không hoặc nhiều đối số (const char *format, ...). Sau đó, nó sẽ xử lý một số số của trình giữ chỗ tiêu thụ một số đối số của đối số và chuyển phần còn lại thành vsprintf cùng với một va_list mới được tạo động.

Đây là mã thực tế để tạo mới va_list:

char *new_args = (char *) malloc(sum); 
char *n = new_args; 

for(int i = 0; i < nArgs; i++) 
{ 
    int j = order[i]; 
    int len = _getlen(types[j]); 

    memcpy(n, args + cumulOffsets[j], len); 
    n += len; 
} 

vsprintf(buffer, sFormat.c_str(), new_args); 

Trong phòng của tôi, tôi đã không và sẽ không bao giờ viết mã này. Trong thực tế, tôi nghĩ rằng đó là một trong những điều khó khăn nhất mà tôi từng thấy trong suốt cuộc đời của mình.

Tuy nhiên, chức năng này rất phức tạp, rất cũ và rất quan trọng. Nó cũng đã không được sửa đổi trong nhiều năm (tốt, ngoại trừ bây giờ) vì vậy trong khi tôi muốn viết lại nó từ đầu tôi không thể biện minh cho thời gian nó sẽ mất cộng với các lỗi nó sẽ giới thiệu.

Vì vậy, tôi cần một cách để làm điều này cùng trên GCC .. Nhưng có một va_list không phải là một char * vì vậy tôi nhận được:

 
error: ISO C++ forbids casting to an array type '__va_list_tag [1]' 
+0

xem xét sử dụng libffi (http://sourceware.org/libffi/) – Christoph

+1

Tôi cho rằng vấn đề này không hòa tan được. Nó không thể tự động xây dựng một va_list trong C/C++ di động. Thêm vào đó vấn đề với __builtin_va_list không được định nghĩa trong bất kỳ tập tin tiêu đề nào, và điều đó trở nên gấp đôi không thể. – TonyK

+0

Với mã hoàn chỉnh hơn cho hàm, nó có thể giúp bạn. Tuy nhiên, như TonyK đã nói, không có một cách di động để tạo ra một va_list. –

Trả lời

4

tôi là một chút mất. Tại sao bạn cần số mới được tạo động va_list? Tại sao không chỉ tái sử dụng cái cũ?

Tôi tin rằng vsnprintf() sử dụng đối tượng hiện tại va_list (nếu bạn có thể gọi nó). Vì vậy, bạn có thể tự do va_start(), sử dụng các đối số bạn muốn qua va_arg(), sau đó vượt qua các đối số còn lại thông qua va_list-vsnprintf(), và sau đó gọi va_end().

Tôi có thiếu gì đó không? Tại sao phải sao chép sâu?

Và nếu bạn cần một bản sao sâu, tại sao không va_start() tươi, loại bỏ các đối số bạn muốn qua va_arg(), và sau đó vượt qua kết quả va_list đối tượng để vsnprintf().

(Mỗi cuộc gọi đến va_arg đổi các va_list đối tượng để các cuộc gọi tiếp theo sẽ trả về số tiếp theo.)

Ngoài ra, bạn chỉ có thể sử dụng va_copy(). (Mặc dù chắc chắn tuân theo nó với một số tương ứng va_end().)

Phụ lục: Cũng lưu ý rằng các macro này dựa trên tiêu chuẩn C89 & C99. GNU g ++ sẽ hỗ trợ chúng. Microsoft có phần hạn chế hơn.


Theo dõi trên bình luận TonyK của:

Những gì tôi nói ở trên hoạt động nếu bạn đang kéo các mục N đầu tiên ra khỏi va_list. Nếu bạn đang kéo đồ vật ra khỏi chính giữa, điều đó khó hơn.

Không có cách nào di động để xây dựng một va_list.

Tuy nhiên, bạn có thể tách chuỗi định dạng, sử dụng nó để xác định loại đối tượng (đôi, float, int, v.v.) và in từng loại riêng lẻ với chuỗi định dạng riêng của nó (phần con của chuỗi định dạng gốc)). Nhiều cuộc gọi snprintf() sẽ gây ra một số phí. Nhưng nếu thói quen này không được gọi quá thường xuyên, nó sẽ là khả thi.

Bạn cũng có thể in ra các phần con của chuỗi định dạng gốc với va_list phù hợp. Nói cách khác, vsnprintf() các phần tử in thứ nhất 1..3, các phần tử thứ hai 5..7, thứ ba 10..13, v.v. (Như vsnprintf() sẽ bỏ qua các phần tử phụ trên va_list xa hơn những gì nó cần. Bạn chỉ cần một loạt các tương ứng định dạng chuỗi mảnh vỡ, và popping các mặt hàng ra khỏi va_list với va_arg() như cần thiết cho mỗi vsnprintf() gọi.)

+0

Tôi nghĩ rằng OP muốn xóa một số đối số từ một va_list hiện có trước khi chuyển nó đến vsprintf. Điều này có vẻ khó khăn với tôi. gcc sử dụng kiểu built-in __builtin_va_list cho điều này, được tự động định nghĩa trước bởi trình biên dịch - nó không được định nghĩa trong bất kỳ tệp tiêu đề nào. Vì vậy, bạn sẽ cần phải đi đến mã nguồn của trình biên dịch để xem nó trông như thế nào, hoặc nhìn vào một số đầu ra ngôn ngữ lắp ráp. Cả hai lựa chọn thay thế này đều rất hấp dẫn! – TonyK

+0

Cảm ơn Tony. Tôi đã hiểu nhầm. Tôi nghĩ anh ta chỉ vừa mới tung đồ ra khỏi mặt trước chứ không phải từ giữa. Đã trả lời trong tin nhắn vì nó đã lâu ... –

1

Không có đủ ngữ cảnh để tìm ra những gì bạn đang cố gắng làm ở đây, nhưng nếu bạn cần COPY va_list, bạn có thể sử dụng chức năng chuẩn C99 va_copy, mà gcc hỗ trợ (nhưng tôi không có ý tưởng nếu MS hỗ trợ nó).

0

Có một cách để làm điều này, nó không phải là khá:

union { 
     char *pa; 
     va_list al; 
     } au; 

.... 
au.pa = new_args; 
vsprintf(buffer, sFormat.c_str(), au.al); 

Sử dụng một sự kết hợp thay vì một dàn diễn viên là xấu xí, nhưng bạn không thể cast nếu va_list là một loại mảng.

Các vấn đề liên quan