Динамический класс C ++ (динамический взлом)
Вопрос
Есть ли способ добавить поле в класс во время выполнения (поле, которого раньше не было)? Примерно такой фрагмент:
Myobject *ob; // create an object
ob->addField("newField",44); // we add the field to the class and we assign an initial value to it
printf("%d",ob->newField); // now we can access that field
Мне действительно все равно, как это будет сделано, мне все равно, уродливый ли это хак или нет, я хотел бы знать, можно ли это сделать, и небольшой пример, если это возможно.
Другой пример. скажем, у меня есть файл XML, описывающий этот класс:
<class name="MyClass">
<member name="field1" />
<member name="field2" />
</class>
и я хочу " добавить " поля «field1» и "field2" к классу (при условии, что класс уже существует). Допустим, это код для класса:
class MyClass {
};
Я не хочу создавать класс во время выполнения, я просто хочу добавить элементы / поля к существующему.
Спасибо!
Решение
Используйте карту и вариант.
Например, используя boost :: option. См. http://www.boost.org/doc/libs/ 1_36_0 / DOC / HTML / variant.html
(Но, конечно, вы можете создать свой собственный, чтобы соответствовать типам ваших атрибутов XML.)
#include <map>
#include <boost/variant.hpp>
typedef boost::variant< int, std::string > MyValue ;
typedef std::map<std::string, MyValue> MyValueMap ;
Добавляя MyValueMap в качестве члена вашего класса, вы можете добавлять свойства в соответствии с их именами. Что означает код:
oMyValueMap.insert(std::make_pair("newField", 44)) ;
oMyValueMap.insert(std::make_pair("newField2", "Hello World")) ;
std::cout << oMyValueMap["newField"] ;
std::cout << oMyValueMap["newField2"] ;
Путем инкапсуляции его в класс MyObject и добавления правых перегруженных методов доступа в этот класс MyObject, приведенный выше код становится несколько понятнее:
oMyObject.addField("newField", 44) ;
oMyObject.addField("newField2", "Hello World") ;
std::cout << oMyObject["newField"] ;
std::cout << oMyObject["newField2"] ;
Но при этом вы несколько теряете безопасность типов в C ++. Но для XML это неизбежно, я думаю.
Другие советы
Нет способа сделать это так, как вы описали, так как компилятору нужно разрешить ссылку во время компиляции - он выдаст ошибку.
Но посмотрите шаблон универсального дизайна . р>
Вы не можете заставить этот синтаксис работать (из-за статической проверки во время компиляции), но если вы хотите изменить синтаксис, вы можете довольно легко добиться того же эффекта. Было бы довольно просто иметь элемент словаря с отображением строки-> > blob и иметь такие функции-члены, как:
template< typename T > T get_member( string name );
template< typename T > void set_member( string name, T value );
Вы можете сделать синтаксис более компактным / хитрым, если хотите (например, с помощью переопределения оператора '- >'). Есть также некоторые специфичные для компилятора приемы, которые вы могли бы использовать (например, MSVC поддерживает __declspec (свойство), которое позволяет сопоставить ссылки на переменную-член с методами определенного формата). В конце концов, однако, вы не сможете сделать что-то, что компилятор не принимает на языке, и заставить его скомпилировать.
Короткая версия: Не могу этого сделать. Для этого нет встроенной поддержки, c ++ имеет статическую типизацию, и компилятор должен знать структуру каждого объекта, которым нужно манипулировать.
Рекомендация: используйте встроенный преобразователь. И не пишите свой собственный (см. Ниже), получите тот, который уже работает и отлажен.
<Ч>Что вы можете сделать: внедрите достаточное количество преобразователей для своих нужд.
Было бы достаточно просто настроить класс с таким элементом данных, как
std::vector<void*> extra_data;
, к которому вы можете прикрепить произвольные данные во время выполнения. Стоимость этого заключается в том, что вам придется управлять этими данными вручную с помощью таких методов, как:
size_t add_data_link(void *p); // points to existing data, returns index
size_t add_data_copy(void *p, size_t s) // copies data (dispose at
// destruction time!), returns
// index
void* get_data(size_t i); //...
Но это не предел, с немного большей осторожностью, вы можете связать произвольные данные с именем, и вы можете продолжить разработку этой схемы, сколько пожелаете (добавить информацию о типе и т. д.), но к чему это приводит, так это к реализации интерпретатора, который позаботился о вашей гибкости во время выполнения.
Нет - C ++ не поддерживает какие-либо манипуляции с системой типов, подобные этой. Даже языки с некоторой степенью отражения во время выполнения (например, .NET) не поддерживают именно эту парадигму. Вам понадобится гораздо более динамичный язык, чтобы сделать это.
Я смотрел на это и немного обыскал, этот фрагмент кода получен из: Блог Майкла Хаммера похоже, это хороший способ сделать это, используя boost :: любой
Сначала вы определяете структуру, которая определяет std :: map, которая содержит ключ (то есть имя переменной) и значение. Функция определяется для объявления пары и установки ее вместе с функцией для получения значения. Довольно просто, если вы спросите меня, но это хороший способ начать, прежде чем делать более сложные вещи.
struct AnyMap {
void addAnyPair( const std::string& key , boost::any& value );
template<typename T>
T& get( const std::string key ) {
return( boost::any_cast<T&>(map_[key]) );
}
std::map<const std::string, boost::any> map_;
};
void AnyMap::addAnyPair( const std::string& key , boost::any& value ) {
map_.insert( std::make_pair( key, value ) );
}
Суть в том, что это хак, поскольку C ++ является языком строгой проверки типов, и поэтому монстры лежат внутри тех, кто нарушает правила. Р>