Я хочу написать макрос, который принимает в качестве единственного аргумента список std :: ostream& оператор<< объединяет объекты и передает консолидированную строку как один объект std :: string в функцию. Возможность передачи объединенной строки в функцию является ключевой; В приведенном ниже примере я знаю, что сам пример может быть переписан для работы просто путем определения макроса в ERR_MSG(inputs) std::cout << "ERROR: " << inputs
, но отправка вывода в std :: cout — это не цель, а просто цель теста, которую я выбрал для примера.
Я использую GCC 4.1.2 (Red Hat 4.1.2-52) и не могу обновить его. Вот очень упрощенная версия того, что я пробовал:
#include <sstream>
#include <iostream>
#define ERR_MSG(inputs) errMsg(std::ostringstream().str()) // 1
#define ERR_MSG(inputs) errMsg((std::ostringstream()<<inputs).str()) // 2
<aReturnType> errMsg(const std::string& msg) // use with 1 & 2
{
std::cout << "\nERROR: " << msg << "\n\n";
return <someObjectCreatedBasedOnTheInput>;
}
#define ERR_MSG(inputs) errMsg(std::ostringstream()<<inputs) // 3
<aReturnType> errMsg(const std::ostringstream& msg) // use with 3
{
std::cout << "\nERROR: " << msg.str() << "\n\n";
return <someObjectCreatedBasedOnTheInput>;
}
int main()
{
ERR_MSG("A number: " << 24 << ", a char: " << 'c' << ", that's all!");
}
Макрос # 1 компилируется, но, конечно, ничего не печатает, кроме «» для сообщения. Ни один макрос 2 & 3 компиляции, со следующими ошибками:
#define ERR_MSG(inputs) errMsg((std::ostringstream()<<inputs).str()) // 2
error: ‘struct std::basic_ostream<char, std::char_traits<char> >’ has no member named ‘str’
#define ERR_MSG(inputs) errMsg(std::ostringstream()<<inputs) // 3
no matching function for call to ‘errMsg(std::basic_ostream<char, std::char_traits<char> >&)’
note: candidates are: char* errMsg(const std::string&)
note: char* errMsg(const std::ostringstream&)
я не интересно, как я мог бы переписать это без макросов; Я могу сделать это довольно легко сам.
=== ОБНОВЛЕНИЕ: ===
Я забыл упомянуть, что в своем реальном случае функция, вызываемая макросом, возвращает объект, который может использовать вызывающий макрос. Это делает недействительными любые реализации макросов, которые не могут быть реализованы в одном выражении, результатом которого является возвращаемый тип функции, вызываемой макросом. Реализация макроса «ничего не делать» (для сборок релиза) просто передаст пустую строку std :: string функции независимо от того, что является «входными данными». Извините, что не упомянул об этом раньше.
Ваша текущая проблема заключается в том, что все operator<<
функции возвращают ostream&
не ostringstream&
, Вы можете решить это с простой актерский состав:
#define ERR_MSG(inputs) errMsg((static_cast<std::ostringstream&>(std::ostringstream().flush() << inputs)).str())
flush
необходимо, потому что std::ostringstream()
является временным. Следовательно, вы не можете вызывать функции, которые принимают ссылку на lvalue (т.е. std::ostream&
). Функции, как большинство operator<<
варианты. Все flush
вызов это вернуть this
указатель как ссылка lvalue.
Если вы готовы использовать некоторые Расширение GCC, вы могли бы объявить фактический ostringstream внутри макроса в блоке, чтобы метод .str () мог использоваться без приведения:
#define ERR_MSG(inputs) \
do { std::ostringstream _s_; _s_<<inputs;errMsg(_s_.str()); } while(false)
Демо-версия: http://ideone.com/clone/y56lc
использование do { } while (false)
идиома, чтобы сделать несколько строк макроса.
#define ERR_MSG(inputs) \
do { \
std::ostringstream osERR_MSG; \
osERR_MSG << inputs; \
errMsg(osERR_MSG.str()); \
} while (false)int main() {
if (1) ERR_MSG("A number: " << 24 << ", a char: " << 'c' << ", that's all!");
else return 0;
}
Причина, по которой я сделал такое странное имя osERR_MSG
чтобы избежать как можно большего количества случаев, подобных этому:
int osERR_MSG = 7;
ERR_MSG(osERR_MSG);
#include <sstream>
#include <iostream>
#define ERR_MSG(inputs) errMsg(std::ostringstream().flush()<<inputs)
int errMsg(std::ostream& os)
{
std::ostringstream& oss(static_cast<std::ostringstream&>(os));
const std::string& str(oss.str());
std::cout << "\nERROR: " << str << "\n\n";
return str.length();
}
int main()
{
int i = ERR_MSG("A number: " << 24 << ", a char: " << 'c' << ", that's all!");
std::cout << i << "\n";
}
Я бы не стал создавать std::ostringstream
а есть функция, вызываемая из деструктора класса, полученного из std::ostream
, Вот пример такого подхода:
#include <sstream>
#include <iostream>
void someFunction(std::string const& value)
{
std::cout << "someFunction(" << value << ")\n";
}
void method(std::string const& value)
{
std::cout << "method(" << value << ")\n";
}
class FunctionStream
: private virtual std::stringbuf
, public std::ostream
{
public:
FunctionStream()
: std::ostream(this)
, d_function(&method)
{
}
FunctionStream(void (*function)(std::string const&))
: std::ostream(this)
, d_function(function)
{
}
~FunctionStream()
{
this->d_function(this->str());
}
private:
void (*d_function)(std::string const&);
};
int main(int ac, char* av[])
{
FunctionStream() << "Hello, world: " << ac;
FunctionStream(&someFunction) << "Goodbye, world: " << ac;
}
Пример использования не использует макрос, но его можно легко обернуть вокруг вышеупомянутого использования FunctionStream()
, Обратите внимание, что в макросе вы, вероятно, хотите убедиться, что тип, видимый пользователем макроса, имеет тип std::ostream&
а не временный тип, поэтому он может быть использован непосредственно с определенными пользователем операторами вывода. Для этого у вас должна быть вставка для одного из типов, непосредственно поддерживаемых std::ostream
который не имеет никакого эффекта, но возвращает std::ostream&
, например:
#define SomeMacro(output) FunctionStream(&someFunction) << "" << output
Подтверждение ответа Николя как лучшего на данный момент:
Ваша текущая проблема заключается в том, что все operator<<
функции возвращают ostream&
не ostringstream&
, Вы можете решить это с простой актерский состав:
#define ERR_MSG(inputs) errMsg((static_cast<std::ostringstream&>(std::ostringstream().flush() << inputs)).str())
Конечно, это все еще имеет проблему (как и все ответы здесь), что-то вроде
ERR_MSG(x ? "x is true" : "x is false")
будет плохо себя вести странным и запутанным образом.