Я стараюсь избегать использования вар-Arg функции C-стиля по двум основным причинам:
- Они не типобезопасным, не может использовать оператор < <
- Они не признают, когда слишком мало или много аргументов были предоставлены
Я сделал способ, который работает с использованием boost::fusion
, который предоставляет аргументы безопасным способом. Он повторяет эти аргументы, распечатывая их, когда встречается %
. Если задано слишком мало или слишком много аргументов, генерируется исключение.
Есть еще одна проблема: макросы Variadic еще не являются стандартными в C++. Итак, я сделал две версии. Один, который работает с текущим C++. Вы должны использовать его, используя
dprintf("name: %, value: %\n", ("foo", 42));
Затем. Другой вариант, используя VARIADIC макросы, можно использовать, определив символ препроцессора, который позволяет писать
dprintf("name: %, value: %\n", "foo", 42);
Вот код. boost.fusion
содержит более подробную информацию для этого:
#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));
}
Я не могу говорить для других компиляторов, но для GCC, -Wformat и __attribute __ ((format)) являются вашими друзьями. Это позволяет использовать API-интерфейсы типа printf со всей проверкой корректности оператора <<. – Tom
Да, я знаю об этих атрибутах. но это не позволит вам передавать собственные типы. например, вы можете использовать список. и хотите перегрузить оператор << для вашего списка. с printf, вы должны снова написать один и тот же цикл и ограничены определенными типами элементов, которые понимает printf. –
оператор перегрузки << позволяет печатать код на стороне типа, который вы хотите распечатать, который я предпочитаю помещать код печати на стороне моего кода отладки.поэтому, всякий раз, когда у вас есть что-то пригодное для печати, ваше безопасное для печати решение dprintf уже понимает его. –