【问题标题】:Accessing members in a C++ struct both dynamically and statically动态和静态访问 C++ 结构中的成员
【发布时间】:2010-12-02 12:51:16
【问题描述】:

我想在 C++ 中有一个结构(或类似的东西),它允许动态访问它的成员。它应该有一个通用的 getter 和 setter,以字符串形式接收成员名称,并返回某种变体类型(例如 boost::variant)。

我认为它可以使用boost::fusion::map 来实现,方法是添加一个表示每个成员名称的字符串,并在字符串和getter 或setter 函数之间构建一个STL 映射。我不想重新发明轮子,所以我希望类似的东西已经存在。

你怎么看?我的想法可行吗?你知道实现我目标的其他方法吗?

【问题讨论】:

  • 我想知道你为什么要这个?即使在直接支持它的语言中,反射也是一种用来绕过糟糕代码的技巧,或者对于懒惰的程序员来说是廉价代码。
  • 您正在击败 C++ 为您提供的类型安全。究竟有什么理由可以证明用一个用于实现不确定性的凌乱黑客来交换如此简单、强大的正确性工具?
  • @wilhelmtell:我正在努力寻找合适的平衡点。我正在从一个非常不确定的来源读取值,它们需要以通用方式进行解析和处理。只有一小部分我知道(并且想知道)正确的类型。
  • @Dibling:我一直以为懒惰的程序员哪里有最好的程序员……
  • 那么关于使用各种类型的标准容器而不是结构?

标签: c++ boost boost-fusion


【解决方案1】:

融合是一种方法,但为什么不将您的“字段”存储在由std::string 键入的std::map 中,其中有效负载是boost::variant...

struct generic
{
std::map<std::string, boost::variant<foo, bar, bob, int, double> > _impl;
};

然后您就可以在 getter/setter 中查找密钥...

哎呀,将variant 包装在optional 中,您可以有可选字段!

一个更复杂的例子:

class foo
{
public:
  typedef boost::variant<int, double, float, string> f_t;
  typedef boost::optional<f_t&> return_value;
  typedef map<string, return_value> ref_map_t;

  foo() : f1(int()), f2(double()), f3(float()), f4(string()), f5(int()) 
  {
    // save the references..
    _refs["f1"] = return_value(f1);
    _refs["f2"] = return_value(f2);
    _refs["f3"] = return_value(f3);
    _refs["f4"] = return_value(f4);
    _refs["f5"] = return_value(f5);
  }

  int getf1() const { return boost::get<int>(f1); }
  double getf2() const { return boost::get<double>(f2); }
  float getf3() const { return boost::get<float>(f3); }
  string const& getf4() const { return boost::get<string>(f4); }
  int getf5() const { return boost::get<int>(f5); }

  // and setters..
  void setf1(int v) { f1 = v; }
  void setf2(double v) { f2 = v; }
  void setf3(float v) { f3 = v; }
  void setf4(std::string const& v) { f4 = v; }
  void setf5(int v) { f5 = v; }

  // key based
  return_value get(string const& key)
  {
    ref_map_t::iterator it = _refs.find(key);
    if (it != _refs.end())
      return it->second;
    return return_value();
  }

  template <typename VT>
  void set(string const& key, VT const& v)
  {
    ref_map_t::iterator it = _refs.find(key);
    if (it != _refs.end())
      *(it->second) = v;
  }

private:
  f_t f1;
  f_t f2;
  f_t f3;
  f_t f4;
  f_t f5;

  ref_map_t _refs;
};

int main(void)
{
  foo fancy;
  fancy.setf1(1);
  cout << "f1: " << fancy.getf1() << endl;

  fancy.set("f1", 10);
  cout << "f1: " << fancy.getf1() << endl;

  return 0;
}

【讨论】:

  • 这可行,但我更喜欢严格的类型检查,以及在编译时知道字段名称的情况下更快的运行时访问。
  • 好的,在这种情况下,在地图中存储对真实字段的引用(它们的类型是变体 - 上面的示例)...
  • 这里的问题是我需要添加一个新字段的5个地方。这似乎是用于添加仅由编译时名称、字符串和类型组成的大量样板。也许我可以修改你的解决方案,让字段公开(以及它们的非变体类型),并在地图中存储某种包装器,将每个包装器转换为变体。
  • 我最终写了一些更复杂的东西:我创建了一个包含字符串键、值的 getter 和 setter 函数的映射,而不是包含变体的映射。然后,getter 和 setter 访问 boost::fusion::map 对象以获取实际值。但是这个解决方案比必要的更复杂......
【解决方案2】:

您在 C++ 中要求Reflection,我认为这是不可用的。你必须想出你自己的东西。

【讨论】:

  • RTTI 在 C++ 中可用,您正在考虑 reflection... :)
  • 是的,C++ 中的反射会使其更简单。我将满足于一种解决方案,该解决方案需要最少的新代码行来添加新字段。
【解决方案3】:

我为此做了一个类似于 boost::cons 的类型列表,其中包含我的成员和某种描述。然后,我通过“链式”函数调用将我的成员连续添加到“元信息”数据结构中来构建此映射。整个事情看起来非常类似于在 boost.python 中定义一个类。如果你真的使用 boost::cons,它也应该在 boost.fusion 中作为一个序列工作,这样你就可以很好地迭代你的数据。也许您可以使用 boost.fusion 映射来在运行时获取 log(n) 访问时间,但在可变参数模板可用之前,它们的大小似乎是有限的。

【讨论】:

  • 我认为 boost::fusion::map 和 boost::cons 都具有 O(1) 运行时访问权限。
猜你喜欢
  • 1970-01-01
  • 2021-01-21
  • 2021-01-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-09-30
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多