Передача объекта произвольного типа.
От: AndreyM16  
Дата: 14.04.11 06:34
Оценка:
Здравствуйте!

Возникли вопросы про сабжу. Как это можно сделать и вообще правильно ли это? По мне так такая штука очень полезная, например есть набор модулей с общим интерфейсом, которые общаются через сообщения, но данные в сообщениях могут быть разного типа и их количество(типов данных) может со временем расти или в том же обзёрвере в его push варианте. Может быть такое возникает при ошибках проектирования или это нормальная ситуация?
Сейчас вижу несколько способов как такое реализовать:

1) void* По мне это грубый хак без какой либо проверки типов.

2) boost::any. Вообщем намного лучше чем первый вариант, есть возможность проверить тип, хотя все равно тоже самое. Но эта возможность(через type_info) не очень стандартизирована и при компиляции разных модулей разными компиляторами может не прокатить, а также тратиться дополнительное время на эту самую проверку.

3) boost::variant. Реализация паттерна визитор. Нет проблем с проверкой типов, но количество типов изначально ограничено и при добавлении нового нужно переписывать код этих самых визиторов.

4) сериализация. Универсальный способ, но зачем нужно сериализовывать объект если нужна только ссылка на него.

В языках с динамической(утиной) типизацией таких вопрос не возникает. В C# насколько я знаю(хотя плохо знаю) передают объект CObject.
Вообщем теряюсь в догадках как это можно нормально реализовать на C++ и вообще правильно ли это или нет.
Re: Передача объекта произвольного типа.
От: Erop Россия  
Дата: 14.04.11 07:28
Оценка:
Здравствуйте, AndreyM16, Вы писали:

AM>4) сериализация. Универсальный способ, но зачем нужно сериализовывать объект если нужна только ссылка на него.


5) Завести модуль, который рулит передачей параметров и сообщений. Все остальные регят в нём типы, которые используют, сообщения, и конвертилки типов, если нужны/доступны.

Дальше аналог any, но свой и не привязанный к реализации RTTI...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re[2]: Передача объекта произвольного типа.
От: AndreyM16  
Дата: 14.04.11 08:10
Оценка:
Здравствуйте, Erop, Вы писали:

E>Здравствуйте, AndreyM16, Вы писали:


AM>>4) сериализация. Универсальный способ, но зачем нужно сериализовывать объект если нужна только ссылка на него.


E>5) Завести модуль, который рулит передачей параметров и сообщений. Все остальные регят в нём типы, которые используют, сообщения, и конвертилки типов, если нужны/доступны.


E>Дальше аналог any, но свой и не привязанный к реализации RTTI...


Как выделенное можно реализовать? Или нужно изначально задать определенные типы которые могут быть использованы(хотя они могут быть определены, только в этом спомогательном модуле) и добавлять новые если появляются, или использовать type_info, я других способов не вижу. А вообще, потребность в этом не ошибка проектирования?
Re: Передача объекта произвольного типа.
От: uzhas Ниоткуда  
Дата: 14.04.11 08:18
Оценка:
Здравствуйте, AndreyM16, Вы писали:

AM>4) сериализация. Универсальный способ, но зачем нужно сериализовывать объект если нужна только ссылка на него.

если система маленькая, то void* + GetType() достаточно
сериализация подходит для серьезной системы (event bus)
лучше отказаться от "ссылок" и передавать данные по значению. так вы решите проблемы с разными модулями (std::string через границы dll, runtime, etc) и временем жизни данных
в сериализации одна из больших подзадач — это поддержка версионности (через xml можно все обрулить)
общее название: message queue, event bus. можете в гугле поискать
Re: Передача объекта произвольного типа.
От: night beast СССР  
Дата: 14.04.11 08:32
Оценка:
Здравствуйте, AndreyM16, Вы писали:

AM>2) boost::any. Вообщем намного лучше чем первый вариант, есть возможность проверить тип, хотя все равно тоже самое. Но эта возможность(через type_info) не очень стандартизирована и при компиляции разных модулей разными компиляторами может не прокатить, а также тратиться дополнительное время на эту самую проверку.


предлагаю обсудить такой набросок (кое где можно подправить, но как иллюстрация пойдет):
enum any_message { any_error, any_clone, any_alloc, any_free };

template< typename T >
struct holder {
    T value;

    holder() : value() {}
    static void * apply ( any_message msg, void const * src ) {
        switch ( msg ) {

        case any_alloc: {
            return 0;
        } case any_clone: {
            void * ptr = new T( * static_cast<T const *>( src ) );
            std::cout << "clone:" << src << " -> " << ptr << std::endl;
            return ptr;
        } case any_free: {
            std::cout << "free:" << src << std::endl;
            delete static_cast<T const *>( src );
            return 0;
        } default: {
            assert( 0 );
        }

        }
    }
};

