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));
}
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
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. –
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ó. –