2014-09-16 27 views
6

Załóżmy, że mam printf -jak funkcji (używane do logowania) wykorzystując idealne do korespondencji:boost :: Format ze zmiennej liczbie argumentów argumentów szablonu

template<typename... Arguments> 
void awesome_printf(std::string const& fmt, Arguments&&... args) 
{ 
    boost::format f(fmt); 
    f % /* How to specify `args` here? */; 
    BlackBoxLogFunction(boost::str(f).c_str()); 
} 

(nie skompilować tego, ale moje prawdziwe funkcja Wynika to wytyczna)

Jak mogę "rozwinąć" argument zmienny do zmiennej :: :: format zmiennej f?

+0

Nie wiem, czy to zadziała, ale czy próbowałeś np. 'args ...'? –

+0

@JoachimPileborg Próbowałem tego: http://coliru.stacked-crooked.com/a/9e651d5f7532cc67, to nie działa niestety (chyba że robię to źle). –

+0

W ten sposób rozwijasz argumenty szablonu variadic. Niestety format doładowania używa przeciążonego operatora '%' do oddzielenia argumentów, co nie będzie działać z rozwiniętymi pakietami argumentów. –

Odpowiedz

10

Jak to zwykle bywa przy zmiennej liczbie argumentów szablonów, można użyć rekurencji:

std::string awesome_printf_helper(boost::format& f){ 
    return boost::str(f); 
} 

template<class T, class... Args> 
std::string awesome_printf_helper(boost::format& f, T&& t, Args&&... args){ 
    return awesome_printf_helper(f % std::forward<T>(t), std::forward<Args>(args)...); 
} 

template<typename... Arguments> 
void awesome_printf(std::string const& fmt, Arguments&&... args) 
{ 
    boost::format f(fmt); 

    auto result = awesome_printf_helper(f, std::forward<Arguments>(args)...); 

    // call BlackBoxLogFunction with result as appropriate, e.g. 
    std::cout << result; 
} 

Demo.


W języku C++ 17 wystarczy po prostu (f % ... % std::forward<Arguments>(args));.

+2

Wymuszenie znaku nowej linii na końcu wyjścia nie jest dobrym pomysłem na ponowne wykorzystanie: jest łatwo zintegrowane z ciągiem formatującym, a uczynienie tego obowiązkowym wyklucza przedłużenie linii * przed końcem *. – Deduplicator

+0

Naprawiono @Deduplicator. –

7

Zrobiłem trochę googling i jakieś ciekawe rozwiązanie:

#include <iostream> 
#include <boost/format.hpp> 

template<typename... Arguments> 
void format_vargs(std::string const& fmt, Arguments&&... args) 
{ 
    boost::format f(fmt); 
    int unroll[] {0, (f % std::forward<Arguments>(args), 0)...}; 
    static_cast<void>(unroll); 

    std::cout << boost::str(f); 
} 

int main() 
{ 
    format_vargs("%s %d %d", "Test", 1, 2); 
} 

nie wiem, czy jest to zalecane rozwiązanie, ale wydaje się działać. Nie podoba mi się użycie hacky static_cast, które wydaje się konieczne, aby uciszyć nieużywane ostrzeżenia zmienne w GCC.

+3

Możesz uniknąć rzutowania, nie tworząc zmiennej tymczasowej 'using unroll = int []; unroll {0, (f% std :: forward (args), 0) ...}; ' – Praetorian

+0

@Praetorian To ładnie działa; jednak "używanie" wydaje się zbędne ... Zastanawiam się, dlaczego 'int [] {/ * ... * /};' nie działa ... –

+0

Ponieważ gramatyka na to nie pozwala. Jest to albo użycie, albo obsada. – Praetorian

4

Wystarczy podsumować void.pointer's solution i wskazówki proponowane by Praetorian, T.C. i Jarod42, pozwól mi dostarczyć ostateczną wersję (online demo)

#include <boost/format.hpp> 
#include <iostream> 

template<typename... Arguments> 
std::string FormatArgs(const std::string& fmt, const Arguments&... args) 
{ 
    boost::format f(fmt); 
    std::initializer_list<char> {(static_cast<void>(
     f % args 
    ), char{}) ...}; 

    return boost::str(f); 
} 

int main() 
{ 
    std::cout << FormatArgs("no args\n"); // "no args" 
    std::cout << FormatArgs("%s; %s; %s;\n", 123, 4.3, "foo"); // 123; 4.3; foo; 
    std::cout << FormatArgs("%2% %1% %2%\n", 1, 12); // 12 1 12 
} 

Również as it was noted by T.C., używając składni fold expression, dostępny od C++ 17 , funkcja FormatArgs może być przepisana w bardziej zwięzły sposób

template<typename... Arguments> 
std::string FormatArgs(const std::string& fmt, const Arguments&... args) 
{ 
    return boost::str((boost::format(fmt) % ... % args)); 
} 
+0

Wow, uwielbiam wyrażenie krotnie ... Nie wiedziałem nawet, że dodali to. Niesamowite. Pytanie o jasność: czy wymagane jest nawiasy wewnętrzne w twoim rozwiązaniu wyrażenia krotności? Innymi słowy, czy to działa: 'boost :: str (boost :: format (fmt)% ...% args);' –

+0

Rozszerzyłem twoje demo, aby uwzględnić twoje rozwiązanie krotności wypowiedzi: http: //coliru.stacked- crooked.com/a/bfabe441fba08d62 Próbowałem również usunąć te wewnętrzne nawiasy, ale nie kompiluje. Byłby edukacyjny, aby wiedzieć, dlaczego. Diagnostyka niewiele pomaga. –

+0

@ void.pointer, wydaje się, że nawias wokół wyrażenia krotności jest jego obowiązkową częścią i nie można go pominąć. Możesz zajrzeć do [gramatycznego opisu wyrażenia krotności] (http://en.cppreference.com/w/cpp/language/fold) (sekcja "Objaśnienie"). Po rozwinięciu wyrażenia krotki, nawiasy wokół niego są usuwane, więc w przypadku boost :: str ((fold expr)), potrzebujemy dodatkowego nawiasu, aby uzyskać :: :: rozplanowanie (rozszerzone fałdowanie) po rozwinięciu wyrażenia – PolarBear

Powiązane problemy