staram się unikać var-Arg funkcje c stylu z dwóch głównych powodów:
- nie są one typu bezpieczny, nie można używać operatorowi < <
- Oni nie uznają kiedy podano zbyt mało lub wiele argumentów:
Zrobiłem sposób, który działa przy użyciu boost::fusion
, który podaje argumenty w sposób bezpieczny dla typu. Powtarza on te argumenty, drukując je po napotkaniu %
. Jeśli podano zbyt mało lub zbyt wiele argumentów, zostanie zgłoszony wyjątek.
Jest jeszcze jeden problem: makra Variadic nie są jeszcze standardem w C++. Tak, zrobiłem dwie wersje. Jeden, który działa z obecnym C++. Musisz go wywołać, używając:
dprintf("name: %, value: %\n", ("foo", 42));
Następnie. Druga wersja, za pomocą zmiennej liczbie argumentów makr, może być używany przez zdefiniowanie symbolu preprocesora, który pozwala pisać
dprintf("name: %, value: %\n", "foo", 42);
Oto kod. Numer boost.fusion
zawiera więcej szczegółów:
#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));
}
Nie mogę mówić o innych kompilatorach, ale dla GCC, -Wformat i __attribute __ ((format)) są twoimi przyjaciółmi. To pozwala na API w stylu printf z całą dobrością sprawdzania typu operatora <<. – Tom
Tak, jestem świadomy tych atrybutów. ale nie pozwoli ci przekazać własnych typów. na przykład możesz użyć listy. i chcesz przeciążyć operatora << dla twojej listy. z printf, musisz ponownie napisać tę samą pętlę i są one ograniczone do pewnych typów elementów, które rozumie printf. –
Przeciążanie operatora << pozwala umieścić kod drukowania na stronie typu, który chcesz wydrukować, a ja wolę umieszczać kod drukowania na boku mojego kodu debugowania.więc, kiedy masz coś do wydrukowania, twoje bezpieczne dla Twojego rodzaju rozwiązanie dprintf już to rozumie. –