繁体   English   中英

基于字符串的类型层次结构,带有编译类型检查

[英]Type hierarchy based on string, with compile type check

我有一个类型的树层次结构,每个类型都由字符串定义,如下所示:

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具有代码提示,但是很遗憾,定义起来并不容易。 每个不同的树级别都要求使用新成员定义新类,该名称与某些字符串相对应。

我正在寻找更好的解决方案-更简单。 像模板专门化一样定义它会很棒,但是我不知道该怎么做。

欢迎任何建议:)问候,皮丘。

这是一个稍微不同的方法。

在这里,我们创建与您的com名称匹配的变量name_token 然后,我们将他们与聪明的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产生编译时错误。

现场例子

有点难看,考虑不使用它。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM