如何设计具有“注释”的类,字段? [英] How to design a class with "annotated" fields?

查看:133
本文介绍了如何设计具有“注释”的类,字段?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

想象一下,我们有一些协议,有几百种消息类型,每一种都需要用C ++类来建模。因为每个类都应该能够自动处理每个字段,一个自然的解决方案是只需要一个 std :: tuple 和所有必需的类型:

  std :: tuple< int,double,char>信息; 

print(message); //通常的变化魔术

这是很好的。然而,现在我想给每个字段一个名称,我想要能够使用该名称时引用我的代码中的字段,以及获得它的文本表示。 Nive,或在C中,我可能写成:

  struct Message 
{
int header;
double temperature;
char flag;
};

这样我们就失去了元组的递归自动处理能力,但我们可以逐字地命名每个字段。在C ++中,我们可以通过枚举来实现:

  struct Message 
{
{headers,temperature,flag};
static const char * FieldNames [] = {header,temperature,flag};

typedef std :: tuple< int,double,char> tuple_type;

template< FieldID I>
typename std :: tuple_element< I,tuple_type> :: type& get()
{return std :: get< I>(data); }

template< FieldID I>
static const char * name(){return FieldNames [I]; }

tuple_type data;
};

现在我可以说, Message m; m.get< Message :: header>()= 12; 等等,我可以递归的字段,使每个打印出自己的值前缀的自己的名字等。 >




现在问题:如何高效地编写此类代码, em>



理想情况下,我想要这样说:

  START_MESSAGE(Message)
ADDFIELD(int,header)
ADDFIELD(double,temperature)
ADDFIELD(char,flag)
END_MESSAGE

有没有办法,结合预处理器,Boost和C ++ 11,实现这样的东西,而不需要外部生成工具? (我认为Boost.Preprocessor调用这个水平和垂直重复,我需要转置字段数据不知何故)。这里的关键特性是,我从来不必重复任何信息,修改或添加

解决方案

您可以使用boost的预处理序列执行此操作。

  #define CREATE_MESSAGE(NAME,SEQ)... 

CREATE_MESSAGE(SomeMessage,
b $ b(double)(temperature)
(char)(flag)

您需要迭代每个对来生成定义。我没有任何示例代码方便,虽然我可以安排一些,如果它是有趣的。



有一点,我有一个类似这样的生成器也生成所有字段的序列化。我觉得这有点太远了。我觉得像具体的定义和声明访问者在字段上更直接。这是一个少一点魔法,以防其他人不得不维护我的代码。我不知道你的情况很明显,只是实施后,我还有保留。 :)



使用C ++ 11的功能再次看看会很酷,虽然我没有机会。



更新:



仍然有几个扭结,但这主要是工作。

  #include< boost / preprocessor.hpp> 
#include< boost / preprocessor / seq / for_each_i.hpp>
#include< boost / preprocessor / arithmetic / mod.hpp>
#include< boost / preprocessor / control / if.hpp>

#include< tuple>

#define PRIV_CR_FIELDS(r,data,i,elem)\
BOOST_PP_IF(BOOST_PP_MOD(i,2),elem BOOST_PP_COMMA,BOOST_PP_EMPTY)()

