Задание элементов данных с помощью общего установщика

Я пытаюсь реализовать суперкласс в 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

1

Решение

Если ключ существует, почему бы просто не сделать

_keysDictionary[key] = value;

Или если вы хотите использовать итератор

it->second = value;
1

Другие решения

Сделайте 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 *

0

Это может быть сделано с использованием методов распознавания типов, и, например, шаблон 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.

0
По вопросам рекламы [email protected]