Я пытаюсь реализовать суперкласс в 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 ++, потому что, конечно, я не могу просто почтить эту пустоту *.
Мои вопросы:
Есть ли другой способ реализации желаемой функциональности?
Есть ли «законный» способ сделать это так, как я это делаю? (Я не могу просто использовать 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
Если ключ существует, почему бы просто не сделать
_keysDictionary[key] = value;
Или если вы хотите использовать итератор
it->second = value;
Сделайте setValueForKey шаблонной функцией вместо функции, которая принимает пустой указатель. Таким образом, вы можете знать информацию о типе свойства достаточно долго, чтобы создать шаблонный установщик
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 *
Это может быть сделано с использованием методов распознавания типов, и, например, шаблон Memento является одним из вариантов. Следующий код может быть дополнен некоторыми макросами, которые генерируют уникальные ключи на основе подписи указателя атрибута:
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.