首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在用C++11进行反射编程时,如何在不知道向量元素类型的情况下枚举向量?

在用C++11进行反射编程时,如何在不知道向量元素类型的情况下枚举向量?
EN

Stack Overflow用户
提问于 2016-10-21 15:49:59
回答 2查看 191关注 0票数 0

我试图用C++在C++11实现中添加动态反射。

代码语言:javascript
复制
class Object {
public:
    Object() = default;
    ~Object() = default;
};

和从对象继承的两个类:

代码语言:javascript
复制
class Person : public Object {
public:
    std::string name;
    int age;
}

class Group : public Object {
public:
    std::string groupName;
    std::vector<Person> persons;
}

我实现了RefectManager来记录所有类的元信息,并创建了一个具有类名的对象,例如:

代码语言:javascript
复制
Object *obj = RefectManager::CreateObject("Group");
MetaInfo *groupMeta = obj->GetMetaInfo();

其中,"groupMeta“保存类组的元信息,它知道:

类组有一个字段列表,其中包含两个字段: *一个名为"groupName“的字段,其类型的名称是"std::string” *一个名为"Person“的字段,其类型的名称为”std::“,在向量中元素类型的名称为”Person“。

我可以通过这个人的名字获得这个人的元信息:

代码语言:javascript
复制
MetaInfo *personMeta = RefectManager::GetMetaInfo("Person");

但是,是否有一种方法可以动态地枚举类组中具有反射元信息的"persons“字段,例如:

代码语言:javascript
复制
for (field in groupMeta's field list) {
    if (field type's name is "std::string") {
        get field's content as string
    } else if (field type's name is "std::vector") {
        // only the element type's name is known as "Person"

        // **how to enumerate the field with vector type?**

        // If we know the element's type through element type's name, 
        // we can do it as following:
        // std::vector<GetType<"Person">::type> *p = (std::vector<GetType<"Person">::type> *)field;
        // std::vector<GetType<"Person">::type>::iterator it = p->begin();
        // for (; it != p->end(); ++it) {
        //     //now we can access the element in field
        // }
    }
}
EN

回答 2

Stack Overflow用户

发布于 2016-10-21 18:02:18

vector<int>vector<bool>vector<Person>是不相关的类型。

您说的是“它们都是向量,内容不同”,但是在C++中,模板会生成类型。std::vector不是一种类型,而是一种类型的工厂。

我们还能在这里做点什么。我们可以讨论vector支持什么操作,以及我们关心什么。

假设我们只想读取我们的vector,实际上,我们只希望能够迭代!

从拥有any_view的想法开始,其中any_view是任何事物的视图(比如void*,但更聪明)。现在,vector<Foo>是一种range<any_view>

这个“视图”概念将值从引用或指针中分割出来--指向值。您的Object类型适合于值,而不是值的视图。

您可以对Object*进行的大部分操作都是您希望在any_view上进行的操作;但是any_view并不拥有它正在查看的内容,不像Object

接口(忽略细节)如下所示:

代码语言:javascript
复制
template<class It>
struct range_t{
  It b, e;
  It begin() const { return b; }
  It end() const { return e; }
  bool empty() const { return begin()==end(); }
  // etc
};
template<class X>
struct any_input_iterator{
  // implement a boost-like type erasure wrapper around
  // the concept of input iteration (read only single-pass iteration)
  using self=any_forward_iterator;
  friend bool operator==(self const&, self const&);
  friend bool operator!=(self const&, self const&);
  self& operator++();
  self operator++(int);
  X operator*()const;
  using value_type = std::remove_reference_t<X>;
  using reference = X;
  // etc
};
template<class X>
using iterable_over = range_t< any_input_iterator<X> >;

struct any_view {
  std::typeinfo const* get_typeid() const;
  template<class T>
  T* get_as() const;
  iteratable_over<any_view> members() const;
};

然后,我们编写生成描述any_view类型T的后端数据的特性类,并编写接受T&并生成any_view的转换构造函数。any_view保存一个指向实现详细信息的函数指针的手动vtable的指针,以及一个指向T&void*,并将其方法分派给它(传入void*)。

这并不容易,但这是解决方案的路线图。一个完整的解决方案对于一个堆栈溢出的post来说有点大。

特别是,类型擦除类型擦除,其中包含super_any包含std::refs作为我们的any_view,我们输入擦除操作的"get成员“,然后我们使用一种基于ADL的自由函数或基于特征的方法来描述成员和如何得到他们,将工作。

完成所有这些之后,您的代码看起来可能如下所示:

代码语言:javascript
复制
for (auto field : data) {
  if (*field.type_id() == typeid(std::string)) {
    std::string s = *field.get_as<std::string>();
  } else if (!field.members().empty()) {
    for (auto&& e:field.members()) {
      if (*e.second.type_id() == typeid(Person)) {
        Person& p = *e.get_as<Person>();
      }
    }
  }
}

注意,在这个抽象中,vector只是一堆成员,它们是元素。让它与众不同也是可以做到的。

票数 0
EN

Stack Overflow用户

发布于 2016-10-23 17:25:53

多亏了Yakk,我用lambda函数完成了类型擦除。

在生成类成员的元信息时,我还将使用它生成lambda函数。

因此,在枚举容器时,让lambda函数来完成它,仅此而已。

再次谢谢你!

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/40180813

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档