2010-09-02 20 views
9

Tôi có hệ thống ghi nhật ký mà tôi đang tìm cách tắt một số thao tác chuỗi.Chức năng quá tải nơi thông số chỉ khác nhau theo hình elip

Hệ thống ghi nhật ký được sử dụng thông qua các macro chức năng mà sau đó chuyển tiếp đến một cuộc gọi chức năng duy nhất. Ví dụ. #define Warning(...) LogMessage(eWarning, __VA_ARGS__);.

LogMessage sau đó thực hiện snprintf vào bộ đệm mới và sau đó hiển thị thông báo đó cho bất kỳ mục tiêu nhật ký nào xảy ra được cài đặt; printf, OutputDebugString, v.v.

Thật không may, tôi đã gặp sự cố trong đó bộ đệm mà chúng tôi chưa có đủ lớn, do đó đầu ra bị cắt bớt. Tôi cũng nhận ra rằng phương thức này sẽ thất bại nếu thông báo đầu ra có các ký hiệu phần trăm trong đó, vì snprintf sẽ cố gắng xử lý các va_args. Cuối cùng, vì phần lớn các thông điệp tường trình của chúng tôi không sử dụng va_args, có vẻ như ngớ ngẩn để sao chép chuỗi chỉ để trình bày nó cho các logger.

Vì vậy, với nguyên mẫu chức năng của tôi, tôi có thể quá tải dựa trên sự hiện diện của dấu ba chấm không? Nói cách khác, tôi sẽ có thể giả định rằng tôi có thể làm điều gì đó như:

LogMessage(LogLevel, const char* message, ...); 
LogMessage(LogLevel, const char* message); 

nỗ lực google của tôi đã không mang lại bất cứ điều gì đặc biệt hữu ích (chỉ hiển thị với tôi rằng elip sẽ phù hợp nếu không có gì khác không, dao động từ của tôi yêu cầu rằng không có gì là kết quả phù hợp) và lần truy cập đầu tiên của tôi khi triển khai đã cho tôi một lỗi gọi hàm chức năng mơ hồ.

Với lỗi, tôi chỉ nên chấp nhận rằng tôi không thể làm điều này, nhưng tôi tự hỏi nếu nó chỉ là trình biên dịch tôi đang sử dụng hoặc nếu có thể tôi đang làm sai. Tôi có thể đạt được một hiệu ứng tương tự với

// edited version of what I really have to remove our local APIs, 
// please excuse minor errors 
const char* message = NULL; 
char buffer[512]; 

va_list args; 
va_start(args, format); 

if(strcmp(format, "%s") == 0) { 
    message = va_arg(args, const char*); 
} 
else if (strchr(format, '%') == NULL) { 
    message = format; 
} 
else { 
    vsnprintf(buffer, 512, format, args); 
    message = buffer; 
} 

va_end(args); 

... nhưng điều này có vẻ lãng phí trong trường hợp điển hình có thể được biết đơn giản chỉ bằng số tham số được truyền. Ví dụ. nếu dấu ba chấm không khớp với bất kỳ thứ gì, hãy chọn hàm khác? Nếu điều này không làm việc, có một phương pháp khác tôi có thể thử mà không yêu cầu người dùng quyết định với tên macro mà chức năng sẽ được gọi là? Thành thật mà nói, nó thậm chí không nhiều về "chất thải" một khi tôi nhận ra rằng nếu một người nào đó bất ngờ nói Error("Buffer not 100% full"); trong thông điệp tường trình của họ và nhận được "Buffer không 1007.732873e10ull" kết quả là.

Chỉnh sửa: Trong khi ví dụ của tôi đã được trả lời bằng cách "không làm điều đó", câu hỏi có thể tự trả lời được không?

Trả lời

2

Tôi đã lấy cảm hứng từ câu trả lời ban đầu này câu hỏi, nhưng đã đưa ra một cải tiến nhỏ.

static void LogMessage(LogLevel level, const char* message); 

template <typename T> 
static void LogMessage(LogLevel level, const char* format, T t, ...) 
{ 
    LogMessageVA(level, format, (va_list)&t); 
} 

static void LogMessageVA(LogLevel level, const char* format, va_list argptr); 

Điều này hoạt động mà không phải giả định rằng đối số thứ hai là const char *.

3

Tôi cũng nhận ra rằng phương pháp này sẽ thất bại nếu thông báo đầu ra có các ký hiệu phần trăm trong đó, vì snprintf sẽ cố gắng xử lý các va_arg.

Sau đó, hãy cẩn thận với người gọi. Nếu hàm của bạn được ghi lại để lấy các chuỗi định dạng kiểu printf thì đó là trách nhiệm của người gọi để thoát khỏi bất kỳ dấu hiệu phần trăm nào. Nó không thực sự là công việc của bạn để cố gắng xử lý các chuỗi định dạng không hợp lệ.