struct any {
    void * content;
    void * (* id)( any_message, void const * );


    template< typename T >
    any ( T const & src ) : content( new T( src ) ), id( &holder<T>::apply ) { std::cout << "alloc:" << (void *)content << std::endl; }

    any ( any const & rhs ) : content( rhs.id( any_clone, rhs.content ) ), id( rhs.id ) {}

        ~any () { this->id( any_free, this->content ); }


    any & swap ( any & rhs ) 
    {
        std::swap( this->content, rhs.content );
        std::swap( this->id, rhs.id );

        return *this;
    }

    any & operator = ( any rhs ) { rhs.swap( *this ); return *this; }

    template< typename T > T * get() { 
        return ( this->id == &holder<T>::apply ) ? static_cast<T *>( this->content ) : 0;
    }
};

int main()
{
    any x = int( 1 );


    any y = int( 2 );

    any z = float( 2 );

    x = y;

    assert( *x.get<int>() == 2 );

    x = z;

    assert( x.get<int>() == 0 );

    return 0;
}
Re: Передача объекта произвольного типа.
От: BulatZiganshin  
Дата: 14.04.11 08:44
Оценка: 7 (1)
Здравствуйте, AndreyM16, Вы писали:

AM>Возникли вопросы про сабжу. Как это можно сделать и вообще правильно ли это? По мне так такая штука очень полезная, например есть набор модулей с общим интерфейсом, которые общаются через сообщения, но данные в сообщениях могут быть разного типа и их количество(типов данных) может со временем расти


я сделал http://code.google.com/p/tabi/
Люди, я люблю вас! Будьте бдительны!!!
Re[2]: Передача объекта произвольного типа.
От: AndreyM16  
Дата: 14.04.11 08:46
Оценка:
Здравствуйте, night beast, Вы писали:

NB>Здравствуйте, AndreyM16, Вы писали:


AM>>2) boost::any. Вообщем намного лучше чем первый вариант, есть возможность проверить тип, хотя все равно тоже самое. Но эта возможность(через type_info) не очень стандартизирована и при компиляции разных модулей разными компиляторами может не прокатить, а также тратиться дополнительное время на эту самую проверку.


NB>предлагаю обсудить такой набросок (кое где можно подправить, но как иллюстрация пойдет):

NB>
...
NB>


Это не будет работать если есть по крайней мере два отдельно компилирумых модуля.
Re[3]: Передача объекта произвольного типа.
От: night beast СССР  
Дата: 14.04.11 09:04
Оценка:
Здравствуйте, AndreyM16, Вы писали:

AM>>>2) boost::any. Вообщем намного лучше чем первый вариант, есть возможность проверить тип, хотя все равно тоже самое. Но эта возможность(через type_info) не очень стандартизирована и при компиляции разных модулей разными компиляторами может не прокатить, а также тратиться дополнительное время на эту самую проверку.


NB>>предлагаю обсудить такой набросок (кое где можно подправить, но как иллюстрация пойдет):


немного подумав решил что механизм виртуальности предпочтительней.

AM>Это не будет работать если есть по крайней мере два отдельно компилирумых модуля.


объектника или длл? если объектника, то в чем именно косяк?
Re[4]: Передача объекта произвольного типа.
От: AndreyM16  
Дата: 14.04.11 09:12
Оценка:
Здравствуйте, night beast, Вы писали:

NB>Здравствуйте, AndreyM16, Вы писали:


AM>>>>2) boost::any. Вообщем намного лучше чем первый вариант, есть возможность проверить тип, хотя все равно тоже самое. Но эта возможность(через type_info) не очень стандартизирована и при компиляции разных модулей разными компиляторами может не прокатить, а также тратиться дополнительное время на эту самую проверку.


NB>>>предлагаю обсудить такой набросок (кое где можно подправить, но как иллюстрация пойдет):


NB>немного подумав решил что механизм виртуальности предпочтительней.


AM>>Это не будет работать если есть по крайней мере два отдельно компилирумых модуля.


NB>объектника или длл? если объектника, то в чем именно косяк?


Я имел ввиду dll так как в вашем any типы индексируются ссылкой на инстанцированный класс holder, и в различных dll для одного типа они будут разными.
Re[5]: Передача объекта произвольного типа.
От: night beast СССР  
Дата: 14.04.11 09:18
Оценка:
Здравствуйте, AndreyM16, Вы писали:

NB>>>>предлагаю обсудить такой набросок (кое где можно подправить, но как иллюстрация пойдет):


NB>>немного подумав решил что механизм виртуальности предпочтительней.


AM>>>Это не будет работать если есть по крайней мере два отдельно компилирумых модуля.


