我知道这个问题可能很模糊,而且这是对我给出的答案here的延伸。基本上,作为一个个人项目,我一直在尝试复制“代码优先”的习惯用法,这在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
调用。实际上,他们所做的就是用基类注册该字段,然后基类进行实际工作。我怎么能完成这种事情,并将细节传递给基类,就像我在构造函数中指定的一样?
在上一个I answered问题中,由于C ++构造事物的方式,我能够提供基类信息。有没有办法可以在构造函数中利用它并摆脱可能引入程序员错误的令人讨厌的field
调用?
此时我很高兴一切都运行得非常好,但我希望减少混乱,并且在构造函数中删除了对这个“注册”过程的需要,并以某种方式让它们自动注册到基类。< / p>
欢迎所有意见:)
修改
field
的实施因此是:
void Record::field(detail::field_base& field) {
field.owner_ = this;
fields_.push_back(&field);
}; // eo field**
答案 0 :(得分:4)
您可以让Field<>
的构造函数接受指向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
答案 1 :(得分:2)
只需将必要信息(即this
指针)传递给Field
构造函数,然后让它完成工作
你甚至可以为这个
引入一个特殊的包装类答案 2 :(得分:2)
不要挂断属性语法。
您想要的是能够将struct
视为具有运行时反射的有意义命名数据的聚合,是否正确?
要引入新名称,请使用以下类型:
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 ++的模板元编程子语言的过程中迷失方向,而且永远不会到处理你的问题。
然后,再次,学习模板元编程不会比你当前的项目更有趣吗? ;)