Използвайте функцията Stream-like Input to C++

Наясно съм, че човек може да използва макроси на препроцесор, за да създаде функция, която приема „подобен на поток“ вход. За да изясня какво имам предвид под „подобен на поток“ вход, позволете ми да ви дам пример (входът към LOGGER е „подобен на поток“):

#include <iostream>
#include <sstream>

#define LOGGER(streamText) {   \
    std::ostringstream buffer; \
    buffer << streamText;      \
    /* Some processing */      \
    std::cout << buffer.str() << std::endl; \
}

int main(){
    LOGGER("Foo: " << "Bar!: " << 42);
}

Това извежда:

Foo: Бар!: 42

Бих искал да имам подобен интерфейс за обикновена C++ функция. Нещо като това (не се компилира, не е валиден C++ код):

// has:
//    - insert( std::ostringstream & str ) function for obtaining content of a std::ostringstream
//    - formatting functions
//    - print( std::ostream & str ) function
class logger_class;

logger_class logger_function( some_magic_type varName ){
    std::ostringstream & str = varName;
    logger_class log;
    log.insert( str );
    return log;
}

int main(){
    logger_class log = logger_function("Foo: " << "Bar!: " << 42);
    log.format( some_formatting_options );
    log.print( std::cout );
}

Което би позволило форматиране на изхода преди действителното му изпращане към std::cout.

Редактиране: C++11 и решенията за усилване също са разрешени.


person elemakil    schedule 15.01.2014    source източник
comment
Търсите ли претоварени версии на ‹‹-оператора (замяна на std::out или std::err с име logger?)   -  person urzeit    schedule 15.01.2014
comment
@urzeit Нещо като. Искам да се уверя, че регистраторът може да приеме, че въведеното, което му беше предадено току-що, е пълното съобщение. Нека ви дам пример: Една опция за форматиране е оцветяването, което (ако приемем ANSI терминал) се състои от ограждане на съдържанието с два разделителя. Чрез използването на макро решението това може да се направи лесно чрез добавяне на разделители преди и след поточно предаване на лог съобщението в изходния поток (тъй като цялото лог съобщение се съдържа в аргумента на макроса).   -  person elemakil    schedule 15.01.2014
comment
Искам функцията да върне обекта за регистриране, защото това позволява да се зададат опциите за форматиране чрез използване на верига на методите, т.е.: logger_function("Foo" << 42).log_level( 500 ).color( RED ).weight( BOLD ).end_line();   -  person elemakil    schedule 15.01.2014
comment
Изглежда, че смесвате два стила: верига на методите и поточно предаване (което обикновено използва манипулатори вместо извиквания на методи). Например, ще работи ли този синтаксис: log() << "Foo" << 42 << log_level(500) << log_color(RED) << log_weight(BOLD) << endl;?   -  person Useless    schedule 15.01.2014
comment
Да, смесвам стрийминг и метод верига. Знам. Поточното предаване се извършва, за да формира аргумента на функция, която връща обект (съдържащ данните, които са комбинирани чрез използване на поточно предаване). Този обект може да бъде конфигуриран с помощта на верига на методите и в крайна сметка ще запише своите съхранени (и вече форматирани) данни в действителен изходящ поток.   -  person elemakil    schedule 15.01.2014
comment
За да се постигне оцветяване с помощта на поточно предаване, трябва да се напише само: log() << log_level( 500 ) << log_color(RED) << log_weight(BOLD) << "Foo" << 42 << finish_color() << finish_weight() << std::endl;, тъй като ANSI оцветяването изисква начално оцветяване и крайно оцветяване последователност.   -  person elemakil    schedule 15.01.2014


Отговори (1)


Използвайки C++11, можете да използвате различни шаблони:

template <typename T>
std::ostringstream & fill_stream(std::ostringstream & str, T&& t)
{
    return (str << std::forward<T>(t));
}

template <typename T, typename... Vs>
std::ostringstream & fill_stream(std::ostringstream & str, T&& t, Vs&&... vs)
{
    str << std::forward<T>(t);
    return fill_stream(std::forward<Vs>(vs)...);
}

template <typename... Ts>
logger_class logger_function( Ts&&... vars ){
    std::ostringstream & str;

    if (!fill_stream(str, std::forward<Ts>(vars)...))
    {
        //Error!
    }

    logger_class log;
    log.insert( str );
    return log;
}

Тогава:

int main()
{
    logger_class log = logger_function("Foo: ", "Bar!: ", 42);
    log.format( some_formatting_options );
    log.print( std::cout );
}

Ако искате да избегнете незабавното създаване на поток от низове, можете да създадете шаблонен клас, който съхранява своите аргументи в кортеж.

template <typename... Ts>
logger_class<Ts&&...> logger_function( Ts&&... vars ){
    // logger_class should probably store its arguments as a tuple
    logger_class<Ts&&...> log ( std::forward<Ts>(vars)... );
    return log;
}

int main()
{
    auto log = logger_function("Foo: ", "Bar!: ", 42);
    log.format( some_formatting_options );
    log.print( std::cout );
}
person Apples    schedule 15.01.2014