Инстанцировать класса с имени?

У Вас есть способность к командам выполнения удаленно? утилита PsExec от , которому позволил бы Systinternals, работает, командная строка разархивировали программу на удаленной машине. Если у Вас есть сценарий, который копирует сборку как .zip файл на удаленный сайт, Вам просто была бы нужна еще одна строка для вызова PsExec для разархивации файлов.

29
задан Lightness Races with Monica 26 July 2011 в 14:09
поделиться

6 ответов

Это проблема, которая обычно решается с помощью Шаблон реестра :

Это ситуация, когда Шаблон реестра описывает:

Объекты должны связаться с другим объект, зная только имя объекта или название службы это предоставляет, но не как с ним связаться. Предоставьте услугу, получившую название объекта, услуги или роли и возвращает удаленный прокси, который инкапсулирует знания о том, как свяжитесь с указанным объектом.

Это та же базовая модель публикации / поиска. что составляет основу Сервиса Ориентированная архитектура (SOA) и для уровень служб в OSGi.

Реестр обычно реализуется с использованием одноэлементного объекта, одноэлементный объект получает информацию во время компиляции или во время запуска об именах объектов и способах их создания. Затем вы можете использовать его для создания объекта по запросу.

Например:

template<class T>
class Registry
{
    typedef boost::function0<T *> Creator;
    typedef std::map<std::string, Creator> Creators;
    Creators _creators;

  public:
    void register(const std::string &className, const Creator &creator);
    T *create(const std::string &className);
}

Вы регистрируете имена объектов и функции создания следующим образом:

Registry<I> registry;
registry.register("MyClass", &MyClass::Creator);

std::auto_ptr<T> myT(registry.create("MyClass"));

Затем мы могли бы упростить это с помощью умных макросов, чтобы он мог быть сделано во время компиляции. ATL использует шаблон реестра для CoClasses, который может быть создан во время выполнения по имени - регистрация так же проста, как использование чего-то вроде следующего кода:

OBJECT_ENTRY_AUTO(someClassID, SomeClassName);

Этот макрос помещается где-то в вашем заголовочном файле, по волшебству он должен быть зарегистрирован синглтоном во время запуска COM-сервера.

39
ответ дан 28 November 2019 в 01:24
поделиться

В C ++ это решение должно быть принято во время компиляции .

Во время компиляции вы можете использовать typedef вместо макроса:

typedef DefaultClass MyDefaultClassToUse;

это эквивалентно и избегает макроса (макросы плохие ;-)).

Если решение должно быть принято во время выполнения, вам нужно написать свой собственный код для его поддержки. Простое решение - это функция, которая проверяет строку и создает экземпляр соответствующего класса.

Расширенная версия этого (позволяющая независимым разделам кода регистрировать свои классы) будет map ].

2
ответ дан 28 November 2019 в 01:24
поделиться

Вы упомянули две возможности - командную строку и макрос компиляции, но решение для каждой из них сильно отличается.

Если выбор сделан с помощью макроса компиляции, это простая проблема, которую можно решить с помощью #defines, #ifdefs и т.п. Предлагаемое вами решение ничем не хуже любого другого.

Но если выбор сделан во время выполнения с использованием аргумента командной строки, тогда вам понадобится некоторая структура Factory, способная получать строку и создавать соответствующий объект. Это можно сделать с помощью простого статического if () .. else if () ... else if () ...

1
ответ дан 28 November 2019 в 01:24
поделиться

Почему бы не использовать фабрику объектов?

В простейшей форме:

BaseClass* myFactory(std::string const& classname, params...)
{
    if(classname == "Class1"){
        return new Class1(params...);
    }else if(...){
        return new ...;
    }else{
       //Throw or return null
    }
    return NULL;
}
5
ответ дан 28 November 2019 в 01:24
поделиться

Один из способов реализовать это - жестко запрограммировать отображение «имен» класса в фабричную функцию. Шаблоны могут сделать код короче. STL может упростить кодирование.

#include "BaseObject.h"
#include "CommonClasses.h"

template< typename T > BaseObject* fCreate( int param1, bool param2 ) {
    return new T( param1, param2 );
}

typedef BaseObject* (*tConstructor)( int param1, bool param2 );
struct Mapping { string classname; tConstructor constructor; 
    pair<string,tConstructor> makepair()const { 
        return make_pair( classname, constructor ); 
    }
} mapping[] = 
{ { "class1", &fCreate<Class1> }
, { "class2", &fCreate<Class2> }
// , ...
};

map< string, constructor > constructors;
transform( mapping, mapping+_countof(mapping), 
    inserter( constructors, constructors.begin() ), 
    mem_fun_ref( &Mapping::makepair ) );

РЕДАКТИРОВАТЬ - по общему запросу :) небольшая доработка, чтобы все выглядело более плавно (спасибо Stone Free, который, вероятно, не хотел сам добавлять ответ)

typedef BaseObject* (*tConstructor)( int param1, bool param2 );
struct Mapping { 
    string classname; 
    tConstructor constructor; 

    operator pair<string,tConstructor> () const { 
        return make_pair( classname, constructor ); 
    }
} mapping[] = 
{ { "class1", &fCreate<Class1> }
, { "class2", &fCreate<Class2> }
// , ...
};

static const map< string, constructor > constructors( 
      begin(mapping), end(mapping) ); // added a flavor of C++0x, too.
10
ответ дан 28 November 2019 в 01:24
поделиться

Раньше я реализовал шаблон Factory таким образом, чтобы классы могли самостоятельно регистрироваться во время выполнения без необходимости самой фабрики знать о них конкретно. Ключевым моментом является использование нестандартной функции компилятора, называемой (IIRC) «присоединение путем инициализации», в которой вы объявляете фиктивную статическую переменную в файле реализации для каждого класса (например, bool) и инициализируете ее вызовом регистрации.

В этой схеме каждый класс должен # включить заголовок, содержащий его фабрику, но фабрика ничего не знает, кроме класса интерфейса. Вы можете буквально добавлять или удалять классы реализации из своей сборки и перекомпилировать без изменения кода.

Загвоздка в том, что только некоторые компиляторы поддерживают присоединение путем инициализации - другие IIRC инициализируют переменные области видимости файла при первом использовании (точно так же, как работает статика, локальная для функции), что здесь не помогает, поскольку к фиктивной переменной никогда не осуществляется доступ, а Заводская карта всегда будет пустой.

Но интересующие меня компиляторы (MSVC и GCC) поддерживают это, так что для меня это не проблема. Вам нужно будет решить для себя, подходит ли вам это решение.

0
ответ дан 28 November 2019 в 01:24
поделиться
Другие вопросы по тегам:

Похожие вопросы: