Any way to generate duplicate operators << definitions?

I have dozens of these types of structures, and I hope there is a smart way to generate the <<operator using macros or metaprogramming templates. Also note that endianess also takes into account and makes it a little more complex ... Looking for a solution that will be at least as functional as the one described.

#define SEP '|'

struct MyStruct {
   char c;
   char s[10];
   uint32_t i;
   unsigned short us;

   friend ostream& operator<<(ostream& os, HeartbeatMessage& r) {
      return os \
         << "c=" << c << SEP
         << "s=" << s << SEP
         << "i=" << bswap_32(i) << SEP
         << "us=" << bswap_16(us) << SEP
}
+3
source share
4 answers

, . ++ , brew .

Boost.Fusion, BOOST_FUSION_ADAPT_STRUCT [], , Fusion for_each .

, , , , . , , , operator<<, , , ( , , ).

+1

( ), Boost, :

//////////////////////////////////////////////////////////////////////////
DEF_PACKET_STRUCT(name, members)

Example:

DEF_PACKET_STRUCT(
    Test_Struct,
    ((float) (f) (0.4f))
    ((std::string) (str))
);

defines Test_Struct with 2 members:
float f;
std::string str;

generates def ctor:
Test_Struct(): f(0.4f) {}

generates serialization and operator<<(std::ostream&...)

//////////////////////////////////////////////////////////////////////////
DEF_DERIVED_PACKET_STRUCT(name, bases, members)
the same as above + derivation from given bases

Example: 
DEF_DERIVED_PACKET_STRUCT(Test_Struct, 
    (Base1)(Base2), 
    ((std::string) (str_multi_derived) ("multi_derived"))
)

Note that even if it should be derived from single base, it should be specified in (), e.g. (Base1)

endianess ,

+1

, .

, , , POD?

POD, , ++ ( , POD). operator<<; . , ?

, , Boost, .

, , , .

, ( , , head)

#include <iostream>
#include <string>

#define bswap_32( x )   x
#define bswap_16( x )   x
typedef unsigned    uint32_t;

char const  sep     = '|';

template< class Type >
inline void write( Type const& v, std::ostream& stream )
{
    stream << v;
}

template<>
inline void write( uint32_t const& v, std::ostream& stream )
{
    stream << bswap_32( v );
}

template<>
inline void write( unsigned short const& v, std::ostream& stream )
{
    stream << bswap_16( v );
}

template< class Type >
inline void write( char const legend[], Type const& v, std::ostream& stream )
{
    stream << legend;  write( v, stream );  stream << '|';
}

#define IMPLEMENT_OUTPUT_1(                     \
    name1                                       \
    )                                           \
    write( #name1 "=", r.name1, os );

#define IMPLEMENT_OUTPUT_2(                     \
    name1, name2                                \
    )                                           \
    IMPLEMENT_OUTPUT_1( name1 )                 \
    write( #name2 "=", r.name2, os );

#define IMPLEMENT_OUTPUT_3(                     \
    name1, name2, name3                         \
    )                                           \
    IMPLEMENT_OUTPUT_2( name1, name2 )          \
    write( #name3 "=", r.name3, os );

#define IMPLEMENT_OUTPUT_4(                     \
    name1, name2, name3, name4                  \
    )                                           \
    IMPLEMENT_OUTPUT_3( name1, name2, name3 )   \
    write( #name4 "=", r.name4, os );


struct MyStruct
{
   char c;
   char s[10];
   uint32_t i;
   unsigned short us;

//    friend std::ostream& operator<<( std::ostream& os, MyStruct const& r )
//    {
//       return os
//          << "c=" << r.c << sep
//          << "s=" << r.s << sep
//          << "i=" << bswap_32( r.i ) << sep
//          << "us=" << bswap_16( r.us ) << sep;
//     }

    friend std::ostream& operator<<( std::ostream& os, MyStruct const& r )
    {
        IMPLEMENT_OUTPUT_4( c, s, i, us )
        return os;
    }
};

int main()
{
    using namespace std;
    MyStruct const  o   = { 'A', "Bbbbbbb", 3, 4 };

    cout << o << endl;
}

, ​​. , Boost'ing, POD. .

, , , ,

0

There is an alternative, but it is becoming ugly.
One solution is to create a base base class, such as Object(in other languages).
The next step is to create containers of pointers to Object.
And finally, at some point, write a method that applies operator<<to each object in the container (via pointers).

Otherwise, I move on to something like this:

struct Annotation_Interface
{
    virtual std::string annotate(const std::string& indentation = "") = 0;
};

class MyStruct : Annotation_Interface
{
   char c;
   char s[10];
   uint32_t i;
   unsigned short us;

  public:
    std::string annotate(const std::string& indentation)
    {
        std::ostringstream output;
        output << "c=" << c << SEP
         << "s=" << s << SEP
         << "i=" << bswap_32(i) << SEP
         << "us=" << bswap_16(us) << SEP;
        return output.str();
     }
};
0
source

Source: https://habr.com/ru/post/1786719/


All Articles