Как сделать переменный макрос для std:: cout?

Как сделать макрос, который принял переменное количество аргументов, и распечатывает его с помощью std:: cout? Извините, если это вопрос noob, не удалось найти ничего, что разъяснило переменные макросы после поиска ответа.

Концептуальный пример:

#include <iostream>
#define LOG(...) std::cout << ... << ... << std::endl
int main() {
    LOG("example","output","filler","text");
    return 0;
}

выводит:

exampleoutputfillertext

Ответ 1

Для этого вам не нужны макросы препроцессора. Вы можете написать это в обычном С++. В С++ 11/14:

#include <iostream>
#include <utility>

void log(){}

template<typename First, typename ...Rest>
void log(First && first, Rest && ...rest)
{
    std::cout << std::forward<First>(first);
    log(std::forward<Rest>(rest)...);
}

int main()
{
    log("Hello", "brave","new","world!\n");
    log("1", 2,std::string("3"),4.0,'\n');
}

Живая демонстрация

В С++ 17:

template<typename ...Args>
void log(Args && ...args)
{
    (std::cout << ... << args);
}

- это все, что требуется. Живая демонстрация

Вывод:

Hellobravenewworld!
1234

Исследование вариативные шаблоны, пакеты параметров и свернуть выражения а не переменные макросы, которые редко бывают полезны в современном С++.

Ответ 2

При использовании Variadic макросов вам нужно __VA_ARGS__, чтобы развернуть все аргументы. Однако проблема состоит в том, что эти аргументы разделены запятой. Предположительно это просто разделяет аргументы на вызов функции, но поскольку макросы работают только с текстом, вы действительно можете использовать __VA_ARGS__ в других контекстах, где список разделенных запятой имеет смысл.

Трюк, который вы можете использовать, - это определить свой собственный оператор запятой для std::ostream (тип std::cout). Например:

#include<iostream>
#define LOG(...) std::cout , __VA_ARGS__ , std::endl

template <typename T>
std::ostream& operator,(std::ostream& out, const T& t) {
  out << t;
  return out;
}

//overloaded version to handle all those special std::endl and others...
std::ostream& operator,(std::ostream& out, std::ostream&(*f)(std::ostream&)) {
  out << f;
  return out;
}

int main() {
  LOG("example","output","filler","text");
  return 0;
}

Теперь вызов LOG будет расширяться до:

std::cout , "example" , "output" , "filler" , "text" , std::endl;

и запятые вызовут перегруженные операторы запятой.

Если вам не нравится перегружать operator, загрязнение всех std::ostream -s, вы можете инкапсулировать std::cout своим собственным специальным классом журнала.

Ответ 3

Не уверен, что есть какой-либо способ определения вариационного макроса в С++ (по крайней мере, не переносимый). Почему вы не используете альтернативный шаблонный подход? Что-то вроде

#include <iostream>

void LOG() {}

template<typename Head, typename... Args>
void LOG(const Head& head, const Args&... args )
{
    std::cout << head << " ";
    LOG(args...);
}

int main()
{
    LOG("This", "is" , "the", 3, "rd test");
}