3

我知道这个问题可能很模糊,它是对我在这里给出的答案的扩展。基本上,作为一个个人项目,我一直在尝试复制“代码优先”的习语,这在 C# 程序员中很流行做数据库工作,但在 C++ 中。一切都很棒,而且运作良好。

C# 的一大优点是诸如属性之类的东西。我们在 C++ 中没有这种新颖性,所以我在数据库中构建的特定类的代码如下所示:

class User : public Record {
public:
    User(Model& m) : Record(L"_user", m)
                   , Username(L"_userName", 32, false)
                   , Nickname(L"_nickName", 32, false) {
        field(Username);
        field(Nickname);
    };  // eo ctor

    Field<String> Username;
    Field<String> Nickname;
};  // eo class User

理想情况下,我想摆脱field构造函数中的调用。本质上,他们所做的只是向基类注册该字段,然后由基类完成真正的工作。我怎样才能完成这种事情,并将详细信息传递给基类,就像我在构造函数中指定的那样简单?

我回答的上一个问题中,由于 C++ 构造事物的方式,我能够提供基类信息。有没有办法可以在构造函数中利用它并摆脱field可能引入程序员错误的讨厌调用?

在这一点上,我很高兴一切都运行良好,但我很想减少混乱并消除构造函数中这种“注册”过程的需要,并以某种方式让它们自动注册到基类中。

欢迎所有意见:)

编辑

的实现field是这样的:

        void Record::field(detail::field_base& field) {
            field.owner_ = this;
            fields_.push_back(&field);
        };  // eo field**
4

3 回答 3

4

您可以让构造函数Field<>接受一个指向 a 的附加指针,并在构造函数的主体中Record调用该指针。field(*this)

template<typename T>
struct Field // Just guessing...
{
    Field(std::string s, int i, bool b, Record* pRecord) 
    // ...
    { 
        pRecord->field(*this); 
    }
    // ...
};

然后,在您的初始化列表中,User您将通过这种方式this作为附加Record指针传递:

User(Model& m) : Record(L"_user", m)
               , Username(L"_userName", 32, false, this)
               , Nickname(L"_nickName", 32, false, this)
{
};  // eo ctor
于 2013-03-14T00:12:15.730 回答
2

只需将必要的信息(即您的this指针)传递给Field构造函数,然后让它完成工作

你甚至可以为此引入一个特殊的包装类

于 2013-03-14T00:11:21.450 回答
2

不要沉迷于属性语法。

您想要的是能够将structs 视为具有运行时反射的有意义命名数据的聚合,对吗?

要引入新名称,请使用类型:

template<typename T> struct FieldTag {};
struct Username:FieldTag<Username> {} username;
struct Nickname:FieldTag<Nickname> {} nickname;

要生成类型的聚合,请使用模板参数:

template<typename Child, typename Field>
struct FieldHandler {
  typedef typename Field::tag_type tag_type;
  typedef typename Field::value_type value_type;
  PsuedoRef<value_type> operator[]( tag_type ) { /* details */ }
  /* details */
};
template<typename... Fields>
struct Aggregate:
  Record,
  FieldHandler<Aggregate<Fields...>, Fields>...
{
  /* details */
};
template<typename Value, typename Tag>
struct Field {
  typedef Tag tag_type;
  typedef Value value_type;
};

上述所有样板的目标,加上一些运算符覆盖滥用,是我们可以从中获得一些漂亮的语法,最终程序员得到一个看起来很像没有样板的 C++ 的语法。

以下是如何创建Data具有两个字段的类型的最终目标:

struct Data:
  Aggregate<
    Field<std::string, Username>,
    Field<std::string, Nickname>
  >
{
  Data(): Aggregate(
    username|L"username" = L"Unknown Name",
    nickname|L"nickname" = L"Unknown NickName"
  {}
};

要访问字段,请使用适当覆盖的运算符:(我喜欢^,它绑定紧密,看起来有点像->

Data d;
d^username= L"Bob";
d^nickname= L"Apples";

这在 C++11 中都是可能的,但并不是那么容易。

现在,将指向函数的指针作为模板参数的能力,以及可转换为函数的无状态 lambda,可能让我们将字符串存储在字段的类型中,从而无需在构造时传递它。另一方面,对未评估上下文的限制使这似乎值得怀疑。

所以剩下的就是丑陋的一点。另外,完成上述工作所需的努力使我认为您可能应该吞下每个字段上烦人的样板。

请注意,上面的代码只是伪代码,是您可以采取的路线的草图,并不是所有接近完成的地方。我建议不要尝试走这条路,除非你想迷失在学习 C++ 的模板元编程子语言的树林中,并且永远无法解决你的问题。

话说回来,学习模板元编程不是比你当前的项目更有趣吗?;)

于 2013-03-14T00:27:25.843 回答