Question

I want to write a macro that takes as its only argument a list of std::ostream& operator<< concatenated objects and passes the consolidated string as a single std::string object to a function. The ability to pass the consolidated string to a function is key; in the example below I am aware that the example itself could be rewritten to work simply by defining the macro to ERR_MSG(inputs) std::cout << "ERROR: " << inputs, but sending the output to std::cout is not the goal, it's just the test objective I chose for the example.

I'm using GCC 4.1.2 (Red Hat 4.1.2-52) and upgrading it is not an option. Here's a very boiled-down version of what I've tried:

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

Macro #1 compiles, but of course prints nothing but "" for the message. Neither macros 2 & 3 compile, with the following errors:

#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&)

I am not interested in how I could rewrite this without macros; I can do that quite easily myself.

=== UPDATE: === I forgot to mention that in its real use case, the function called by the macro returns an object that may be used by the caller of the macro. That invalidates any macro implementations that cannot be implemented in a single expression whose result is the returned type of the function called by the macro. The "do nothing" implementation of the macro (for release builds) will simply pass an empty std::string to the function regardless of what the "inputs" are. Sorry for not mentioning that earlier.

Was it helpful?

Solution

Your current problem is that all of the various operator<< functions return an ostream&, not an ostringstream&. You can solve that with a simple cast:

#define ERR_MSG(inputs) errMsg((static_cast<std::ostringstream&>(std::ostringstream().flush() << inputs)).str())

The flush is needed because std::ostringstream() is a temporary. Therefore, you can't call functions on it that take an lvalue reference (ie: std::ostream&). Functions like most operator<< variants. All the flush call does is return the this pointer as an lvalue reference.

OTHER TIPS

If you are willing to use some GCC extension, you could declare an actual ostringstream inside the macro in a block, so that the .str() method can be used without casting:

#define ERR_MSG(inputs) \
    do { std::ostringstream _s_; _s_<<inputs;errMsg(_s_.str()); } while(false)

Demo: http://ideone.com/clone/y56lc

Use do { } while (false) idiom to make a few lines macro.

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

The reason I made such strange name osERR_MSG is to avoid as much as possible cases like this:

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";
}

I'd not create an std::ostringstream but rather have a function called from the destructor of a class derived from std::ostream. Here is an example of this approach:

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

The example use doesn't use a macro but this can be wrapped easily around the above use of FunctionStream(). Note, that in a macro you probably want to make sure that the type seen by the user of the macro is of type std::ostream& rather than a temporary type so it can be used directly with user defined output operators. To this end you should have an insertion for one of the types directly supported by std::ostream which doesn't have any effect but returns an std::ostream&, for example:

#define SomeMacro(output) FunctionStream(&someFunction) << "" << output

Reinstating Nicol's answer as its the best so far:

Your current problem is that all of the various operator<< functions return an ostream&, not an ostringstream&. You can solve that with a simple cast:

#define ERR_MSG(inputs) errMsg((static_cast<std::ostringstream&>(std::ostringstream().flush() << inputs)).str())

Of course, this still has the problem (like all the answers here) that something like

ERR_MSG(x ? "x is true" : "x is false")

will misbehave in an odd and confusing manner.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top