Write a Macro to stringify the contents of a struc

2019-09-18 23:43发布

问题:

I am trying to write a macro to expand the contents of a struct:

struct Str
{
    int a;
    float f;
    char *c;
};
Str s = {123, 456.789f, "AString"};

#define STRINGIFY_STR(x) ... // Macro to stringify x which is an instance of Str

printf("%s", STRINGIFY_STR(s));

desired output: [a: 123, f:456.789, c:AString]

Is it possible to write a macro that does this? If it is, then how?

回答1:

Is there a reason you want to do this as a macro?

You should write a function to perform this action instead of using the preprocessor.


Depending on your goals, there are a few options. The boost formatting library provides a great toolkit to build the formatted string. You could always overload operator<< to provide clean output, as well.

If you're doing this in pure C, the sprintf family of methods work for creating formatted output.



回答2:

This is quite gross, and only works on gcc/g++.

#define STRINGIFY_STR(x) \
    std::string(({ std::ostringstream ss; \
                   ss << "[a: " << x.a \
                      << ", f: " << x.f \
                      << ", c: " << x.c << "]"; \
                   ss.str(); })).c_str()

You have to create the string from the values. Please don't do it this way. Please follow Reed's advice.

Here is how I would modify your struct to allow it to be pretty printed:

struct Str
{
    int a;
    float f;
    char *c;

    std::ostream & dump (std::ostream &os) const {
        return os << "[a: " << a
                  << ", f: " << f
                  << ", c: " << c << "]";
    }
};

std::ostream & operator << (std::ostream &os, const Str &s) {
    return s.dump(os);
}

Str s = {123, 456.789f, "AString"};

Now, to print out s, you can use std::cout:

std::cout << s << std::endl;

Or, if you really want a string:

std::stringstream ss;
s.dump(ss);
puts(ss.str().c_str());


回答3:

struct stringify
{
    operator std::string() const
    {
        return str ;
    }
    std::string str ;
};

template < typename T > stringify operator+ ( stringify s, const T& object )
{
    std::ostringstream stm ;
    stm << object ;
    if( !s.str.empty() && !std::isspace( *s.str.rbegin() ) ) s.str += ' ' ;
    s.str += stm.str() ;
    return s ;
}


回答4:

I'm also against the approach, but to answer the question:

#define STRINGIFY_STR(x) \
   (std::string("[a: ") + std::to_string((long double)x.a) \
  + std::string(", f:") + std::to_string((long double)x.f) \
  + std::string(",c: ") + x.c + std::string("]") ).c_str()

I suggest writing a member function that does this though.