2009-01-18 31 views
6

Tôi đang sử dụng một ứng dụng trong C++ có sử dụng một hàm dprintf đặc biệt để in thông tin, đây là một ví dụ:thông tin In trong "chế độ kiểm tra" nhưng không phải trong "thực hiện bình thường"

dprintf(verbose, "The value is: %d", i); 

đang làm gì khi tôi xác định tiết cho mục đích thử nghiệm thì tôi in thông tin và khi tôi làm việc trong thực thi bình thường, tôi không xác định nó và tôi không thấy thông tin vô dụng trên màn hình. Câu hỏi của tôi là làm thế nào tôi có thể thực hiện chức năng đó hoặc thực hiện cùng một ý tưởng ?.

Trả lời

14

tôi cố gắng tránh sử dụng var-arg chức năng c-style vì hai lý do chính:

  • Họ không gõ an toàn, không thể sử dụng toán tử < <
  • Họ không nhận ra khi quá ít hoặc nhiều đối số đã được cung cấp

Tôi đã thực hiện một cách hoạt động bằng cách sử dụng boost::fusion, được đưa ra các đối số theo cách an toàn. Nó lặp qua các đối số đó, in ra chúng khi gặp phải %. Nếu quá ít hoặc quá nhiều đối số đã được đưa ra, một ngoại lệ được ném.

Vẫn còn một vấn đề: Macro biến thể chưa chuẩn trong C++. Vì vậy, tôi đã thực hiện hai phiên bản. Một hoạt động với C++ hiện tại. Bạn phải gọi nó bằng cách sử dụng

dprintf("name: %, value: %\n", ("foo", 42)); 

Sau đó. Bạn có thể sử dụng phiên bản khác, sử dụng macro variadic, bằng cách xác định biểu tượng tiền xử lý, cho phép bạn viết

dprintf("name: %, value: %\n", "foo", 42); 

Đây là mã. boost.fusion cung cấp thêm chi tiết về điều này:

#include <boost/fusion/include/sequence.hpp> 
#include <boost/fusion/include/make_vector.hpp> 
#include <boost/fusion/include/next.hpp> 
#include <stdexcept> 
#include <iostream> 

template<typename IterS, typename IterSeqE> 
void print_vec(IterS b, IterS e, IterSeqE, IterSeqE) { 
    while(b != e) { 
     if(*b == '%') { 
      if(++b != e && *b == '%') { 
       std::cout << '%'; 
      } else { 
       throw std::invalid_argument("too many '%'"); 
      } 
     } else { 
      std::cout << *b; 
     } 
     ++b; 
    } 
} 

template<typename IterS, typename IterSeqB, typename IterSeqE> 
void print_vec(IterS b, IterS e, IterSeqB seqb, IterSeqE seqe) { 
    while(b != e) { 
     if(*b == '%') { 
      if(++b != e && *b == '%') { 
       std::cout << '%'; 
      } else { 
       std::cout << *seqb; 
       return print_vec(b, e, next(seqb), seqe); 
      } 
     } else { 
      std::cout << *b; 
     } 
     ++b; 
    } 
    throw std::invalid_argument("too few '%'"); 
} 

template<typename Seq> 
void print_vec(std::string const& msg, Seq const& seq) { 
    print_vec(msg.begin(), msg.end(), begin(seq), end(seq)); 
} 

#ifdef USE_VARIADIC_MACRO 
# ifdef DEBUG 
# define dprintf(format, ...) \ 
     print_vec(format, boost::fusion::make_vector(__VA_ARGS__)) 
# else 
# define dprintf(format, ...) 
# endif 
#else 
# ifdef DEBUG 
# define dprintf(format, args) \ 
     print_vec(format, boost::fusion::make_vector args) 
# else 
# define dprintf(format, args) 
# endif 
#endif 

// test, using the compatible version. 
int main() { 
    dprintf("hello %, i'm % years old\n", ("litb", 22)); 
} 
+1

Tôi không thể nói cho các trình biên dịch khác, nhưng đối với GCC, -Wformat và __attribute __ ((định dạng)) là bạn của bạn. Điều đó cho phép các API kiểu printf với tất cả các tính năng kiểm tra kiểu của toán tử <<. – Tom

+1

vâng, tôi biết các thuộc tính đó. nhưng nó sẽ không cho phép bạn vượt qua các loại riêng. ví dụ, bạn có thể sử dụng một danh sách. và muốn quá tải toán tử << cho danh sách của bạn. với printf, bạn được yêu cầu viết cùng một vòng lặp lại, và được giới hạn trong các kiểu phần tử nhất định mà printf hiểu. –

+0

toán tử quá tải << cho phép đặt mã in ở mặt bên của loại bạn muốn in, mà tôi thích đặt mã in ở bên cạnh mã gỡ lỗi của tôi.vì vậy, bất cứ khi nào bạn có thứ gì đó có thể in được, giải pháp dprintf loại an toàn của bạn đã hiểu nó. –

9
#ifdef DEBUG 
#define dprintf(format, ...) real_dprintf(format, __VA_ARGS__) 
#else 
#define dprintf 
#endif 

Tại đây real_dprintf() là hàm "thực" được gọi và dprintf() chỉ là một macro gói cuộc gọi.

+0

Tôi sẽ đăng câu trả lời tương tự, vì vậy tôi lưu nó và upvote của bạn. Đây là tình huống duy nhất mà tôi sử dụng các macro tiền xử lý trong mã C++. –

4

Giải pháp tiền xử lý sẽ hoạt động, nhưng có thể gây khó chịu khi phải xây dựng lại để thay đổi từ cái này sang cái kia. Tôi sẽ thường đưa ra quyết định trong thời gian chạy. đầu tiên tôi tuyên bố:

static void do_nothing(const char *fmt, ...) { (void)fmt; } 
extern void real_dprintf(const char *fmt, ...); 
void (*dprintf)(const char *fmt, ...) = do_nothing; 

Sau đó trong mã khởi tạo Tôi có

if (getenv("APPLICATION") && strstr(getenv("APPLICATION"), "dprintf")) 
    dprintf = real_dprintf; 

Bằng cách này tôi có thể nhanh chóng thay đổi chế độ bằng cách thay đổi giá trị của một biến môi trường.

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