Reflection для С++ классов
От: Batiskaf Израиль http://www.mult.ru/
Дата: 30.04.04 10:29
Оценка: 136 (10)
Всем добрый день. Давно наваял библиотеку для эмуляции рифлексии для С++ обьектов, все времени нет привести это в божеский вид, но прежде всего давно хотелось услышать ваши впечателения и замечания о подходе и решениях.

Перечень предоставляемых сервисов:
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++' по просьбе автора. — Павел Кузнецов
Will I live tomorrow? Well I just can't say
But I know for sure — I don't live today.
Jimi Hendrix.