2014-09-16 6 views
6
переменным числом

Предположим, у меня есть функция с printf -как (используемой для регистрации), используя совершенную переадресацию:подталкивание :: формат с аргументами шаблона

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()); 
} 

(я не компилировать это, но моя настоящая функция следует этой рекомендации)

Как я могу «развернуть» переменный аргумент в переменную boost :: format f?

+0

Я не знаю, будет ли это работать, но вы пробовали, например, '' Арг ...? –

+0

@JoachimPileborg Я пробовал это: http://coliru.stacked-crooked.com/a/9e651d5f7532cc67, он не работает, к сожалению (если я не ошибаюсь). –

+0

Вот как вы расширяете вариационные аргументы шаблона. К сожалению, формат Boost использует перегруженный оператор '%' для разделения аргументов, который не будет работать с расширенными пакетами аргументов. –

ответ

10

Как обычно с переменным числом шаблонов, вы можете использовать рекурсию:

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.


В C++ 17 просто (f % ... % std::forward<Arguments>(args)); будет делать.

+2

Принуждение новой строки в конце вывода не является хорошей идеей для повторного использования: она легко интегрируется в форматированную строку, и делает его обязательным, исключает расширение линии * до того, как она закончится. – Deduplicator

+0

Исправлена ​​ошибка дедупликатора. –

7

Я сделал некоторые погуглите и нашел интересное решение:

#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); 
} 

Я не знаю, если это рекомендуемое решение, но это, кажется, работает. Мне не нравится использование hacky static_cast, что кажется необходимым отключить предупреждения неиспользуемых переменных в GCC.

+3

Вы можете избежать приведения, не создавая временную переменную 'using unroll = int []; unroll {0, (f% std: forward (args), 0) ...}; ' – Praetorian

+0

@Praetorian Это хорошо работает; однако «использование» кажется излишним ... Интересно, почему 'int [] {/ * ... * /};' не работает ... –

+0

Потому что грамматика не позволяет этого. Это либо «использование», либо литье. – Praetorian

4

Просто суммировать void.pointer's solution и намеки предложены by Praetorian, T.C. и Jarod42, позвольте мне представить окончательный вариант (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 
} 

Кроме того, as it was noted by T.C., используя синтаксис fold expression, доступен с C++ 17 , функция FormatArgs может быть переписана более кратким образом.

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

Вау, мне нравится выражение складки ... Я даже не знал, что они добавили это. Потрясающие. Вопрос для ясности: требуется ли внутренняя скобка в вашем решении для выражения сгиба? Другими словами, будет ли это работать: 'boost :: str (boost :: format (fmt)% ...% args);' –

+0

Я расширил вашу демоверсию, чтобы включить решение для выражения сгиба: http: //coliru.stacked- crooked.com/a/bfabe441fba08d62 Я также попытался удалить эти внутренние скобки, но он не компилируется. Было бы образовательным узнать, почему. Диагностика не очень помогает. –

+0

@ void.pointer, кажется, что круглые скобки вокруг выражения сгиба являются обязательной частью этого и не могут быть опущены. Вы можете посмотреть [описание грамматики выражения сгиба] (http://en.cppreference.com/w/cpp/language/fold) (раздел «Объяснение»). Когда выражение fold расширяется, скобки вокруг него удаляются, поэтому в случае boost :: str ((fold expr)) нам нужно иметь дополнительную скобку, чтобы получить boost :: str (расширенный fold expr) после того, как выражение fold расширяется – PolarBear

Смежные вопросы