Динамический класс C ++ (динамический взлом)

StackOverflow https://stackoverflow.com/questions/235376

  •  04-07-2019
  •  | 
  •  

Вопрос

Есть ли способ добавить поле в класс во время выполнения (поле, которого раньше не было)? Примерно такой фрагмент:

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 ++ является языком строгой проверки типов, и поэтому монстры лежат внутри тех, кто нарушает правила.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top