#define PRIV_CR_STRINGS(r,data,i,elem)\
BOOST_PP_IF(BOOST_PP_MOD(i,2),BOOST_PP_STRINGIZE(elem)BOOST_PP_COMMA,BOOST_P

#define PRIV_CR_TYPES i,elem)\
BOOST_PP_IF(BOOST_PP_MOD(i,2),BOOST_PP_EMPTY,elem BOOST_PP_COMMA)()

#define CREATE_MESSAGE(NAME,SEQ)\
struct NAME {\
enum FieldID {\
BOOST_PP_SEQ_FOR_EACH_I(PRIV_CR_FIELDS,_,SEQ)\
}; \
std :: tuple< \
BOOST_PP_SEQ_FOR_EACH_I (PRIV_CR_TYPES,_,SEQ)\
> data; \
template< FieldID I> \
auto get() - > decltype(std :: get< I> ;(data)){\
return std :: get< I>(data); \
} \
template< FieldID I> \
static const char * name(){\
static constexpr char * FieldNames [] = {\
BOOST_PP_SEQ_FOR_EACH_I(PRIV_CR_STRINGS,_,SEQ)\
}; \
return FieldNames [I]; \
} \
};

CREATE_MESSAGE(foo,
(int)(a)
(float)(b)


#undef CREATE_MESSAGE

int main(int argc,char ** argv){

foo f;
f.get< foo :: a>()= 12;

return 0;
}

这是get的decltype有问题。我没有真正使用元组知道什么期望。我不认为它与如何生成类型或字段有任何关系。



这里是预处理器使用-E生成的:

  struct foo {
enum FieldID {a,b,};
std :: tuple< int,float,>数据;
template< FieldID I>
auto get() - > decltype(std :: get< I>(data)){
return std :: get< I&
}
template< FieldID I> static const char * name(){
static constexpr char * FieldNames [] = {a,b,};
return FieldNames [I];
}
};


Imagine we have some sort of protocol with hundreds of message types, each of which we want to model by a C++ class. Since each class should be able to process each field automatically, a natural solution is to just have an std::tuple with all the required types:

std::tuple<int, double, char> message;

print(message);   // the usual variadic magic

This is all fine and well. However, now I want to give each field a name, and I want to be able to use the name when referring to the field in my code, as well as get a textual representation of it. Naively, or in C, I might have written:

struct Message
{
    int    header;
    double temperature;
    char   flag;
};

That way we lose the recursive automagic processing power of the tuple, but we can name each field literally. In C++, we can do both by means of an enum:

struct Message
{
    enum FieldID { header, temperature, flag };
    static const char * FieldNames[] = { "header", "temperature", "flag" };

    typedef std::tuple<int, double, char> tuple_type;

    template <FieldID I>
    typename std::tuple_element<I, tuple_type>::type & get()
    { return std::get<I>(data); }

    template <FieldID I>
    static const char * name() { return FieldNames[I]; }

    tuple_type data;
};

Now I can say, Message m; m.get<Message::header>() = 12; etc., and I can recurse over the fields and make each print out their own value prefixed by their own name, etc.


Now the question: How can I author such code efficiently, without repetition?

Ideally, I want to be able to say this:

START_MESSAGE(Message)
ADDFIELD(int, header)
ADDFIELD(double, temperature)
ADDFIELD(char, flag)
END_MESSAGE

Is there any way, combining preprocessor, Boost and C++11, to achieve something like this without the need for external generation tools? (I think Boost.Preprocessor calls this "horizontal" and "vertical" repetition. I need to "transpose" the field data somehow.) The key feature here is that I never have to repeat any of the information, and that modifying or adding one field only requires one single change.

解决方案

You can do this with boost's preprocessor sequences.

#define CREATE_MESSAGE(NAME, SEQ) ...

CREATE_MESSAGE(SomeMessage,
  (int)(header)
  (double)(temperature)
  (char)(flag)
)

You would need to iterate over each pair to generate the definitions. I don't have any example code handy, though I can probably arrange some if it is interesting.

At one point I had a generator for something like this that also generated all the serialization for the fields. I kind of felt like it went a little too far. I feel like concrete definitions and declarative visitors on the fields is more straight forward. It's a little less magical in case someone else had to maintain the code after me. I don't know you're situation obviously, just after implementing it I still had reservations. :)

It would be cool to look at again with the C++11 features, though I haven't had a chance.

Update:

There are still a few kinks to work out, but this is mostly working.

#include <boost/preprocessor.hpp>
#include <boost/preprocessor/seq/for_each_i.hpp>
#include <boost/preprocessor/arithmetic/mod.hpp>
#include <boost/preprocessor/control/if.hpp>

#include <tuple>

#define PRIV_CR_FIELDS(r, data, i, elem) \
    BOOST_PP_IF(BOOST_PP_MOD(i, 2),elem BOOST_PP_COMMA,BOOST_PP_EMPTY)()

#define PRIV_CR_STRINGS(r, data, i, elem) \
    BOOST_PP_IF(BOOST_PP_MOD(i, 2),BOOST_PP_STRINGIZE(elem) BOOST_PP_COMMA,BOOST_P

#define PRIV_CR_TYPES(r, data, i, elem) \
    BOOST_PP_IF(BOOST_PP_MOD(i, 2),BOOST_PP_EMPTY,elem BOOST_PP_COMMA)()

#define CREATE_MESSAGE(NAME, SEQ) \
    struct NAME { \
        enum FieldID { \
            BOOST_PP_SEQ_FOR_EACH_I(PRIV_CR_FIELDS, _, SEQ) \
        }; \
        std::tuple< \
            BOOST_PP_SEQ_FOR_EACH_I(PRIV_CR_TYPES, _, SEQ) \
        > data;\
        template <FieldID I> \
            auto get() -> decltype(std::get<I>(data)) { \
                return std::get<I>(data); \
            } \
        template <FieldID I> \
            static const char * name() { \
                static constexpr char *FieldNames[] = { \
                    BOOST_PP_SEQ_FOR_EACH_I(PRIV_CR_STRINGS, _, SEQ) \
                }; \
                return FieldNames[I]; \
            } \
    };

CREATE_MESSAGE(foo,
        (int)(a)
        (float)(b)
    )

#undef CREATE_MESSAGE

int main(int argc, char ** argv) {

    foo f;
    f.get<foo::a>() = 12;

    return 0;
}

It is having problems with get's decltype. I haven't really used tuple to know what to expect there. I don't think it has anything to do with how you generate the types or fields, though.

Here is what the preprocessor is producing with -E:

struct foo { 
  enum FieldID { a , b , }; 
  std::tuple< int , float , > data;
  template <FieldID I> 
    auto get() -> decltype(std::get<I>(data)) { 
      return std::get<I>(data); 
  } 
  template <FieldID I> static const char * name() { 
    static constexpr char *FieldNames[] = { "a" , "b" , }; 
    return FieldNames[I]; 
  } 
};

这篇关于如何设计具有“注释”的类,字段?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