2

这让我感到困扰:我有一个类层次结构,其中包含一组强制或非强制属性。这些类被序列化为 XML(或其他)。必须的属性应该一直序列化,那些不是的,只有在我指定它们时才应该序列化。

我正在寻找一种方法,可以尽可能地自动化这个过程,而不必专门检查每个属性是否设置了值。

这是一个例子:

 class Book
 {

     QString m_author;           // this is optional
     QString m_secondaryAuthor;  // this is optional
     QString m_title;            // this is mandatory

 public: 
     QString serializePlainText()
     {
         QString result = "Book:";
         result += m_title;
         if(m_author.length()) result+= " by "+m_author;
         if(m_SecondaryAuthor.length()) result+= " and "+m_SecondaryAuthor;
         return result;
     }

     void setTitle(const QString& title)
     {
         m_title = title;
     }

     void setAuthor(const QString& author)
     {
         m_author = author;
     }
 };

当然,这只是一个简单的例子,现实生活要复杂得多,类可以有数百个属性。这里是问题的第二部分:我需要能够以更严格的方式设置属性,即。不使用映射到字符串属性值的映射,但或多或​​少类似于上面的示例(使用setter函数setTitle(const QString&)......等等......

考虑到这样做的任何奇特方式:

  1. 无需手动声明和检查所有属性
  2. 有一个漂亮的和程序员友好的 API?

我或多或少地坚持使用 Qt,但这不是强制性的。

谢谢,f。

4

2 回答 2

1

如果你愿意使用预处理器为你生成代码,你可以依赖Boost.Preprocessor。例如,您可以按照以下方式做一些事情:

#define GET_TYPE( pair ) BOOST_PP_TUPLE_ELEM( 2, 0, pair )
#define GET_NAME( pair ) BOOST_PP_TUPLE_ELEM( 2, 1, pair )

#define DECLARE_MANDATORY( r, data, elem ) GET_TYPE( elem ) GET_NAME( elem );

#define DECLARE_MANDATORY_ATTRIBUTES( attributes ) \
    BOOST_PP_SEQ_FOR_EACH( DECLARE_MANDATORY, ~, attributes )

#define DECLARE_OPTIONAL( r, data, elem ) boost::optional< GET_TYPE( elem ) > GET_NAME( elem );

#define DECLARE_OPTIONAL_ATTRIBUTES( attributes ) \
    BOOST_PP_SEQ_FOR_EACH( DECLARE_OPTIONAL, ~, attributes )

#define MANDATORY_ACCESSORS( r, data, elem ) \
    BOOST_PP_CAT( void set_, GET_NAME( elem ) ) (GET_TYPE( elem ) const & value) { GET_NAME( elem ) = value; } \
    GET_TYPE( elem ) BOOST_PP_CAT( get_, GET_NAME( elem ) ) () const { return GET_NAME( elem ); }

#define DEFINE_MANDATORY_ACCESSORS( attributes ) \
    BOOST_PP_SEQ_FOR_EACH( MANDATORY_ACCESSORS, ~, attributes )

#define OPTIONAL_ACCESSORS( r, data, elem ) \
    BOOST_PP_CAT( void set_, GET_NAME( elem ) ) (GET_TYPE( elem ) const & value) { GET_NAME( elem ).reset(value); } \
    GET_TYPE( elem ) BOOST_PP_CAT( get_, GET_NAME( elem ) ) () const { return *GET_NAME( elem ); }

#define DEFINE_OPTIONAL_ACCESSORS( attributes ) \
    BOOST_PP_SEQ_FOR_EACH( OPTIONAL_ACCESSORS, ~, attributes )

class Book
{
    #define BOOK_MANDATORY_ATTRIBUTES ((std::string, title))
    #define BOOK_OPTIONAL_ATTRIBUTES ((std::string, author)) ((std::string, secondaryAuthor))

  public:

    DECLARE_MANDATORY_ATTRIBUTES( BOOK_MANDATORY_ATTRIBUTES )
    DECLARE_OPTIONAL_ATTRIBUTES( BOOK_OPTIONAL_ATTRIBUTES )

    DEFINE_MANDATORY_ACCESSORS( BOOK_MANDATORY_ATTRIBUTES )
    DEFINE_OPTIONAL_ACCESSORS( BOOK_OPTIONAL_ATTRIBUTES )
};

int main()
{
    Book b;
    b.set_title("H2G2");

    std::cout << b.get_title();

    b.set_author("Douglas Adams");
    std::cout << b.get_author();
}

您可以创建另一个宏来为您的序列化函数生成代码,利用 [ boost::optional](http://www.boost.org/libs/optional] 来测试是否设置了可选属性。

如果您不想进行代码生成,您将无法自动获取属性的访问器。但是,序列化方面可以更经典地处理,例如通过为所有属性使用公共基类。这样,您可以将它们存储在容器中,并遍历它们以要求它们序列化自己:

struct Attribute
{
    virtual QString serializePlainText() = 0;
};

// Generic attribute for the most common cases
template <typename T>
class MandatoryAttribute : public Attribute
{
    T value;

  public:

    std::string serializePlainText() 
    { 
        return boost::lexical_cast<std::string>(value);
    }
};

// Generic attribute for the most common cases
template <typename T>
class OptionalAttribute : public Attribute
{
    boost::optional<T> value;

  public:

    bool isSet() const { return value; }

    std::string serializePlainText() 
    { 
        return isSet() ? 
            boost::lexical_cast<std::string>(value) :
            "";
    }
};
于 2012-06-13T15:08:00.413 回答
0

所以。我的第一个想法是“使用一种不错的动态语言”,但我想这不是一个选择。不过,我确实有一些模糊的建议。

  1. 使用地图。地图可以被迭代,使你的序列化代码大大减少痛苦。它还使添加新属性变得更加容易。
  2. 使用单个 getter 和单个 setter 函数,因为这是客户端代码调用的最简单、最简洁的 API。
  3. 分别保存已知属性名称和元数据值的映射。

调用 set 后,可以在属性元数据表中查找请求的属性名称。然后,您可以执行任何您想要的验证或转换。我假设您不需要在这里为每个属性的结构设置一组独特而复杂的要求,当然......大多数属性可能只需要最小或最大长度之类的东西?

在序列化时,您可以遍历属性元数据表,查找所有必需属性的名称,并在属性值表中查找它们。之后,遍历您的属性值表,并序列化任何剩余的项目。

于 2012-06-13T14:17:31.993 回答