我有一个类型的树层次结构,每个都由字符串定义,如下所示:
com
com.example
com.example.shape
com.example.shape.triangle
com.example.shape.triangle.equilateral
com.example.shape.triangle.isosceles
com.example.shape.triangle.right
com.example.shape.quadrilateral
com.example.shape.quadrilateral.rectangle
com.example.shape.quadrilateral.squere
类型使用动态参数定义一些数据,这些数据可以在运行时更改,因此无法创建编译时类型层次结构。因此,每个实体只是一个类型名称(字符串)和参数列表,您始终可以在系统中注册新类型。然而,许多类型是预定义的,并且可以在系统启动时注册。为了对在运行时创建的数据和预定义的数据具有相同的体验,我将这两种动态表示用于此。对于预定义类型,我希望有一种机制可以在编译时验证类型名称,并且我不希望每次必须使用时将字符串直接放在代码中,它可以通过定义字符串const表达式来解决,但它不是很好,像这样:
string some_type = "com.example.type1";
...
registerType(some_type, parameters_definition);
所以我想的是更好的方法。
另一种方法是制作这样的东西:
#include <iostream>
#include <string>
struct Base {
Base(std::string parent_name, std::string my_name) : name_(parent_name + "." + my_name) {}
std::string name_;
};
std::ostream& operator<< (std::ostream& os, const Base& base) {
os << base.name_;
return os;
}
struct G : Base {
G(std::string parent_name, std::string my_name) : Base(parent_name, my_name) {}
};
struct F : Base {
F(std::string parent_name, std::string my_name) : Base(parent_name, my_name) {}
G rectangle{name_, "rectangle"};
G squere{name_, "squere"};
};
struct E : Base {
E(std::string parent_name, std::string my_name) : Base(parent_name, my_name) {}
};
struct D : Base{
D(std::string parent_name, std::string my_name) : Base(parent_name, my_name) {}
E equilateral{name_, "equilateral"};
E isosceles{name_, "isosceles"};
E right{name_, "right"};
};
struct C : Base {
C(std::string parent_name, std::string my_name) : Base(parent_name, my_name) {}
D triangle{name_, "triangle"};
F quadrilateral{name_, "quadrilateral"};
};
struct B : Base{
B(std::string parent_name, std::string my_name) : Base(parent_name, my_name) {}
C shape{name_, "shape"};
};
struct A {
A(std::string my_name) : name_(my_name) {};
std::string name_;
B example{name_, "example"};
};
std::ostream& operator<< (std::ostream& os, const A& a) {
os << a.name_;
return os;
}
int main() {
A com("com");
std::cout << com << std::endl;
std::cout << com.example << std::endl;
std::cout << com.example.shape << std::endl;
std::cout << com.example.shape.triangle << std::endl;
std::cout << com.example.shape.triangle.equilateral << std::endl;
std::cout << com.example.shape.triangle.isosceles << std::endl;
std::cout << com.example.shape.triangle.right << std::endl;
std::cout << com.example.shape.quadrilateral << std::endl;
std::cout << com.example.shape.quadrilateral.rectangle << std::endl;
std::cout << com.example.shape.quadrilateral.squere << std::endl;
return 0;
}
很高兴使用,特别是IDE具有代码提示,但遗憾的是不容易定义。每个不同的树级别都需要使用新成员定义新类,该名称对应于某些字符串。
我正在寻找更好的解决方案 - 更简单。将它定义为某种模板专业化会很棒,但我不知道该怎么做。
欢迎任何建议:) 问候, 皮丘。
答案 0 :(得分:0)
这是一种略有不同的方法。
在这里,我们创建与您的name_token
名称匹配的变量com
。然后,我们将它们与聪明的operator/
一起定位。
首先有一些样板文件,但实际的语法很简洁:
template<class...Ts>
struct or_trait : std::false_type {};
template<class T0, class...Ts>
struct or_trait<T0, Ts...> : std::integral_constant<bool, T0{} || or_trait<Ts...>{} > {};
namespace names {
template<class Tag, bool is_root, class...Parents>
struct name_token {
std::string name;
name_token( std::string in ):name(std::move(in)) {}
template<class Rhs>
constexpr static bool is_valid() {
return or_trait< std::is_same<Parents, Rhs>... >{};
}
};
template<class Tag, class...Parents>
name_token<Tag, false, Parents...> name( Tag, std::string s, Parents const&... ) { return std::move(s); }
template<class Tag, class...Parents>
struct name_token<Tag, true, Parents...> {
std::string name;
name_token( std::string in ):name(std::move(in)) {}
operator std::string() const { return name; }
template<class Rhs>
constexpr static bool is_valid() {
return or_trait< std::is_same<Parents, Rhs>... >{};
}
friend std::ostream& operator<<(std::ostream& os, name_token const& self) {
return os << std::string(self);
}
};
template<class Tag, class...Parents>
name_token<Tag, true, Parents...> rootname( Tag, std::string s, Parents const&... ) { return std::move(s); }
template<class LastToken>
struct name_expression {
std::string current;
operator std::string()const { return current; }
friend std::ostream& operator<<(std::ostream& os, name_expression const& self) {
return os << std::string(self);
}
template<class Rhs>
constexpr static bool is_valid() {
return LastToken::template is_valid<Rhs>();
}
};
template<class Lhs, class Rhs,
std::enable_if_t< Rhs::template is_valid<Lhs>(), int>* =nullptr
>
auto operator/( Lhs lhs, Rhs rhs ) {
return name_expression<Rhs>{ std::string(lhs) + "." + rhs.name };
}
template<class Lhs, class Rhs,
std::enable_if_t< Rhs::template is_valid<Lhs>(), int>* =nullptr
>
auto operator/( name_expression<Lhs> lhs, Rhs rhs ) {
return name_expression<Rhs>{ std::string(lhs) + "." + rhs.name };
}
template<class Tag, class Token>
struct uniquely_tagged {
Tag tag;
Token const& token;
auto operator*( std::string n )&& {
return name( tag, std::move(n), token );
}
};
template<class OldTag, bool b, class...Parents, class Tag>
auto operator*( name_token<OldTag, b,Parents...> const& parent, Tag tag ) {
return uniquely_tagged<Tag, name_token<OldTag, b,Parents...>>{tag, parent};
}
}
然后我们可以创建名称标记,如下所示:
auto com = names::rootname([]{}, "com");
auto example = com*[]{}*"example";
auto shape = example*[]{}*"shape";
auto triangle = shape*[]{}*"triangle";
auto right = triangle*[]{}*"right";
auto isosceles = triangle*[]{}*"isosceles";
auto equilateral = triangle*[]{}*"equilateral";
auto quadrilateral = shape*[]{}*"quadrilateral";
auto rectangle = quadrilateral*[]{}*"rectangle";
auto square = quadrilateral*[]{}*"square";
现在com/example
有效,但com/shape
会产生编译时错误。
有点难看,请考虑不使用它。