1

我正在尝试在 C++ 中实现一个超类,它将实现一种方法:

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

该方法所要做的就是将与给定键关联的属性的值设置为新值。这在实现自省机制的语言中很容易;据我所知,C++ 没有。

为了做到这一点,我创建了另一种方法:

void registerKeyForProperty(void *propertyPtr, string key);

这个方法所做的只是在内部存储一个指向与给定键关联的属性的指针,因此我的所有子类都会为他们声明的每个属性调用它,并且我将有一种方法可以为属性设置值而无需使用二传手。(这就是我需要的!)(我在帖子末尾解释了原因......)

对于第二个功能,我有以下实现:

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++,因为我当然不能只尊重 void*。

我的问题是:

有没有其他方法可以实现所需的功能?有没有像我这样做的“合法”方式?(我不能简单地使用 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
4

3 回答 3

1

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

_keysDictionary[key] = value;

或者如果你想使用迭代器

it->second = value;
于 2013-09-14T09:04:37.013 回答
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 强制转换。

于 2013-09-14T19:59:22.560 回答
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*

于 2013-09-14T18:22:18.030 回答