我有几十种这些类型的结构,我希望有一种聪明的方法来生成运算符<<使用宏或模板元编程的方法。还请注意,endianess也是考虑因素并使其更加棘手......寻找一种至少与所概述的一样高效的解决方案。
#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
}
答案 0 :(得分:1)
正如评论中所指出的,这对于反思来说会很好。由于C ++没有内置反射,你可以做的最好的事情是使用一些库或家庭酿造代码来模拟反射并以通用方式实现打印。
我建议您使用Boost.Fusion,特别是BOOST_FUSION_ADAPT_STRUCT
[Link],使您的类型可以作为融合序列使用,然后使用Fusion的for_each来定义打印。
如果你有多个方法在成员上运行,那么基本上重复结构的反射仿真定义所需的增加的工作将很快得到回报。但是,如果您真正拥有的只是单operator<<
,则可能不值得(或者您决定,至少您可以在中心位置定义所有打印和格式化)。
答案 1 :(得分: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功能可以与此示例中的成员默认值相同的方式实现
答案 2 :(得分:0)
据推测,你的结构中的成员名称和类型是不同的,否则没问题。
大概你需要你的结构是POD吗?
在这种情况下,不同的POD结构,主要的限制是模板不能处理普通的C ++标识符(这是你必须拥有的POD结构中的成员)。那么你需要一个宏来定义operator<<
;只有宏才能处理标识符。然后主要的问题是,如何将可变数量的参数传递给宏?
好吧,有一些方法可以传递可变数量的参数并使用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结构的。所以我可能值得一看。
干杯&amp;对不起,这可能没什么帮助,
答案 3 :(得分:0)
还有另一种选择,但它很难看
一种解决方案是创建通用基类,例如Object
(在其他语言中)
下一步是创建一个指向Object
的指针容器
最后在某个时候编写一个方法,将operator<<
应用于容器中的每个对象(通过指针)。
否则,我会这样做:
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();
}
};