Всем добрый день. Давно наваял библиотеку для эмуляции рифлексии для С++ обьектов, все времени нет привести это в божеский вид, но прежде всего давно хотелось услышать ваши впечателения и замечания о подходе и решениях.
Перечень предоставляемых сервисов:
1). Создание экземпляра метакласса по строковому названию класса в абстарктной форме, абстрактная фабрика. Возможность создания фабрики для отдельных семейств классов, например для семейства классов оконной системы, и отдельно для семейства прокси классов для удаленных обьектов и так далее. Все это разделение для ускорения регистрации и поиска метаклассов в списках. Далее ведется работа с полученным метаклассом.
2). Получение функтора-конструктора, который умеет создавать экземпляр обьекта. Возможность получения функтора-конструктора по конкретному заданному прототипу.
3). Получение функтора-деструктора, который умеет разрушать ранее созданный экземпляр.
4). Получение функтора-property по строковому имени и типу запрашиваемого свойства.
5). Получение функтора-метода по строковому имени и прототипу запрашиваемого свойства.
6). Инфраструктура сериализации/десериализации обьектов. В перспективе возможность добавить методы клонирования экземпляров на остнове инфраструктуры персистентности.
7). Упрощенные средства регистрации методов, свойств и конструкторов/деструкторов в метаклассах, средства регистрации метаклассов в родовой фабрике.
вот линк на библиотеку:
http://www.rsdn.ru/File/4126/Reflection.rar
Для компиляции использовался VC 7 ( 2003 ), и версия библиотеки Loki годичной давности. Пример использования библиотеки находится в файлах Application.h/cpp, Reflection.cpp.
Как это все выглядит в коде:
// Смотри файл Reflection.cpp
//Получение экземпляра метакласса:
TheClass* pClass =
TheClass::GetClass("Application::BusinessLogic::MyClassName");
//Получение функтора-конструктора по прототипу, заданному в ConstructParamList;
typedef TYPELIST_4( const std_string&,
unsigned char,
const std_string&,
unsigned int ) ConstructorParamList;
typedef Loki::Functor< TheObject*,
ConstructorParamList > Constructor;
//В случае отстутствия конструктора с таким прототипом будет выброшено исключение
//подобная стратегия применяется для всех запросов к метаклассу
Constructor ctor =
pClass->GetConstructor< ConstructorParamList>();
//Создание экземпляра обьекта с вызовом полученного конструктора
TheObject* pObj = ctor("Vasya", 27, "London", 12 );
//Get functor to member method
typedef Loki::NullType VoidParamList;
typedef Loki::Functor<void, VoidParamList > VoidMethod;
VoidMethod f1 =
pClass->GetMethod<void, VoidParamList >(pObj, "f1");
f1();
//Get string property
StringProperty name = pClass->GetProperty<std::string>(pObj, "name");
name = "Rick";
//Serialize object
std::ostrstream out_stream;
IO::Formatter::BinaryWriter b_writer(out_stream);
b_writer << pObj;
//Удаление экземпляра обьекта
TheClass::Destructor dtor = pClass->GetDestructor(pObj);
dtor();
Для определения своего формата сериализации достаточно написать свой Writer&Reader, XMLWriter/XMLReader, IniWriter/IniReader ( реализован только бинарный плоский формат ).
Для сериализации своих типов можно пойти по двум направлениям, либо поле будет использовать рефлексивную сериализацию либо достоточно определить операторы << >> ( как и было проделанно для некоторых STL-ных контейнеров в файле StdStreamOperations.h )
Несколько слов о том как классы адаптируются под инфраструктуру рефлексии:
//Смотри Application.h/Application.cpp
using namespace Reflection;
using namespace IO::Formatter;
class Employee :
public System::ObjectImpl< Employee,
System::Object<DEFAULT_THREADING> >
{
public:
typedef std_string app_string;
typedef Employee TheEmployee;
typedef System::Object<DEFAULT_THREADING> TheObject;
protected:
app_string _name;
byte _age;
app_string _address;
uint _department;
void DefaultInit();
public:
Employee();
Employee(
const app_string& name,
const Reflection::byte& age,
const app_string& address,
const uint department );
virtual void PrintEmployee();
DEFINE_STREAMING_OPERATIONS( EmployeeSerializer )
template<
template< class > class ThreadingModel = DEFAULT_THREADING >
struct TheClassImpl :
public Reflection::Private::ClassImpl< TheEmployee ,
TheObject,
EmployeeSerializer,
ThreadingModel >
{
TheClassImpl()
{
BindProperty( "name",
&Employee::_name);
BindProperty( "age",
&Employee::_age );
BindProperty( "address",
&Employee::_address );
BindProperty( "department",
&Employee::_department );
BindMethod( "PrintEmployee",
&Employee::PrintEmployee);
BindConstructor<
const app_string&,
Reflection::byte,
const app_string&,
uint>( );
}
};
typedef TheClassImpl<DEFAULT_THREADING> ClassImpl;
};
Макро DEFINE_STREAMING_OPERATIONS определяет темплейтную структуру для завязывания операторов << >> в механизм персистенстности, таким образом определив свои операторы в области видимости вашего класса компилятор подхватит эти операторы. Эту структуру можно определить единожды для группы классов, лишь бы операторы сериализации попали в область видимости единицы компиляции.
Позже структуру TheClassImpl можно закрыть макросами, для наглядности был приведен открытый код. Тайпдеф ClassImpl нужен для шаблона System::ObjectImpl, который имплементирует контейнер для экземпляра метакласса. Второй темплейт параметр класса Reflection::Private::ClassImpl определяет семейство для фабрики классов, о которой говорилось ранее, таким образом наш класс находится в семействе классов Object. Конструктор метакласса является регистратором всех полей и методов, которые будут учавствовать в рефлексии и персистентности.
Регистрация метакласса производится макросом в срр файле:
REGISTER_CLASS( Employee_Class_Tag, Application::BusinessLogic::Employees::Employee)
Шаблонный параметр литерал с внешней компоновкой оказался не так удобен, думаю потом я заменю этот макрос и первый его параметр за ненадобностью отпадет. В библиотеке наверняка куча багов, писал это все полтора года назад, после все ушло в стол и не применялось, так что наверняка есть кривости.
За деталями обращайтесь к коду. Буду рад любым фидбекам
06.07.04 17:33: Перенесено модератором из 'C/C++' по просьбе автора. — Павел Кузнецов