【问题标题】:Any way to generate repetitive operator<< definitions?有什么方法可以生成重复的 operator<< 定义?
【发布时间】:2011-06-12 14:30:56
【问题描述】:

我有几十种这些类型的结构,我希望有一种巧妙的方法可以使用宏或模板元编程生成 operator

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

【问题讨论】:

  • 您是否考虑过模板选项?
  • 你所有的结构都有四个成员吗?
  • 所有成员都是公开的,为什么要使用朋友?货物崇拜?
  • 以前有人问过这个问题,因为 C++ 没有反射,您需要手动编写它们,或者以某种方式根据结构描述自动生成代码(通过您最喜欢的脚本语言)。

标签: c++ templates macros metaprogramming c-preprocessor


【解决方案1】:

可能你的结构中的成员名称和类型不同,否则没问题。

大概你需要你的结构是 POD 吗?

在这种情况下,不同的 POD 结构,主要限制是模板不能处理普通的 C++ 标识符(这是您必须为 POD 结构中的成员所拥有的)。所以你需要一个宏来定义operator&lt;&lt;;只有宏可以处理标识符。然后主要的问题是,如何将可变数量的参数传递给宏?

嗯,有一些方法可以传递可变数量的参数并对其进行迭代,使用 Boost 库的宏支持,但这很复杂。

无论如何,您都必须提供数据成员名称,因此它不会比您已有的代码更简洁

记住这一点,并避免使用 Boosted 可变参数宏,它可能看起来像这样(我不会使用它,但如果你喜欢它,你也可以考虑为函数头声明定义一个宏) :

#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 结构,它是最接近的。所以我可能值得一看。

干杯和抱歉,这可能没有帮助,

【讨论】:

    【解决方案2】:

    正如 cmets 中所指出的,这与反射相得益彰。由于 C++ 没有内置反射,您可以做的最好的事情是使用一些库或自制代码来模拟反射并以通用方式实现您的打印。

    我建议您使用 Boost.Fusion,特别是 BOOST_FUSION_ADAPT_STRUCT [Link] 使您的类型可用作融合序列,然后使用 Fusion 的 for_each 定义打印。

    如果您有多个方法对成员进行操作,那么基本上重复结构的反射仿真定义所需的增加工作将很快得到回报。但是,如果您真的只有那个 operator&lt;&lt;,那么可能不值得付出努力(或者您决定,至少您可以在一个中心位置定义所有打印和格式)。

    【讨论】:

      【解决方案3】:

      有一个替代方案,但它变得丑陋。
      一种解决方案是创建一个通用基类,例如 Object(在其他语言中)。
      下一步是创建一个指向 Object 的指针容器。
      最后在某个时候编写一个方法,将operator&lt;&lt; 应用于容器中的每个对象(通过指针)。

      否则,我会选择这样的:

      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();
           }
      };
      

      【讨论】:

        【解决方案4】:

        一旦我遇到类似的问题(自定义协议有很多不同的网络数据包),为此使用了 Boost Preprocessor,结果如下所示:

        //////////////////////////////////////////////////////////////////////////
        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)
        

        你的字节序特性可以像本例中的成员默认值一样实现

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2012-06-23
          • 2014-08-14
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-04-06
          • 1970-01-01
          相关资源
          最近更新 更多