NB>>объектника или длл? если объектника, то в чем именно косяк?


AM>Я имел ввиду dll так как в вашем any типы индексируются ссылкой на инстанцированный класс holder, и в различных dll для одного типа они будут разными.


про длл я ни спорил.
Re: Передача объекта произвольного типа.
От: Тот кто сидит в пруду Россия  
Дата: 14.04.11 09:32
Оценка: +3
Здравствуйте, AndreyM16, Вы писали:

AM>2) boost::any. Вообщем намного лучше чем первый вариант, есть возможность проверить тип, хотя все равно тоже самое. Но эта возможность(через type_info) не очень стандартизирована и при компиляции разных модулей разными компиляторами может не прокатить, а также тратиться дополнительное время на эту самую проверку.


Тут надо понимать, что при компиляции разных модулей разными компиляторами может вообще ничего не проканать, кроме plain С. И даже обычный _HAS_ITERATOR_DEBUGGING=0 в MSVC 2010 способен поломать бинарную совместимость std::string между вашим модулем и CRT. Так что бы порекомендовал осетра маленько урезать и сперва четко определиться с набором условий, для которого вам нужно решение.
Одним из 33 полных кавалеров ордена "За заслуги перед Отечеством" является Геннадий Хазанов.
Re[3]: Передача объекта произвольного типа.
От: Mr.Delphist  
Дата: 14.04.11 10:14
Оценка:
Здравствуйте, AndreyM16, Вы писали:

AM>А вообще, потребность в этом не ошибка проектирования?


— полностью согласен. Сколько раз сталкивался с таким, всё равно этот "произвольный объект" и финальный свитч "по типам" в конце концов заменялся на иное решение, более "прямого" характера. Поэтому — не тратьте время, если нет горящего дедлайна. Обдумайте спокойно: как можно генерализовать необходимые операции? зачем понадобился этот нашлёпок с "любым типом"?
Re[4]: Передача объекта произвольного типа.
От: BulatZiganshin  
Дата: 14.04.11 10:56
Оценка: +1
Здравствуйте, Mr.Delphist, Вы писали:

AM>>А вообще, потребность в этом не ошибка проектирования?


MD> — полностью согласен. Сколько раз сталкивался с таким, всё равно этот "произвольный объект" и финальный свитч "по типам" в конце концов заменялся на иное решение, более "прямого" характера. Поэтому — не тратьте время, если нет горящего дедлайна. Обдумайте спокойно: как можно генерализовать необходимые операции? зачем понадобился этот нашлёпок с "любым типом"?


для расширяемости. например поддержки в сервере новой операции без переделки старых клиентов, её не использующих. странно, что ты проспал использование xml, например, для этих целей
Люди, я люблю вас! Будьте бдительны!!!
Re[3]: Передача объекта произвольного типа.
От: Erop Россия  
Дата: 14.04.11 11:37
Оценка:
Здравствуйте, AndreyM16, Вы писали:

AM>Как выделенное можно реализовать? Или нужно изначально задать определенные типы которые могут быть использованы(хотя они могут быть определены, только в этом спомогательном модуле) и добавлять новые если появляются, или использовать type_info, я других способов не вижу.


В смысле? Какие проблемы с реализацией? Регишь имя типа в виде строки и интерфейс для доступа. Полагаешься на то, что твои плагины не будут называть одним именем два разных типа. Это ничего не ухудшает, так как просто при программировании на С++ ты на это тоже полагаешься...

Если всё и всегда inprocess, то можно обойтись и без доп. модуля. И всё регать локально. В каждом из модулей.

Смотри. Пишешь что-то типа
class MyAny {
public:

    MyAny( const MyAny& );  // тривиально, через data->Clone()
    ~MyAny();               // тоже тривиально.
    template<typename T>
    static MyAny Create( T& toCreate )
    {
        return MyAny( TypeRegistrar<T>::GetAnyName(), new IT<T>( toCreate ) );
    }

    template<typename T> T Get()
    {
         assert( TypeRegistrar<T>::HasName( typeName ) );
         return return *static_cast<T const*>( data->Get() )
    }

    template<typename T>
    class TypeRegistrar {
        static TypeRegistrar* first; // = 0
        TypeRegistrar* next;
        std::string name;