Thành thực mà nói nó không phải là ngay cả khi nhiều về "chất thải" một lần tôi nhận ra rằng nếu ai đó bừa bãi nói Error("Buffer not 100% full"); trong thông điệp đăng nhập của họ và nhận được "đệm không 1007.732873e10ull" như vậy.

Tôi nghĩ rằng bạn nên đi cùng với đặc tính C++. Trong các phương thức Java thường kiểm tra các đối số hợp lệ và ném các ngoại lệ khi truyền các giá trị không hợp lệ. Trong C++, bạn chỉ cần để người gọi tự bắn vào chân.Tốt hơn là để họ viết 100%% hơn là nhảy qua hoops để bảo vệ họ không biết cách gọi hàm của bạn đúng cách.

+2

Tôi đoán tôi không mua triết lý đó, thích được tự do trong những gì tôi chấp nhận và bảo thủ trong những gì tôi gửi. IMO, nếu tôi có thể thần thánh ý định của người dùng, tôi nên. Phần mềm thất bại trong lĩnh vực này chỉ đơn giản là do các lập trình viên thiếu kinh nghiệm là không thể tha thứ cho tôi. Và quan điểm của tôi rõ ràng là tôi không muốn ghi lại các chức năng này như chấp nhận các chuỗi định dạng printf * chỉ *, tôi muốn chúng đi theo một trong hai cách. –

+0

Ngay sau khi bạn cho phép đối số kiểu printf, bạn * phải * giả định rằng đó là những gì họ đang có khi bạn nhìn thấy chúng, nếu không những người muốn sử dụng chúng sẽ không nhận được hành vi mà bạn mong đợi. Nó sẽ không hoạt động để thử và trộn liệu bạn có xử lý các đối số kiểu printf như các đối số kiểu printf hay không. Nó chỉ yêu cầu rắc rối. Bạn có thể kiểm tra xem chuỗi có hợp lệ cho sprintf hay không, nhưng đừng mong đợi nó hoạt động một cách sạch sẽ để chấp nhận các đối số kiểu printf và bỏ qua chúng cùng một lúc. –

+0

Và xem xét có bao nhiêu vấn đề phát triển web có bởi vì trình duyệt chấp nhận html xấu, tôi không mua cho một thứ hai chấp nhận tự do nhưng được bảo thủ trong những gì bạn gửi là một ý tưởng tốt. Chúng ta sẽ tốt hơn nếu web không được xây dựng bằng triết lý đó. Đừng làm điều đó trong mã của riêng bạn nếu bạn có thể tránh nó. Chắc chắn, chương trình trong một cách mà giảm thiểu sai sót (ngay cả đối với người mới), nhưng không hy sinh chính xác trong hy vọng vô ích của nó làm việc tốt hơn. Nó sẽ không. –

1

Ok tôi nghĩ rằng tôi đã đưa ra một giải pháp cho câu hỏi.

Thực tế là bạn không thể quá tải chỉ dựa trên việc có các tham số cho dấu ba chấm hay không. I E. bạn không thể có các hàm có chữ ký chỉ thay đổi khi có các dấu ba chấm.

Tuy nhiên, nó có thể làm điều gì đó giống như những gì tôi hỏi nếu tôi bỏ thông số const char* từ nguyên mẫu elip. I E.

LogMessage(LogLevel, ...); 
LogMessage(LogLevel, const char* message); 

là rõ ràng, nhưng bây giờ bạn chiến đấu với thực tế là bạn phải thừa nhận rằng các tham số đầu tiên là một const char*, nhưng nó có thể cũng không được. Nhận lời khuyên của John Kugelman, có lẽ điều đó tốt; bạn ghi lại các tham số được cho phép và người dùng cẩn thận. Hàm phi elip sẽ được gọi nếu chỉ có một const char* và hàm elip sẽ được gọi nếu có bất kỳ điều gì khác bao gồm cả tài liệu const char* theo sau bởi một số tham số.

Thật không may có vẻ như đây là mức độ của một giải pháp có thể cho phép bạn chuyển va_args vào chức năng con, trong trường hợp mẫu của tôi là vsnprintf.

Đó có thể là biểu mẫu không hợp lệ để chấp nhận câu trả lời của riêng tôi, mặc dù đó là câu trả lời cho câu hỏi được trình bày.

1

Trong C++ 11 bạn có thể sử dụng các mẫu variadic với một chuyên môn hóa rõ ràng đối với trường hợp đơn lập luận:

void bar(int a, ...) { 
    // va_list stuff 
} 

template <typename... T> 
void foo(int a, T... args) { // (1) 
    bar(a, args...); // or do all the vararg stuff here directly 
} 

template <> 
void foo(int a) {   // (2) 
    printf("single\n"); 
} 

Sau đó:

//foo(); // compile error, as expected 
foo(1);  // uses (2) 
foo(2,1); // uses (1) 
foo(3,1,"asdf"); // uses (1) 
... 
Các vấn đề liên quan