使用通用setter设置数据成员

时间:2013-09-14 08:58:22

标签: c++ reflection introspection

我正在尝试在c ++中实现一个实现一个方法的超类:

-void setValueForKey(void *value, string key);

所有这些方法都必须将与给定键关联的属性的值设置为新值。 在实现内省机制的语言中,这很容易;据我所知,C ++没有。

为了实现这个目的,我创造了另一种方法:

void registerKeyForProperty(void *propertyPtr, string key);

所有这个方法都是存储和内部映射指向与给定键关联的属性的指针,所以我的所有子类都会为它们声明的每个属性调用它,我将有一种方法来设置属性的值而没有必要使用setter。(这就是我需要的!)(我在帖子的最后解释了为什么......)

对于第二个函数,我有以下实现:

void registerKeyForProperty(void *propertyPtr, string key){

   _keysDictionary->insert(pair<string,void*>(key,property));
}

其中_keysDictionary是一个stl地图。

对于第一个我有以下实现:

void ConstructableObject::setValueForKey(void* value, string key) {

    map<string,void *>::iterator it=_keysDictionary->find(key);

    if(it==_keysDictionary->end()){return;}//just return if there is nothing for that key

    void *property=it->second;

    (*property)=value;
}

问题是最后一行不是合法的C ++,因为当然我不能仅仅尊重无效*。

我的问题是:

还有其他方法可以实现所需的功能吗? 我这样做的方式是否有“合法”的方式? (我不能简单地使用reinterpret_cast因为我不知道要播放什么......)

为什么:

我需要解析包含某些对象信息的xml文件。我将使用TinyXML,因此我将拥有对象及其值的属性名称。这就是我想用它的方式:

MyClass obj();//the constructor would call setValueForKey(...,...) for every property so all are now registered
for every attribute{
    obj.setValueForKey(attribute.value,attribute.name);
}

//all properties should be set now

3 个答案:

答案 0 :(得分:1)

如果密钥存在,为什么不简单地执行

_keysDictionary[key] = value;

或者如果你想使用迭代器

it->second = value;

答案 1 :(得分:0)

使setValueForKey成为模板化函数,而不是接受void指针的函数。这样,您可以了解属性的类型信息足够长的时间来创建模板化的setter

class BaseSetter
{
    public:
        virtual void set(void* inValue) = 0;
}

template <typename T>
class SpecializedSetter
{
    public:
        SpecializedSetter(T* inMyValue)
        : mValue(inValue)
        { }

        virtual void set(void* inValue)
        {
            *mValue = *reinterpret_cast<T*>(inValue);
        }
   private:
        T* mValue;
}


template <typename T>
void registerKeyForProperty(T* inValue, string inKey)
{
    registerSetterForProperty(new SpecificSetter<T>(inValue), inKey);
}

但是,假设inValue是指向与类上的值相同类型的数据的指针。为了确保安全,请考虑boost::any或定义包含XML文件中类型信息的其他类型并使用它而不是void *

答案 2 :(得分:0)

可以使用类型感知技术来完成,例如,Memento Pattern是其中一种选择。可以使用基于属性指针签名生成唯一键的一些宏内容扩展以下代码:

class introspection
{
public:
  template <typename Class, typename Member>
  void registerKey(std::string key, Member Class::*memberPointee, Class* classPointee)
  {
    typedef member_setter<Class, Member> hold_member_pointer;
    base_setter* setter = new hold_member_pointer(memberPointee, classPointee);
    keys.insert(std::make_pair(key, setter));
  }

  template <typename Value>
  void setValue(std::string key, Value value)
  {
    if ( keys.count(key) > 0 )
    {
      keys[key]->set(value);
    }
    else
    {
      throw std::logic_error("no such key");
    }
  }

private:
  struct base_setter
  {
    virtual void set(boost::any value) = 0;
  };  // struct base_setter

  template <typename Class, typename Member>
  struct member_setter : base_setter
  {
    member_setter(Member Class::*memberPointee, Class* classPointee)
    : memberPointee(memberPointee)
    , classPointee(classPointee) {}

    void set(boost::any value) override
    {
      Member newValue = boost::any_cast<Member>(value);
      classPointee->*memberPointee = newValue;
    }

    Member Class::*memberPointee;
    Class*          classPointee;
  };  // struct member_setter

  std::map<std::string, base_setter*> keys;
};  // class introspection

struct Data
{
  int value;
};  // struct Data

int main()
{
  introspection i;
  Data d;
  d.value = 100;
  i.registerKey("value", &Data::value, &d);
  i.setValue("value", 200);           // OK
  i.setValue("value", "not valid");   // bad_any_cast
}

这里可以(不那么容易)改进的一件事是为setValue提供编译时类型检查,而不是运行时any_cast强制转换。