        TypeRegistrar( const TypeRegistrar& ); void operator=( const TypeRegistrar& ); // тел нет
    public:
        TypeRegistrar( const char* TypeName ) : name( TypeName ) 
        {
             assert( name != 0 );
             next = first;
             first = this;
        }
        static bool HasName( const char* name ) 
        {
             assert( name != 0 );
             for( TypeRegistrar* cur = fisrt; cur != 0; cur = cur->next ) {
                  if( cur->name == name ) {
                      return true;
                  }
             }
             return false;
        }
        static const char* GetAnyName()
        {
             assert( first != 0 );
             return first->name.c_str();
        }
    };
private:
    struct I {
        virtual ~I() {}
        virtual const void* Get() = 0;
        virtual I* Clone() = 0;
    };
    template<typename T> struct IT : I {
        virtual ~I() {}
        virtual const void* Get() { return &data; }
        virtual I* Clone() { return new IT( *this ); }
    private:
        T data;
    };

    const char* typeName;
    I* data

};


Ну и пишешь дальше вне всех функций, например там, где объявляешь MyType, ещё и так:
MyAny::TypeRegistrar<MyType> registrarMyType( "MyType" ); // для этого можно сделать макрос
А потом в какой-то функции пишешь:
void foo( MyType& x )
{
    SendMyMessage( MessageId, MyAny::Create( x ) );
}
а в обработчике пишешь:
void processMyMessage( MyAny any )
{
    MyType t = any.Get<MyType>();
   // текст обработчика
}



AM>А вообще, потребность в этом не ошибка проектирования?

Ну это от задачи зависит. иногда плагинам нужен сколь угодно расширяемый интерфейс, например.
Зачем тебе вообще сообщения понадобились?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Re: Передача объекта произвольного типа.
От: Vamp Россия  
Дата: 14.04.11 12:46
Оценка:
AM>Возникли вопросы про сабжу. Как это можно сделать и вообще правильно ли это? По мне так такая штука очень полезная, например есть набор модулей с общим интерфейсом, которые общаются через сообщения, но данные в сообщениях могут быть разного типа и их количество(типов данных) может со временем расти или в том же обзёрвере в его push варианте.

Я выскажу свое имхо. Как правило, с переданным объектом надо что-то делать, правильно? Вот это что-то напрашивается реализовать в виде виртуальных функций и передавать ссылку (указатель) на общего предка.
Да здравствует мыло душистое и веревка пушистая.
Re[2]: Передача объекта произвольного типа.
От: BulatZiganshin  
Дата: 14.04.11 19:27
Оценка:
Здравствуйте, Vamp, Вы писали:

V>Я выскажу свое имхо. Как правило, с переданным объектом надо что-то делать, правильно? Вот это что-то напрашивается реализовать в виде виртуальных функций и передавать ссылку (указатель) на общего предка.


это работает если клиенты зафиксированы и вызывают функции только из определённого набора. когда новые функции могут появляться и на серверной, и на клиентской стороне, это не работает
Люди, я люблю вас! Будьте бдительны!!!
Re[3]: Передача объекта произвольного типа.
От: Vamp Россия  
Дата: 14.04.11 19:30
Оценка:
BZ>это работает если клиенты зафиксированы и вызывают функции только из определённого набора. когда новые функции могут появляться и на серверной, и на клиентской стороне, это не работает
Не понял, ясное дело, если появляются новые функции, надо добавлять функции к коду класса, и добавлять код их вызова к коду клиента. А как иначе?
Да здравствует мыло душистое и веревка пушистая.
Re[4]: Передача объекта произвольного типа.
От: BulatZiganshin  
Дата: 14.04.11 19:41
Оценка:
Здравствуйте, Vamp, Вы писали:

BZ>>это работает если клиенты зафиксированы и вызывают функции только из определённого набора. когда новые функции могут появляться и на серверной, и на клиентской стороне, это не работает

V>Не понял, ясное дело, если появляются новые функции, надо добавлять функции к коду класса, и добавлять код их вызова к коду клиента. А как иначе?

а так, что при этом придётся выкинуть существующих клиентов и ждать пока появятся новые. что приемлемо, если это всё — часть одной программы и клиентов немного, но когда тебе нужно поменять десяток классов только потому что в одном из них появился новый параметр у метода — начинаешь понимать, что тут что-то не так
Люди, я люблю вас! Будьте бдительны!!!
Re[5]: Передача объекта произвольного типа.
От: Vamp Россия  
Дата: 14.04.11 19:48
Оценка:
BZ>а так, что при этом придётся выкинуть существующих клиентов и ждать пока появятся новые.
Не понял, зачем выкидывать клиентов-то?
Да здравствует мыло душистое и веревка пушистая.
Re[6]: Передача объекта произвольного типа.
От: BulatZiganshin  
Дата: 14.04.11 19:50
Оценка:
Здравствуйте, Vamp, Вы писали:

BZ>>а так, что при этом придётся выкинуть существующих клиентов и ждать пока появятся новые.

V>Не понял, зачем выкидывать клиентов-то?

затем, что они несовместимы со старым интерфейсом, в который добавлен новый параметр
Люди, я люблю вас! Будьте бдительны!!!
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.