2012-02-16 13 views
11

Tôi có chức năng cố gắng ghi nhật ký công cụ vào bảng điều khiển và cũng vào tệp nhật ký, nhưng nó không hoạt động. Việc sử dụng thứ hai của đối số chiều dài biến cho rác được ghi vào bàn điều khiển. Ý tưởng nào?Việc sử dụng lặp lại đối số hàm variadic không hoạt động

void logPrintf(const char *fmt, ...) { 
     va_list ap; // log to logfile 
     va_start(ap, fmt); 
     logOpen; 
     vfprintf(flog, fmt, ap); 
     logClose; 
     va_end(ap); 
     va_list ap2; // log to console 
     va_start(ap2, fmt); 
     printf(fmt, ap2); 
     va_end(ap2); 
    } 
+1

Bạn cần sử dụng vprintf lần thứ hai, không phải printf. –

Trả lời

-2

Tôi nghĩ rằng cách này có ý nghĩa hơn:

void logPrintf(const char *fmt, ...) { 
     va_list ap; // log to logfile 
     va_start(ap, fmt); 
     logOpen; 
     vfprintf(flog, fmt, ap); //logfile 
     printf(fmt, ap); //console 
     logClose; 
     va_end(ap); 
    } 
+0

Cảm ơn Tony, nhưng điều đó cũng không hiệu quả. Nó giống như con trỏ được để lại ở cuối danh sách để sử dụng thứ hai là rác. – Neddie

+0

Yup, đó là chính xác những gì đang xảy ra. 'va_list's luôn là tham chiếu. –

+1

Ví dụ này (ngay cả với 'vprintf' thay vì' printf') là sai theo trang người đàn ông: "Nếu ap được chuyển đến một hàm sử dụng va_arg (ap, type) thì giá trị của ap là không xác định sau khi trả về chức năng đó. " Trên linux x86-32, nó hoạt động như dự định, nhưng không có nghĩa là trên x86-64. –

2

Nâng cấp trình biên dịch của bạn, có nghĩa là giống như C++:

template <typename... Args> 
void logPrintf(const char *fmt, Args&&... args) { 
    logOpen; 
    fprintf(flog, fmt, args...); 
    logClose; 

    printf(fmt, args...); 
} 

Mặc dù tất nhiên nó sau đó sẽ là hương vị tốt để cung cấp các phiên bản an toàn của printffprintf.

+0

Ví dụ trên yêu cầu C++ 11. –

+1

@DavidGiven: có (do đó, * nâng cấp trình biên dịch của bạn *). –

+0

Tôi nghĩ rằng vfprintf cần phải được fprintf thay vì ở đây - bạn đang đi qua các đối số thực tế chứ không phải là một va_list. –

8

Mã gốc không thành công vì mã cố gắng sử dụng printf() nơi mã cần sử dụng vprintf(). Lấy điểm đáng ngờ như logOpenlogClose báo cáo theo mệnh giá (cho các ký hiệu, có lẽ họ đang macro mà mở và đóng dòng flog tập tin), mã nên được:

void logPrintf(const char *fmt, ...) { 
    va_list ap; 
    va_start(ap, fmt); 
    logOpen; 
    vfprintf(flog, fmt, ap); 
    logClose; 
    va_end(ap); 
    va_list ap2; 
    va_start(ap2, fmt); 
    vprintf(fmt, ap2); 
    va_end(ap2); 
} 

Không có yêu cầu đặc biệt để sử dụng hai biến số va_list riêng biệt; bạn hoàn toàn có thể sử dụng cùng một lần hai lần miễn là bạn sử dụng va_end() trước khi sử dụng lại va_start().

void logPrintf(const char *fmt, ...) { 
    va_list ap; 
    va_start(ap, fmt); 
    logOpen; 
    vfprintf(flog, fmt, ap); 
    logClose; 
    va_end(ap); 
    va_start(ap, fmt); 
    vprintf(fmt, ap); 
    va_end(ap); 
} 

Khi một giá trị va_list được chuyển đến một chức năng (vfprintf()vprintf() trong mã này), bạn nên cho rằng nó không còn có thể sử dụng trong hàm hiện tại. Chỉ an toàn khi gọi số va_end() trên đó.

Không cần va_copy() trong mã này. Nó hoạt động, nhưng nó không cần thiết. Bạn cần va_copy() trong những hoàn cảnh khác, chẳng hạn như khi chức năng của bạn là thông qua một va_list và bạn cần phải xử lý danh sách hai lần:

void logVprintf(const char *fmt, va_list args1) 
{ 
    va_list args2; 
    va_copy(args2, args1); 
    logOpen; 
    vfprintf(flog, fmt, args1); 
    logClose; 
    vprintf(fmt, args2); 
    va_end(args2); 
} 

Lưu ý rằng trong mã này, đó là trách nhiệm mã gọi để gọi va_end() trên args1. Trên thực tế, tiêu chuẩn nói:

Mỗi gọi của va_startva_copy macro được kết hợp bởi một invocation tương ứng của va_end vĩ mô trong cùng chức năng.

Kể từ khi logVprintf() chức năng không gọi hoặc va_start hoặc va_copy để khởi args1, nó có thể không hợp pháp gọi va_end trên args1. Mặt khác, tiêu chuẩn yêu cầu nó gọi va_end cho args2.

Chức năng logPrintf() có thể được thực hiện trong điều kiện của logVprintf() bây giờ:

void logPrintf(const char *fmt, ...) 
{ 
    va_list args; 
    va_start(args, fmt); 
    logVprintf(fmt, args); 
    va_end(args); 
} 

Cấu trúc này - một chức năng hoạt động mà phải mất một va_list và một hàm bìa mà sẽ đưa ellipsis (arguments biến) và chuyển chúng đến hoạt động sau khi chuyển đổi sang một số va_list - thường là cách tốt để làm việc. Sớm hay muộn, bạn thường thấy cần thiết cho phiên bản với một đối số va_list.

+2

Đây là câu trả lời đúng để giải quyết trực tiếp câu hỏi của OP mà không đề xuất thay đổi kiến ​​trúc của họ. – Vortico

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