А generic-и так могут?
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 29.05.05 20:25
Оценка: 5 (1)
Уважаемые коллеги!

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

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

Я хотел бы узнать, можно ли подобные вещи вытворять с помощью generic-ов в C# и/или Java. И если можно, насколько это выглядит проще/читабельнее и/или эффективнее.



Задача в том, что есть два механизма доставки получателям сообщений. Один синхронный (под названием mbapi), второй -- асинхронный (под названием SObjectizer). Был создан модуль, позволяющий строить преобразования синхронных сообщений mbapi в асинхронные сообщения SObjectizer-а. Для этого, каждому сообщению mbapi нужно было сопоставить специальный объект-почтальон (postman), который преобразовывает экземпляр асинхронного mbapi-сообщения, в экземпляр асинхронного сообщения и производит отсылку SObjectizer-сообщения получателю.

Для удобства использования всей этой кухни было сделано несколько вспомогательных шаблонных класса. Один из них, so_postman_templ_t был базовым шаблоном для почтальона. Он требовал, чтобы его параметризовали типом сообщения, у которого есть конструктор с тремя аргументами:
Sobj_type(
    // Адрес получателя сообщения.
    To_type * to,
    // Само сообщение.
    Mbapi_type * msg,
    // Адрес отправителя сообщения (обратный адрес).
    Reply_to_type * reply_to );


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

Важно отметить, что сперва требовалось оперировать всего тремя параметрами сообщения: адресами отправителя и получателя, и самим сообщением. Но в один прекрасный момент, когда по старой схеме работало уже много чего, возникла необходимость сопоставить сообщению дополнительную, расширенную информацию. Т.е., потребовалось поддержать SObjectizer-сообщения, конструктор которых имеет вид:
Sobj_type(
    To_type * to,
    Mbapi_type * msg,
    Reply_to_type * reply_to,
    std::auto_ptr< mbapi_3::extended_info_t > extended_info );

т.е., в зависимости от того, какие параметры шаблона использованы для инстанцирования so_postman_templ_t, код so_postman_templ_t должен создавать объекты типа Sobj_type используя либо конструктор с тремя параметрами, либо конструктор с четырьмя параметрами. Причем, в коде so_postman_templ_t нельзя было написать простой if и использовать в разных его ветках разные вызовы конструкторов. Ведь тогда компилятор и линкер требовали бы наличия у типа Sobj_type сразу обоих конструкторов. А вся задача состояла в том, чтобы новая версия so_postman_templ_t умела работать со старыми типами, у которых конструкторы всего с тремя параметрами.

У меня на C++ шаблонах получилось приведенное ниже решение. Хочу сделать два отступления. Во-первых, требовалось обеспечить совместимость с VC++ 6.0, в котором нет поддержки частичной специализации. Из-за этого пришлось ввести дополнительные типы: sender_params_t, no_extended_info_support_sender_t, extended_info_support_sender_t, sender_selector_t. Если бы можно было применить частную специализацию, то можно было обойтись всего одним типом и его частичной специализацией. И решение бы сильно сократилось в объеме.
Во-вторых, я понимаю, что ошибка была допущена в самом начале, когда было решено передавать в конструктор Sobj_type всю имеющуюся информацию в виде отдельных параметров. Проблем бы не было, если бы передавался всего один аргумент -- некая структура, из которой можно было бы извлечь все. Да, такая ошибка была мной допущена. Но, она была выявлена после двух лет успешной эксплуатации, когда начали меняться требования к имеющемуся механизму. И вот в таких неприятных, но реальных, условиях шаблоны C++ позволили обеспечить работоспособность уже написанного кода.

Вот, собственно, само решение:
//
// extended_info_t
//

/*!
    \since v.3.3.0
    \brief Расширеная информация об MBAPI-сообщении, которая доступна
        для получателя MBAPI-сообщения.

    Большинству получателей MBAPI-сообщений необходимо всего три значения:
    пункт назначения сообщения, само сообщение и его обратный адрес.
    Но в версии 3.3.0 получателю предоставляется так же возможность получить
    историю ремаршрутизаций сообщения. В следующих версиях получатель может
    захотеть получать еще какую-то информацию о сообщении. Для того, чтобы
    не модифицировать для этого шаблон so_templ_postman_t предлагается
    всю дополнительную информацию передавать получателю в виде структуры
    extended_info_t, которая будет расширятся дополнительными атрибутами по
    мере надобности.
*/
struct    extended_info_t
    {
        //! История ремаршрутизации сообщений.
        std::auto_ptr< rerouting_history_t >    m_rerouting_history;

        //! Инициализирующий конструктор.
        /*!
            Самостоятельно извлекает всю информацию из delivery_info_t.
        */
        extended_info_t( const delivery_info_t & info )
            :    m_rerouting_history( info.rerouting_history() ?
                    new rerouting_history_t( *info.rerouting_history() ) : 0 )
            {}
        ~extended_info_t()
            {}
        
        /*! \name Методы getter-ы.
            \{ */
        const rerouting_history_t *
        rerouting_history() const { return m_rerouting_history.get(); }
        /*! \} */
    };

namespace impl
{

//
// sender_params_t
//

/*!
    \since v.3.3.0
    \brief Вспомогательная обертка для параметров доставки MBAPI-сообщения
        в виде SObjectizer сообщения.

    Необходима, главным образом, для того, чтобы сделать вспомогательные
    классы no_extended_info_support_sender_t и extended_info_support_sender_t
    не шаблонными -- это необходимо для поддержки C++ компилятров, в которых
    нет частичной специализации шаблонов.
*/
template<
        class Mbapi_type,
        class Sobj_type,
        class To_type,    
        class Reply_to_type >
struct    sender_params_t
    {
        /*! \name Псевдонимы для параметров шаблона.
            \{ */
        typedef Mbapi_type    mbapi_type_t;
        typedef Sobj_type    sobj_type_t;
        typedef To_type    to_type_t;
        typedef Reply_to_type    reply_to_type_t;
        /*! \} */

        //! Исходные параметры MBAPI-сообщения.
        const delivery_info_t &    m_info;

        //! Инициализирующий конструктор.
        sender_params_t( const delivery_info_t & info )
            :    m_info( info )
            {}

        //! Доступ к параметрам MBAPI-сообщения.
        const delivery_info_t &
        info() const { return m_info; }
    };

//
// no_extended_info_support_sender_t
//

/*!
    \since v.3.3.0
    \brief Генератор SObjectizer-сообщения, для случая, когда конструктор
        сообщения получает всего три параметра.
*/
struct    no_extended_info_support_sender_t
    {
        //! Шаблонный метод для генерации SObjectizer-сообщения.
        /*!
            \note В качестве параметра шаблона ожидается sender_params_t.
        */
        template< class Params >
        void
        send(
            //! Имя владельца сообщения.
            const std::string & owner_name,
            //! Имя SObjectizer-сообщения.
            const std::string & msg_name,
            //! Все доступные параметры MBAPI-сообщения.
            const Params & p ) const
            {
                // Для защиты от исключений при выделении памяти.
                std::auto_ptr< Params::to_type_t > to(
                        dynamic_cast< typename Params::to_type_t * >(
                                p.info().to().clone() ) );
                std::auto_ptr< Params::mbapi_type_t > msg(
                        dynamic_cast< typename Params::mbapi_type_t * >(
                                p.info().msg().clone() ) );
                std::auto_ptr< Params::reply_to_type_t > reply_to(
                        dynamic_cast< typename Params::reply_to_type_t * >(
                                p.info().reply_to().clone() ) );

                so_4::api::send_msg_safely( owner_name, msg_name,
                        new typename Params::sobj_type_t(
                                to.release(),
                                msg.release(),
                                reply_to.release() ),
                        "", p.info().delay() );
            }
    };

//
// extended_info_support_sender_t
//

/*!
    \since v.3.3.0
    \brief Генератор SObjectizer-сообщения, для случая, когда конструктор
        сообщения получает четвертым параметром rerouting_history.
*/
struct    extended_info_support_sender_t
    {
        //! Шаблонный метод для генерации SObjectizer-сообщения.
        /*!
            \note В качестве параметра шаблона ожидается sender_params_t.
        */
        template< class Params >
        void
        send(
            //! Имя владельца сообщения.
            const std::string & owner_name,
            //! Имя SObjectizer-сообщения.
            const std::string & msg_name,
            //! Все доступные параметры MBAPI-сообщения.
            const Params & p ) const
            {
                // Для защиты от исключений при выделении памяти.
                std::auto_ptr< Params::to_type_t > to(
                        dynamic_cast< typename Params::to_type_t * >(
                                p.info().to().clone() ) );
                std::auto_ptr< Params::mbapi_type_t > msg(
                        dynamic_cast< typename Params::mbapi_type_t * >(
                                p.info().msg().clone() ) );
                std::auto_ptr< Params::reply_to_type_t > reply_to(
                        dynamic_cast< typename Params::reply_to_type_t * >(
                                p.info().reply_to().clone() ) );
                std::auto_ptr< extended_info_t > extended(
                        new extended_info_t( p.info() ) );

                so_4::api::send_msg_safely( owner_name, msg_name,
                        new typename Params::sobj_type_t(
                                to, msg, reply_to, extended ),
                        "", p.info().delay() );
            }
    };

//
// sender_selector_t
//

/*!
    \since v.3.3.0
    \brief Вспомогательный класс для выбора одного из sender-ов.

    Данная версия одновременно является специализацией для случая,
    когда Extended_info_supported == false.
*/
template< bool Extended_info_supported >
struct    sender_selector_t
    {
        typedef no_extended_info_support_sender_t    sender_type_t;
    };

/*!
    \since v.3.3.0

    Специализация sender_selector_t для случая, когда
    Extended_info_suppored == true.
*/
template<>
struct    sender_selector_t< true >
    {
        typedef extended_info_support_sender_t    sender_type_t;
    };

} /* namespace impl */

//
// so_postman_templ_t
//

//! Реализация интерфейса %postman_t в виде шаблона.
/*!

    \par Версии, предшествующие 3.3.0
    Предназначен для генерации SObjectizer-сообщений,
    которые имеют конструктор следующего вида:
\code
Sobj_type(
    To_type * to,
    Mbapi_type * msg,
    Reply_to_type * reply_to );
\endcode

    Например:
\code
struct    msg_receiver_ack_t
{
    std::auto_ptr< mbapi_3::dest_t > m_to;
    std::auto_ptr< mbapi_3::sms::receive_ack_t > m_msg;
    std::auto_ptr< mbapi_3::dest_t > m_reply_to;

    msg_receiver_ack_t() {}
    msg_receiver_ack_t(
        mbapi_3::dest_t * to,
        mbapi_3::sms::receive_ack_t * msg,
        mbapi_3::dest_t * reply_to )
        :    m_to( to )
        ,    m_msg( msg )
        ,    m_reply_to( reply_to )
        {
        }
};

so_postman_templ_t<
        mbapi_3::sms::receive_ack_t,
        msg_receive_ack_t >
    receive_ack_postman(
        "some_agent_name",
        "msg_receive_ack" );
\endcode

    \par Начиная с версии 3.3.0 поддерживаются SObjectizer-сообщения,
    конструктор который имеет следующий вид:
\code
Sobj_type(
    std::auto_ptr< To_type > to,
    std::auto_ptr< Mbapi_type > msg,
    std::auto_ptr< Reply_to_type > reply_to,
    std::auto_ptr< mbapi_3::extended_info_t > extended_info );
\endcode
*/
template<
    // Тип MBAPI-сообщения.
    class    Mbapi_type,
    // Тип SObjectizer-сообщения.
    class    Sobj_type,
    // Тип адреса получателя сообщения.
    class To_type = mbapi_3::dest_t,
    // Тип адреса отправителя сообщения.
    class    Reply_to_type = mbapi_3::dest_t,
    // Поддерживается ли получателем расширенная информация?
    bool Extended_info_supported = false >
class    so_postman_templ_t :
    public postman_t
    {
    private :
        //! Имя агента-владельца сообщения.
        const std::string    m_owner_agent;
        //! Имя сообщения, которое нужно отослать.
        const std::string    m_msg_name;

        //! Объект, который реально будет осущетвлять доставку сообщения.
        const typename impl::sender_selector_t<
                        Extended_info_supported >::sender_type_t
                m_sender;

    public :
        inline
        so_postman_templ_t(
            //! Имя агента-владельца сообщения.
            const std::string & owner_agent,
            //! Имя сообщения, которое нужно отослать.
            const std::string & msg_name )
            :    m_owner_agent( owner_agent )
            ,    m_msg_name( msg_name )
            {}

        virtual ~so_postman_templ_t()
            {}

        virtual void
        deliver(
            const mbapi_3::router::delivery_info_t & info )
            {
                impl::sender_params_t<
                                Mbapi_type, Sobj_type, To_type, Reply_to_type >
                        params( info );
                m_sender.send( m_owner_agent, m_msg_name, params );
            }
    };

} /* namespace router */

} /* namespace mbapi_3 */


Т.е., фокус в том, что если Extended_info_supported равен false (по умолчанию), то so_postman_templ_t::m_sender имеет тип impl::no_extended_info_support_sender_t и для инстанцирования SObjectizer-сообщения используется конструктор с тремя параметрами. В противном случае m_sender имеет тип impl::extended_info_support_sender_t и для инстанцирования SObjectizer сообщения используется контруктор с четырьмя параметрами. Причем осуществляется все это в compile-time.

Получается, что в старом коде и в том новом коде, в котором не требуется extended_info_t, шаблон so_postman_templ_t применяется без последного параметра Extended_info_supported (принимается значение false). Если же новому коду нужно использовать extended_info_t, то при инстанцировании so_postman_templ_t указывается true в качестве параметра Extended_info_supported.

Важным достоинством шаблонов C++ здесь является то, что если extended_info_t получателю сообщения не нужен, то при генерации SObjectizer сообщения он даже не создается.
... << RSDN@Home 1.1.4 beta 7 rev. 447>>


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re: А generic-и так могут?
От: _FRED_ Черногория
Дата: 30.05.05 08:29
Оценка:
Здравствуйте, eao197, Вы писали:

E>Уважаемые коллеги!


E> Одним из самых полезных для меня качеств языка C++ является, имхо, удобство при сопровождении и расширении ранее написанного кода. Этому способствуют такие возможности, как препроцессор, перегрузка методов и функций, аргументы по умолчанию и шаблоны. Вот как раз о шаблонах для упрощения сопровождения кода и пойдет речь.


E> Не хотелось бы превращать эту тему в очередной холивар по поводу C++ vs C# vs Java. Просто я на днях с помощью шаблонов расширил функциональность одного из модулей. При этом не потребовалась модификация кода, который работал с этим модулем ранее. Ниже я привожу примененное мной решение на C++.


[...skipped...]


Правильно я догадался, что при появлении необходимости передавать _иногда_ дополнительную информацию, пришлось не только расширить so_postman_templ_t, но и добавить несколько типов: no_extended_info_support_sender_t, extended_info_support_sender_t, template< bool Extended_info_supported >
struct sender_selector_t, template<> struct sender_selector_t< true >?
Пытаюсь вот представить себе, что было и что стало.
under « — Tango In Space»,
... << RSDN@Home 1.1.4 beta 7 rev. 461>>
Help will always be given at Hogwarts to those who ask for it.
Re[2]: А generic-и так могут?
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 30.05.05 08:46
Оценка:
Здравствуйте, _FRED_, Вы писали:

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


E>>Уважаемые коллеги!


E>> Одним из самых полезных для меня качеств языка C++ является, имхо, удобство при сопровождении и расширении ранее написанного кода. Этому способствуют такие возможности, как препроцессор, перегрузка методов и функций, аргументы по умолчанию и шаблоны. Вот как раз о шаблонах для упрощения сопровождения кода и пойдет речь.


E>> Не хотелось бы превращать эту тему в очередной холивар по поводу C++ vs C# vs Java. Просто я на днях с помощью шаблонов расширил функциональность одного из модулей. При этом не потребовалась модификация кода, который работал с этим модулем ранее. Ниже я привожу примененное мной решение на C++.


_FR>[...skipped...]


_FR>

_FR>Правильно я догадался, что при появлении необходимости передавать _иногда_ дополнительную информацию, пришлось не только расширить so_postman_templ_t, но и добавить несколько типов: no_extended_info_support_sender_t, extended_info_support_sender_t, template< bool Extended_info_supported >
_FR>struct sender_selector_t, template<> struct sender_selector_t< true >?
_FR>Пытаюсь вот представить себе, что было и что стало.

Да, правильно, эти типы пришлось добавить (вообще, все что было добавлено, помечено в комментариях как \since v.3.3.0)
Предыдущая реализация so_postman_templ_t имела вид:
template<
    // Тип MBAPI-сообщения.
    class    Mbapi_type,
    // Тип SObjectizer-сообщения.
    class    Sobj_type,
    // Тип адреса получателя сообщения.
    class To_type = mbapi_3::dest_t,
    // Тип адреса отправителя сообщения.
    class    Reply_to_type = mbapi_3::dest_t >
class    so_postman_templ_t :
    public postman_t
{
    private :
        //! Имя агента-владельца сообщения.
        const std::string    m_owner_agent;
        //! Имя сообщения, которое нужно отослать.
        const std::string    m_msg_name;

    public :
        inline
        so_postman_templ_t(
            //! Имя агента-владельца сообщения.
            const std::string & owner_agent,
            //! Имя сообщения, которое нужно отослать.
            const std::string & msg_name )
            :
                m_owner_agent( owner_agent ),
                m_msg_name( msg_name )
        {
        }

        virtual ~so_postman_templ_t()
        {
        }

        inline
        virtual void
        deliver(
            const mbapi_3::dest_t & to,
            const mbapi_3::msg_t & msg,
            const mbapi_3::dest_t & reply_to,
            unsigned int delay )
        {
            so_4::rt::msg_auto_ptr_t< Sobj_type >
                msg_to_send( new Sobj_type(
                    dynamic_cast< To_type * >( to.clone() ),
                    dynamic_cast< Mbapi_type * >( msg.clone() ),
                    dynamic_cast< Reply_to_type * >( reply_to.clone() ) )
                );

            msg_to_send.send( m_owner_agent, m_msg_name, "",
                delay );
        }
};
... << RSDN@Home 1.1.4 beta 7 rev. 447>>


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re: А generic-и так могут?
От: AndrewVK Россия http://blogs.rsdn.org/avk
Дата: 31.05.05 16:02
Оценка: +7
Все это здорово, но ради единственно энтузиазма продираться сквозь килобайты кода имхо вряд ли кто станет. Если ты хочешь получить ответ, думаю стоит попытаться вырпазить вопрос ввиде десятка другого строк.
... << RSDN@Home 1.1.4 beta 7 rev. 457>>
AVK Blog
Re[2]: А generic-и так могут? (сокращенный вариант кода)
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 31.05.05 16:34
Оценка:
Здравствуйте, AndrewVK, Вы писали:

AVK>Все это здорово, но ради единственно энтузиазма продираться сквозь килобайты кода имхо вряд ли кто станет. Если ты хочешь получить ответ, думаю стоит попытаться вырпазить вопрос ввиде десятка другого строк.


Попробуем.
Итак, был класс:
template< class Mbapi_type, class Sobj_type, class To_type = mbapi_3::dest_t, class Reply_to_type = mbapi_3::dest_t >
class    so_postman_templ_t :
    public postman_t
{
    private :
                ......
                
        inline
        virtual void
        deliver(
            const mbapi_3::dest_t & to,
            const mbapi_3::msg_t & msg,
            const mbapi_3::dest_t & reply_to,
            unsigned int delay )
        {
            so_4::rt::msg_auto_ptr_t< Sobj_type >
                msg_to_send( new Sobj_type(
                    dynamic_cast< To_type * >( to.clone() ),
                    dynamic_cast< Mbapi_type * >( msg.clone() ),
                    dynamic_cast< Reply_to_type * >( reply_to.clone() ) )
                );

            msg_to_send.send( m_owner_agent, m_msg_name, "",
                delay );
        }
};


Важно то, что метод so_postman_templ_t::deliver вызывал у типа Sobj_type конструктор с тремя параметрами. Который использовался, например, так:
struct    some_message_t { some_message_t( dest_type_t * to, source_message_t * msg, dest_type_t * reply_to ) {...} ... };

new so_postman_templ_t< source_message_t, some_message_t, dest_type_t, dest_type_t >( some_agent, some_message );


Со временем пришлось добавить в некоторых случаях поддержку дополнительной информации, для чего у типов Sobj_type (которые хотели эту дополнительную информацию получить) должен быть конструктор с дополнительным аргументом:
struct another_message_t {
    another_message_t( dest_type_t * to, source_message_t * msg, dest_type_t * reply_to,
        std::auto_ptr< extended_info_t > ) {...}
        ...
};


При этом требовалось чтобы so_postman_templ_t можно было параметризовать как типом, в конструктор которого принимает три агрумента, так и типом, который принимает четыре аргумента:
new so_postman_templ_t< source_message_t, some_message_t, dest_type_t, dest_type_t >( some_agent, some_message )
new so_postman_templ_t< source_message_t, another_message_t, dest_type_t, dest_type_t, true >( some_agent, another_message );


При этом нельзя было в коде so_postman_templ_t написать что-то вроде:
template<
    class Mbapi_type, class Sobj_type, class To_type = mbapi_3::dest_t, class Reply_to_type = mbapi_3::dest_t,
    bool Extended_info_supported = false >
class    so_postman_templ_t :
    public postman_t
{
    private :
                ......
                
        inline
        virtual void
        deliver(
            const mbapi_3::dest_t & to,
            const mbapi_3::msg_t & msg,
            const mbapi_3::dest_t & reply_to,
            unsigned int delay )
        {
                    if( Extended_info_supported )
                        new Sobj_type( /* четыре аргумента */ );
                    else
                        new Sobj_type( /* три аргумента */ );
                        
                    ...
        }
};


Поскольку мне нужно было поддерживать Visual C++, в котором нет частичной специализации, то пришлось сделать сложное решение с несколькими вспомогательными типами:
namespace impl
{

template<
        class Mbapi_type,
        class Sobj_type,
        class To_type,    
        class Reply_to_type >
struct    sender_params_t
    {
        typedef Mbapi_type    mbapi_type_t;
        typedef Sobj_type    sobj_type_t;
        typedef To_type    to_type_t;
        typedef Reply_to_type    reply_to_type_t;
                ...
    };

struct    no_extended_info_support_sender_t
    {
        template< class Params >
        void
        send(
            const std::string & owner_name,
            const std::string & msg_name,
            const Params & p ) const
            {
                            /* Фактически здесь вызывается: */
                            new Sobj_type( /* три параметра */ );
                            ...
            }
    };

struct    extended_info_support_sender_t
    {
        template< class Params >
        void
        send(
            const std::string & owner_name,
            const std::string & msg_name,
            const Params & p ) const
            {
                            /* Фактически здесь вызывается: */
                            new Sobj_type( /* четыре параметра */ );
                            ...
            }
    };

template< bool Extended_info_supported >
struct    sender_selector_t
    {
        typedef no_extended_info_support_sender_t    sender_type_t;
    };

template<>
struct    sender_selector_t< true >
    {
        typedef extended_info_support_sender_t    sender_type_t;
    };

} /* namespace impl */

template<
    class    Mbapi_type,
    class    Sobj_type,
    class To_type = mbapi_3::dest_t,
    class    Reply_to_type = mbapi_3::dest_t,
    bool Extended_info_supported = false >
class    so_postman_templ_t :
    public postman_t
    {
    private :
                /* Вот здесь на этапе компиляции определяется, будет ли вызываться
                    конструктор с тремя параметрами или конструктор с четырьмя параметрами */
        const typename impl::sender_selector_t<
                        Extended_info_supported >::sender_type_t
                m_sender;
                ...
    public :
        virtual void
        deliver(
            const mbapi_3::router::delivery_info_t & info )
            {
                impl::sender_params_t<
                                Mbapi_type, Sobj_type, To_type, Reply_to_type >
                        params( info );
                m_sender.send( m_owner_agent, m_msg_name, params );
            }
    };


Если бы можно было забить на Visual C++ 6.0, то нужно было бы всего лишь сделать частную специализацию so_postman_templ_t для случая, когда Extended_info_supported == true.

А воопрос вообще занимает одну строку: чтобы потребовалось сделать для повторения такого фокуса в C# и/или Java?

PS
Здесь приведен псевдокод. Реальный код и более подробная постановка задачи изложена в исходном посте.
... << RSDN@Home 1.1.4 beta 7 rev. 447>>


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re: А generic-и так могут?
От: Павел Кузнецов  
Дата: 31.05.05 22:57
Оценка: 12 (1)
eao197,

> Для удобства использования всей этой кухни было сделано несколько вспомогательных шаблонных класса. Один из них, so_postman_templ_t был базовым шаблоном для почтальона. Он требовал, чтобы его параметризовали типом сообщения, у которого есть конструктор с тремя аргументами:

>
> Sobj_type(
>     // Адрес получателя сообщения.
>     To_type * to,
>     // Само сообщение.
>     Mbapi_type * msg,
>     // Адрес отправителя сообщения (обратный адрес).
>     Reply_to_type * reply_to );
>


Тпру, дальше дорогу не сделали: generics не умеют "сами" создавать объекты классов с конструкторами, имеющими параметры, поэтому с использованием generics исходный код выглядел бы как-нибудь очень по-другому.
Posted via RSDN NNTP Server 2.0 beta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[2]: А generic-и так могут?
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 01.06.05 04:33
Оценка:
Здравствуйте, Павел Кузнецов, Вы писали:

ПК>eao197,


>> Для удобства использования всей этой кухни было сделано несколько вспомогательных шаблонных класса. Один из них, so_postman_templ_t был базовым шаблоном для почтальона. Он требовал, чтобы его параметризовали типом сообщения, у которого есть конструктор с тремя аргументами:

>>
>> Sobj_type(
>>     // Адрес получателя сообщения.
>>     To_type * to,
>>     // Само сообщение.
>>     Mbapi_type * msg,
>>     // Адрес отправителя сообщения (обратный адрес).
>>     Reply_to_type * reply_to );
>>


ПК>Тпру, дальше дорогу не сделали: generics не умеют "сами" создавать объекты классов с конструкторами, имеющими параметры, поэтому с использованием generics исходный код выглядел бы как-нибудь очень по-другому.


Т.е. при использовании generic-ов пришлось бы, например, чтобы в типе SObj_type был статический метод-фабрика. И создание бы объекта выглядело как-то так:
Sobj_type * instance = Sobj_type::create_instance( to, msg, reply_to );


Такая штука с generic-ами возможна?
... << RSDN@Home 1.1.4 beta 7 rev. 447>>


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re[3]: А generic-и так могут?
От: Павел Кузнецов  
Дата: 01.06.05 15:51
Оценка: 35 (3)
eao197,

> ПК> Тпру, дальше дорогу не сделали: generics не умеют "сами" создавать объекты классов с конструкторами, имеющими параметры, поэтому с использованием generics исходный код выглядел бы как-нибудь очень по-другому.


> Т.е. при использовании generic-ов пришлось бы, например, чтобы в типе SObj_type был статический метод-фабрика. И создание бы объекта выглядело как-то так:

>
> Sobj_type * instance = Sobj_type::create_instance( to, msg, reply_to );
>

> Такая штука с generic-ами возможна?

Так тоже не получится: generics умеют вызывать только (виртуальные) методы интерфейсов. Вот мои мучения вокруг этого: http://rsdn.ru/Forum/Message.aspx?mid=979456
Автор: Павел Кузнецов
Дата: 10.01.05
Вот ссылка на мнение Брюса Эккеля по этому поводу: http://rsdn.ru/Forum/Message.aspx?mid=989922
Автор: Павел Кузнецов
Дата: 17.01.05
Posted via RSDN NNTP Server 2.0 beta
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Re[4]: А generic-и так могут?
От: Геннадий Васильев Россия http://www.livejournal.com/users/gesha_x
Дата: 01.06.05 21:30
Оценка: +3
Здравствуйте, Павел Кузнецов, Вы писали:

ПК>Так тоже не получится: generics умеют вызывать только (виртуальные) методы интерфейсов. Вот мои мучения вокруг этого: http://rsdn.ru/Forum/Message.aspx?mid=979456
Автор: Павел Кузнецов
Дата: 10.01.05
Вот ссылка на мнение Брюса Эккеля по этому поводу: http://rsdn.ru/Forum/Message.aspx?mid=989922
Автор: Павел Кузнецов
Дата: 17.01.05


Мда. Место заменителя C++ остаётся вакантным.
... << RSDN@Home 1.1.4 beta 3 rev. 185>>
Я знаю только две бесконечные вещи — Вселенную и человеческую глупость, и я не совсем уверен насчёт Вселенной. (c) А. Эйнштейн
P.S.: Винодельческие провинции — это есть рулез!
Re[4]: А generic-и так могут?
От: McSeem2 США http://www.antigrain.com
Дата: 02.06.05 02:49
Оценка: 38 (3) +2 :))) :))) :))) :)
Здравствуйте, Павел Кузнецов, Вы писали:

ПК>Так тоже не получится: generics умеют вызывать только (виртуальные) методы интерфейсов. Вот мои мучения вокруг этого: http://rsdn.ru/Forum/Message.aspx?mid=979456
Автор: Павел Кузнецов
Дата: 10.01.05
Вот ссылка на мнение Брюса Эккеля по этому поводу: http://rsdn.ru/Forum/Message.aspx?mid=989922
Автор: Павел Кузнецов
Дата: 17.01.05


Bottom line:
Генерики, что в C#, что в Java — это чисто так, гламурненько. А шаблоны в C++ — это ГОТИЧНО!
McSeem
Я жертва цепи несчастных случайностей. Как и все мы.
Re[4]: А generic-и так могут?
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 02.06.05 07:02
Оценка: +1
Здравствуйте, Павел Кузнецов, Вы писали:

ПК>eao197,


>> Т.е. при использовании generic-ов пришлось бы, например, чтобы в типе SObj_type был статический метод-фабрика. И создание бы объекта выглядело как-то так:

>>
>> Sobj_type * instance = Sobj_type::create_instance( to, msg, reply_to );
>>

>> Такая штука с generic-ами возможна?

ПК>Так тоже не получится: generics умеют вызывать только (виртуальные) методы интерфейсов. Вот мои мучения вокруг этого: http://rsdn.ru/Forum/Message.aspx?mid=979456
Автор: Павел Кузнецов
Дата: 10.01.05


Имхо, мне показалось, что там речь шла немного о другом -- о том, насколько дорого достичь приемлимой производительности с использованием generic-ов в C#. Мне же интересен немного другой аспект generic-ов, я бы сказал, выразительная мошь. Т.е. меня интересует, насколько геморройно (или наоборот) с помощью generic-ов решаются задачи, которые я решал на шаблонах C++ почти без проблем.

Нужно, похоже, самому разобраться с generic-ами в Java (поскольку этот язык я когда-то знал), а затем и как-то с C# (только его я совсем не знаю). И тогда, может быть, что-то проясниться.

Пока я подумал, что на generic-ах можно было бы сделать что-то, похожее на этот вариант на C++:
template< class Mbapi_type, class Sobj_type, class Sobj_type_factory, class To_type, class Reply_to_type >
class    so_postman_templ_t
{
private :
    Sobj_type_factory m_factory;
    ...
public :
    so_postman_templ_t( Sobj_type_factory factory, ... ) : m_factory( factory ), ... { ... }
    
    void deliver(...)
        {
            Sobj_type * message = Sobj_type_factory.create(...);
            ...
        }
};


Да только засомневался что-то, что такое возможно, поскольку Sobj_type_factory так же должен быть шаблоном (ведь он же должен как-то связываться с параметрами Mbapi_type, Sobj_type, To_type, Reply_to_type).
... << RSDN@Home 1.1.4 beta 7 rev. 447>>


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re[4]: А generic-и так могут?
От: alexeiz  
Дата: 02.06.05 08:44
Оценка: +5
"Павел Кузнецов" <5834@users.rsdn.ru> wrote in message
news:1201223@news.rsdn.ru
> Так тоже не получится: generics умеют вызывать только (виртуальные)
> методы интерфейсов. Вот мои мучения вокруг этого:
> http://rsdn.ru/Forum/Message.aspx?mid=979456
Автор: Павел Кузнецов
Дата: 10.01.05


Delegates — типичное решение для этого ограничения generics. В C# они могут быть анонимными. Конечно, будет runtime cost и всё такое, но это уже другой вопрос.

А вообщем, имеет мало смысла пытаться перевести код с templates на generics по причине их фундаментальных различий. Нужно с нуля его разрабатывать, учитывая возможности и ограничения generics.
Posted via RSDN NNTP Server 1.9
Re[5]: А generic-и так могут?
От: alexeiz  
Дата: 02.06.05 08:45
Оценка:
"McSeem2" <12737@users.rsdn.ru> wrote in message
news:1201590@news.rsdn.ru
> Bottom line:
> Генерики, что в C#, что в Java — это чисто так, гламурненько. А
> шаблоны в C++ — это ГОТИЧНО!

leet speak?
Posted via RSDN NNTP Server 1.9
Re[5]: А generic-и так могут?
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 02.06.05 09:43
Оценка:
Здравствуйте, alexeiz, Вы писали:

A>"Павел Кузнецов" <5834@users.rsdn.ru> wrote in message

A>news:1201223@news.rsdn.ru
>> Так тоже не получится: generics умеют вызывать только (виртуальные)
>> методы интерфейсов. Вот мои мучения вокруг этого:
>> http://rsdn.ru/Forum/Message.aspx?mid=979456
Автор: Павел Кузнецов
Дата: 10.01.05


A>Delegates — типичное решение для этого ограничения generics. В C# они могут быть анонимными. Конечно, будет runtime cost и всё такое, но это уже другой вопрос.


A>А вообщем, имеет мало смысла пытаться перевести код с templates на generics по причине их фундаментальных различий. Нужно с нуля его разрабатывать, учитывая возможности и ограничения generics.


Ну это я уже начал понимать. А как нужное мне решение может выглядеть на generic-ах?
... << RSDN@Home 1.1.4 beta 7 rev. 447>>


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re[5]: А generic-и так могут?
От: L.C.R. Россия lj://_lcr_
Дата: 20.06.05 21:37
Оценка:
McSeem2,

ПК>>Так тоже не получится: generics умеют вызывать только (виртуальные) методы интерфейсов. Вот мои мучения вокруг этого: http://rsdn.ru/Forum/Message.aspx?mid=979456
Автор: Павел Кузнецов
Дата: 10.01.05
Вот ссылка на мнение Брюса Эккеля по этому поводу: http://rsdn.ru/Forum/Message.aspx?mid=989922
Автор: Павел Кузнецов
Дата: 17.01.05


MS>Bottom line:

MS>Генерики, что в C#, что в Java — это чисто так, гламурненько. А шаблоны в C++ — это ГОТИЧНО!

Что же ты тогда скажешь про макросы в ЛИСПЕ!?
quicksort =: (($:@(<#[),(=#[),$:@(>#[)) ({~ ?@#)) ^: (1<#)
Re: А generic-и: попытка упростить описание
От: eao197 Беларусь http://eao197.blogspot.com
Дата: 24.06.05 20:23
Оценка:
Еще раз здравствуйте.

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

Итак, существует некий механизм доставки сообщений получателям. В этом механизме есть:
// Базовый класс для сообщений.
class msg_t { ... };

// Базовый класс для пунктов назначений (адресов получателей и отправителей сообщений).
class dest_t { ... };

// Метод для маршрутизации сообщения:
void
route(
    // Куда должно идти сообщение.
    const dest_t & to,
    // Само сообщение.
    const msg_t & msg,
    // От кого идет сообщение.
    const dest_t & reply_to,
    // Еще какие-то параметры, суть которых сейчас не важна
    ... );


Если кому-то нужно было отослать сообщение, то выполнялось это просто, например, так:
route( some_dest_t( "recipient" ), some_message_t( "hello!" ), some_dest_t( "sender" ) );


А вот с получением сообщения было сложнее. Каждый, кто хотел получать сообщения, должен был подписаться на него и зарегистрировать специальный объект-почтальон. Метод route пробегался по всем зарегистрированным получателям и, если находил нужного, то передавал сообщение почтальону.

Почтальоны были двух видов: синхронные, которые сразу инициировали обработку сообщения внутри route, и асинхронные, которые ставили сообщение в очередь объектам-агентам, а сообщение обрабатывалось тогда, когда агент разгребал свою очередь и извлекал оттуда сообщение. Далее речь пойдет именно об асинхронных почтальонах.

Задача асинхронного почтальона в том, чтобы преобразовать тройку значений <to, msg, reply_to> в один динамически созданный объект. Казалось бы, что здесь можно было бы обойтись готовой структурой:
struct    so_message_t
    {
        std::auto_ptr< dest_t >    m_to;
        std::auto_ptr< msg_t >    m_msg;
        std::auto_ptr< dest_t >    m_reply_to;
    };

и сделать одного почтальона, который бы генерировал объекты so_message_t. Но, проблема в том, что агенты хотят работать с конкретными типами пунктов назначений и сообщений. Т.е., чтобы so_message_t было шаблоном:
template< class Msg, class To, class Reply_to >
struct    so_msg_templ_t
    {
        std::auto_ptr< To >    m_to;
        std::auto_ptr< Msg >    m_msg;
        std::auto_ptr< Reply_to >    m_reply_to;
    };

А почтальон бы автоматически приводил бы указатели от базовых типов (dest_t, msg_t) к производным типам (To, Msg, Reply_to).

Собственно такой шаблон сообщения и такой шаблон почтальона был создан. Более того, шаблон почтальона был сделан так, чтобы он не был строго завязан на тип so_msg_templ_t -- реальный тип сообщения задавался параметром. Такая схема отлично работала несколько лет. Но в коде шаблона почтальона был явно зашит вызов конструктора сообщения:
so_postman_templ_t::deliver( /* параметры из route */ )
    {
        ... new Message_type( to.clone(), msg.clone(), reply_to.clone() ) ...
    }

Причем конструктора с тремя параметрами!
За время работы с so_postman_templ_t было сделано много кода и в качестве сообщений для агентов использовались не только инстанции шаблона so_msg_templ_t, но и не шаблонные типы сообщения. Последнее очень важно, т.к. у класса so_msg_templ_t можно изменить конструктор (что и было сделано), а вот у не шаблонных объектов-сообщений сделать это тяжело. И, что еще важно, где-то so_postman_templ_t использовался напрямую, а где-то применялись производные от него типы (так же шаблонные).

Итак, в один прекрасный день потребовалось поддержать сообщения, которые кроме трех основных параметров должны были получать еще один. И встала задача переделать класс so_postman_templ_t так, чтобы он мог либо вызывать конструктор с тремя аргументами, либо конструктор с четырьмя аргументами. Т.е. что-то вроде:
so_postman_templ_t::deliver( /* параметры из route */ )
    {
        if( Extended_info_supported )
            // Используем конструктор с четырьмя параметрами.
            ... new Message_type( to.clone(), msg.clone(), reply_to.clone(), extended_info.clone() ) ...
        else
            // Используем конструктор с тремя параметрами.
            ... new Message_type( to.clone(), msg.clone(), reply_to.clone() ) ...
    }

Где Extended_info_suppored -- это новый параметр шаблона, который по-умолчанию устанавливается в false.
Но именно так записать нельзя, т.к. тогда бы компилятор требовал бы у Message_type обоих конструкторов.
Поэтому пришлось искать решение через специализацию. Не нужна была бы поддержка VC++6.0, то можно было бы сделать частную специализацию so_postman_templ_t для случая, когда Extended_info_supported == true. Но поддерживать VC++6.0 потребовалось, поэтому пришлось использовать вспомогательные типы:
struct    no_extended_info_support_sender_t
    {
            // Используем конструктор с тремя параметрами.
            ... new Message_type( to.clone(), msg.clone(), reply_to.clone() ) ...
    };

struct    extended_info_support_sender_t
    {
            // Используем конструктор с четырьмя параметрами.
            ... new Message_type( to.clone(), msg.clone(), reply_to.clone(), extended_info.clone() ) ...
    };

template< bool Extended_info_supported >
struct    sender_selector_t
    {
        typedef no_extended_info_support_sender_t    sender_type_t;
    };

template<>
struct    sender_selector_t< true >
    {
        typedef extended_info_support_sender_t    sender_type_t;
    };

Вот как раз тип sender_selector_t и его полная специализация для Extended_info_supported==true и определяли, какой конструктор Message_type будет использоваться:
template< class    Mbapi_type, class    Sobj_type, class To_type = mbapi_3::dest_t, class    Reply_to_type = mbapi_3::dest_t,
    bool Extended_info_supported = false >
class    so_postman_templ_t : public postman_t {
    ...
    //! Объект, который реально будет осущетвлять доставку сообщения.
    const typename impl::sender_selector_t<
                    Extended_info_supported >::sender_type_t
            m_sender;
    ...
};

Если Extended_info_supported == false, то у объекта m_sender был тип no_extended_info_support_sender_t и у Message_type вызывался конструктор с тремя параметрами. В противном случае у m_sender был тип extended_info_support_sender_t и у Message_type вызывался конструктор с четырьмя параметрами.

Благодоря фокусу с параметром шаблона по-умолчанию Extended_info_supported весь старый код, в котором было написано что-то типа:
new so_postman_templ_t< some_message_t, some_async_message_t, some_dest_t >( ... );

остался полностью работоспособным.



Надеюсь, что это описание будет доступнее. Полный же код реализованного решения приведен в исходном сообшении.
... << RSDN@Home 1.1.4 beta 7 rev. 447>>


SObjectizer: <микро>Агентно-ориентированное программирование на C++.
Re[2]: А generic-и: попытка упростить описание
От: Sinclair Россия https://github.com/evilguest/
Дата: 24.06.05 21:50
Оценка: 46 (3)
Здравствуйте, eao197, Вы писали:
E>Признаю, что в исходном посте я сделал слишком объемное и сложное описание. Приношу свои извинения.
E>Попробую сделать его попроще.
Спасибо. Я правильно понял, что суть проблемы сводится к следующему:
1. У нас был универсальный класс нетипизированного сообщения
2. У нас был шаблонный класс типизированного сообщения, унаследованный от нетипизированного
3. У нас были нешаблонные классы-потомки нетипизированного сообшения
4. У всех классов были конструкторы с тремя параметрами, что использовалось внутри кода доставки для порождения новых сообщений.

Не вполне понятно, как выглядит теперь сигнатура метода Deliver и откуда брать четвертый параметр для сообщений, у которых его нет. Ну да ладно. Попробуем так угадать.

Итак, как бы это работало на C# 2.0?
Начнем с конца.
Поскольку дженерики не позволяют использовать никаких конструкторов вообще, кроме как конструктора без параметров, то нам пришлось бы придумывать какой-то другой метод создания объектов-сообщений.
Вариант 1 — использовать Reflection.
using System.Reflection;

public class Postman<MessageType> 
{
  public void Deliver(Destination to, Msg msd, Destination replyTo)
    {
        ConstructorInfo messageCtor = typeof(MessageType).GetConstructor(new Type[]{typeof(Destination),typeof(Msg), typeof(Destination)});
        if (messageCtor != null)
            MessageType msg = (MessageType)messageCtor.Invoke(new object[]{to, msg, replyTo};
    }
}

В таком случае все, что нам надо от жизни для реализации ExtendedDeliver — это добавить пару строк:
public class Postman<MessageType> 
{
  public void ExtendedDeliver(Destination to, Msg msd, Destination replyTo, Extended extended)
    {
        MessageType msg = null;
        ConstructorInfo messageCtor = typeof(MessageType).GetConstructor(new Type[]{typeof(Destination),typeof(Msg), typeof(Destination), typeof(Extended)});
        if (messageCtor != null)
            msg = (MessageType)messageCtor.Invoke(new object[]{to, msg, replyTo, extended};
        else // попробуем найти старый конструктор
        {
            messageCtor = typeof(MessageType).GetConstructor(new Type[]{typeof(Destination),typeof(Msg), typeof(Destination)});
            if (messageCtor != null)
                msg = (MessageType)messageCtor.Invoke(new object[]{to, msg, replyTo};
        }
    }
}

Как видим, все получилось и намного компактнее, чем на C++.
Конечно, в реальной жизни такой подход скорее всего будет быстро соптимизирован — уж очень плохо вставлять постоянный поиск конструктора.
Поэтому на практике, для сообщений скорее всего будет применяться фабрика. И приплясываний с бубном не потребуется вообще.
... << RSDN@Home 1.1.4 beta 5 rev. 395>>
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[3]: А generic-и: попытка упростить описание
От: Павел Кузнецов  
Дата: 24.06.05 23:07
Оценка: 14 (1) +1 :)
Sinclair,

> Вариант 1 — использовать Reflection.

>
> public class Postman<MessageType>
> {
>   public void Deliver(Destination to, Msg msd, Destination replyTo)
>     {
>         ConstructorInfo messageCtor = typeof(MessageType).GetConstructor(new Type[]{typeof(Destination),typeof(Msg), typeof(Destination)});
>         if (messageCtor != null)
>             MessageType msg = (MessageType)messageCtor.Invoke(new object[]{to, msg, replyTo};
>     }
> }
>


Очень неплохо Есть, правда, кой-какие недостатки по сравнению с оригинальным решением (пережить, наверное, можно, но неприятно):
  • нет статической проверки типов, все смещается на время исполнения
  • насколько я понимаю, не будут работать конструкторы MessageType с неточным совпадением типов (например, принимающие ссылку на базовый класс)
  • если я не ошибаюсь, не будут работать шаблоны конструкторов MessageType

    >
    > public class Postman<MessageType>
    > {
    >   public void ExtendedDeliver(Destination to, Msg msd, Destination replyTo, Extended extended)
    >     {
    >         MessageType msg = null;
    >         ConstructorInfo messageCtor = typeof(MessageType).GetConstructor(new Type[]{typeof(Destination),typeof(Msg), typeof(Destination), typeof(Extended)});
    >         if (messageCtor != null)
    >             msg = (MessageType)messageCtor.Invoke(new object[]{to, msg, replyTo, extended};
    >         else // попробуем найти старый конструктор
    >         {
    >             messageCtor = typeof(MessageType).GetConstructor(new Type[]{typeof(Destination),typeof(Msg), typeof(Destination)});
    >             if (messageCtor != null)
    >                 msg = (MessageType)messageCtor.Invoke(new object[]{to, msg, replyTo};
    >         }
    >     }
    > }
    >


    Но, вообще

    > Как видим, все получилось и намного компактнее, чем на C++.


    В C++, если бы eao197 не пришлось мучиться с VC++ 6.0, было бы не длиннее.
    Posted via RSDN NNTP Server 2.0 beta
  • Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
    Re[4]: А generic-и: попытка упростить описание
    От: Sinclair Россия https://github.com/evilguest/
    Дата: 25.06.05 00:10
    Оценка:
    Здравствуйте, Павел Кузнецов, Вы писали:

    ПК>Очень неплохо Есть, правда, кой-какие недостатки по сравнению с оригинальным решением (пережить, наверное, можно, но неприятно):

    ПК>
  • нет статической проверки типов, все смещается на время исполнения
    Тут ничего не поделаешь. Можно было бы обойти этот момент, если бы была возможность требовать не только пустой new.
    Максимум удобства, который можно получить — это выполнить поиск конструктора заранее, в статическом инициализаторе. И, соответственно, кинуть исключение. Тогда падение произойдет на очень раннем этапе (в случае удачи — прямо на старте приложения), и вся необходимая информация попадет именно к тому, кому нужно.

    ПК>
  • насколько я понимаю, не будут работать конструкторы MessageType с неточным совпадением типов (например, принимающие ссылку на базовый класс)
    Хороший вопрос... Нет. В доке сказано, что дефолтный биндер, применяемый при поиске мемберов, учитывает все стандартные приведения:

    The following table lists the conversions supported by the default binder.

    Source Type   | Target Type 
    ------------------------
    Any type      | Its base type. 
    Any type      | The interface it implements. 
    Char          | Unt16, UInt32, Int32, UInt64, Int64, Single, Double 
    Byte          | Char, Unt16, Int16, UInt32, Int32, UInt64, Int64, Single, Double 
    SByte         | Int16, Int32, Int64, Single, Double 
    UInt16        | UInt32, Int32, UInt64, Int64, Single, Double 
    Int16         | Int32, Int64, Single, Double 
    UInt32        | UInt64, Int64, Single, Double 
    Int32         | Int64, Single, Double 
    UInt64        | Single, Double 
    Int64         | Single, Double 
    Single        | Double 
    Non-reference | By-reference.


    Кстати, похоже я прогнал. В примечаниях MSDN к Type.GetConstructor приведена такая фраза:

    If the current Type represents a type parameter of a generic type or method, this method always returns null.

    Что, вообще-то, очень странно. Впрочем, существует набор способов это обойти.
    ПК>
  • если я не ошибаюсь, не будут работать шаблоны конструкторов MessageType
    Это я не понял. Ты что имеешь в виду?
    Я-то имел в виду вот что:
    public class MessageType<To, Msg, ReplyTo>
    where 
        To: Destination;
        Msg: Messsage;
        ReplyTo: Destination
    {
        public MessageType(To to, Msg msg, ReplyTo r) // типизированный конструктор 
        {
        }
    }

    Именно этот конструктор мы и ищем.
    ПК>В C++, если бы eao197 не пришлось мучиться с VC++ 6.0, было бы не длиннее.
    Может быть.
    ... << RSDN@Home 1.1.4 beta 5 rev. 395>>
  • Уйдемте отсюда, Румата! У вас слишком богатые погреба.
    Re[5]: А generic-и: попытка упростить описание
    От: Павел Кузнецов  
    Дата: 25.06.05 03:11
    Оценка:
    Здравствуйте, Sinclair, Вы писали:

    ПК>>
  • нет статической проверки типов, все смещается на время исполнения

    S>Тут ничего не поделаешь. Можно было бы обойти этот момент, если бы была возможность требовать не только пустой new.


    Не очень понял, а как бы ты написал требования, если нужно выбрать один из двух конструкторов, а не требовать какой-либо из них?..

    ПК>>
  • если я не ошибаюсь, не будут работать шаблоны конструкторов MessageType

    S>Это я не понял. Ты что имеешь в виду?

    class MyMessageType
    {
    public:
      template<class To, class Msg, class ReplyTo>
      MyMessageType(To const&, Msg const&, ReplyTo const&);
    };

    Такое может быть нужно, чтоб "сфотографировать" полный тип аргументов при инициализации, как, например, это делает boost::shared_ptr.
  • Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
    Re[3]: А generic-и: попытка упростить описание
    От: eao197 Беларусь http://eao197.blogspot.com
    Дата: 25.06.05 08:11
    Оценка:
    Здравствуйте, Sinclair, Вы писали:

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

    E>>Признаю, что в исходном посте я сделал слишком объемное и сложное описание. Приношу свои извинения.
    E>>Попробую сделать его попроще.
    S>Спасибо. Я правильно понял, что суть проблемы сводится к следующему:
    S>1. У нас был универсальный класс нетипизированного сообщения
    S>2. У нас был шаблонный класс типизированного сообщения, унаследованный от нетипизированного
    S>3. У нас были нешаблонные классы-потомки нетипизированного сообшения

    Нет не так.
    Был шаблонный класс типизированного сообщения, который ни от кого не наследовался (so_msg_templ_t).
    Были нешаблонные типизированные сообщения, которые наследовались от шаблонного типизированного сообщения (т.е. от конкретной инстанции so_msg_templ_t).
    Были типизированные сообщения, которые ни от кого не наследовались, но имели конструктор с тремя параметрами.

    Именно такие типы указывались в параметре Message_type шаблону so_postman_templ_t. А использовались они для асинхронной доставки агентам объектов, производных от msg_t. Т.е. была своя иерархия классов, производных от msg_t, каждому типу из которой соответствовал какой-то случай из вышеперечисленных.

    S>4. У всех классов были конструкторы с тремя параметрами, что использовалось внутри кода доставки для порождения новых сообщений.


    Да.

    S>Не вполне понятно, как выглядит теперь сигнатура метода Deliver и откуда брать четвертый параметр для сообщений, у которых его нет. Ну да ладно. Попробуем так угадать.


    Да все элементарно (хотя сомневаюсь, что это красиво с позиций архитектурной красоты). У базового класса postman_t изначально был один виртуальный метод:
    virtual void
    deliver(
        const mbapi_3::dest_t & to,
        const mbapi_3::msg_t & msg,
        const mbapi_3::dest_t & reply_to,
        unsigned int delay ) = 0;


    Затем был добавлен еще один метод:
    virtual void
    deliver( const delivery_info_t & info );


    Который просто вызывал метод deliver(to, msg, reply_to, info). В коде route теперь вызвается deliver(info). Это позволило обеспечить совместимость с кодом, который не использовал so_postman_templ_t, а создавал своих почтальнов, непосредственно наследуясь от postman_t.
    Сам же so_postman_templ_t теперь выполняет свои действия именно в deliver(info).

    Спасибо за пример. Про применимость reflection я знал заранее, но ведь это не строготипизированное решение. Думал, что можно без reflection обойтись.
    ... << RSDN@Home 1.1.4 beta 7 rev. 447>>


    SObjectizer: <микро>Агентно-ориентированное программирование на C++.
    Re[4]: А generic-и: попытка упростить описание
    От: eao197 Беларусь http://eao197.blogspot.com
    Дата: 25.06.05 08:46
    Оценка:
    Два исправления:

    E>Который просто вызывал метод deliver(to, msg, reply_to, delay). В коде route теперь вызвается deliver(info). Это позволило обеспечить совместимость с кодом, который не использовал so_postman_templ_t, а создавал своих почтальнов, непосредственно наследуясь от postman_t.


    E>Спасибо за пример. Про применимость reflection я знал заранее, но ведь это не статически типизированное решение. Думал, что можно без reflection обойтись.
    ... << RSDN@Home 1.1.4 beta 7 rev. 447>>


    SObjectizer: <микро>Агентно-ориентированное программирование на C++.
    Re[4]: А generic-и: попытка упростить описание
    От: Sinclair Россия https://github.com/evilguest/
    Дата: 25.06.05 10:41
    Оценка: 37 (2)
    Здравствуйте, eao197, Вы писали:
    E>Нет не так.
    E>Был шаблонный класс типизированного сообщения, который ни от кого не наследовался (so_msg_templ_t).

    E>Были нешаблонные типизированные сообщения, которые наследовались от шаблонного типизированного сообщения (т.е. от конкретной инстанции so_msg_templ_t).

    E>Были типизированные сообщения, которые ни от кого не наследовались, но имели конструктор с тремя параметрами.
    Ок, попробуем по другому. Поскольку с самого начала нам надо порождать MessageType, то мы пойдем по компонентному пути:
    public abstract class Postman
    {
      // handles the delivery of the message
      public virtual void deliver(Dest to, Msg msg, Dest replyTo);
    }

    Это основа нашего постмена.
    У нас будет обобщенный класс сообщения, устроенный как грабли:
    public class GenericMessage<ToType, MsgType, ReplyToType>
        where ToType: Dest
        where MsgType: Msg
        where ReplyToType: Dest;
    {
    
        public GenericMessage(SomeDestination to, Msg msg, SomeOtherDestination)
        {
          ...
        }
    }

    Поскольку дженерики не позволяют применить ограничения на конструктор с параметрами, воспользуемся фабрикой:
    public delegate MessageType MessageFactory<MessageType, ToType, MsgType, ReplyToType>(ToType to, MsgType msg, ReplyToType replyTo)
        where ToType: Dest
        where MsgType: Msg
        where ReplyToType: Dest;

    Соответственно, в класс почтальона мы передадим экземпляр порождающего делегата:
    public class Postman<MessageType, ToType, MsgType, ReplyToType>: Postman
        where ToType: Dest
        where MsgType: Msg
        where ReplyToType: Dest;
    {
        private MessageFactory<MessageType, ToType, MsgType, ReplyToType> _factory;
        private Queue<MessageType> _queue = new Queue<MessageType>();
    
        public Postman(MessageFactory<MessageType, ToType, MsgType, ReplyToType> factory)
        {
            _factory = factory;
        }
        
        override void deliver(Dest to, Msg msg, Dest replyTo)
        {
            deliver((ToType)to, (MsgType)msg, (ReplyToType)replyTo);
        }
        public virtual void deliver(ToType to, MsgType msg, ReplyToType replyTo)
        {
            _queue.Enqueue(_factory(to, msg, replyTo));
        }
    }

    Сделаем также почтальона по доставке обобщенных сообщений:
    public class GenericPostman<ToType, MsgType, ReplyToType>: Postman<GenericMessage<ToType, MsgType, ReplyToType>, ToType, MsgType, ReplyToType>
    {
      // Factory helper
      public static GenericMessage<ToType, MsgType, ReplyToType> CreateMessage(ToType to, MsgType msg, ReplyToType replyTo)
        {
            return new GenericMessage<ToType, MsgType, ReplyToType>(to, msg, replyTo);
        }
        
        public GenericPostman(): base (CreateMessage)
        {
        }
    }


    Для нетривиальных случаев конструктор нам придется вызывать руками:
    public class SpecificMessageType
    {
      public SpecificMessageType(SomeDestination to, SomeMessageType msg, SomeOtherDestination replyTo)
        {
        ...
        }
    }
    {
      Router.RegisterPostman(new GenericPostman<SomeDestination, SomeMessageType, SomeOtherDestination>()); // это простой случай
        
      Router.RegisterPostman(new Postman<SpecificMessageType, SomeDestination, SomeMessageType, SomeOtherDestination>
            ( new delegate(SomeDestination to, SomeMessageType msg, SomeOtherDestination replyTo)
                {    return new SpecificMessageType(to, msg, replyTo) }
            )
        );
    }


    Итак, все готово.
    А теперь попробуем дополнить конструкторы сообщений четвертым параметром.
    Вот дополнительный код:

    public class GenericMessage<ToType, MsgType, ReplyToType, ExtendedDataType>
        where ToType: Dest
        where MsgType: Msg
        where ReplyToType: Dest;
    {
    
        public GenericMessage(SomeDestination to, Msg msg, SomeOtherDestination to, ExtendedDataType ext)
        {
          ...
        }
    }
    
    public delegate MessageType MessageFactory<MessageType, ToType, MsgType, ReplyToType, ExtendedDataType>(ToType to, MsgType msg, ReplyToType replyTo, ExtendedDataType ext)
        where ToType: Dest
        where MsgType: Msg
        where ReplyToType: Dest;


    Теперь нам понадобится новый почтальон, умеющий работать с новыми параметрами:
    public class Postman<MessageType, ToType, MsgType, ReplyToType, ExtendedDataType>: Postman
        where ToType: Dest
        where MsgType: Msg
        where ReplyToType: Dest;
    {
        private MessageFactory<MessageType, ToType, MsgType, ReplyToType, ExtendedDataType> _factory;
        private Queue<MessageType> _queue = new Queue<MessageType>();
    
        public Postman(MessageFactory<MessageType, ToType, MsgType, ReplyToType, ExtendedDataType> factory)
        {
            _factory = factory;
        }
        
        override void deliver(DeliveryInfo info)
        {
            _queue.Enqueue(_factory(info.to, info.msg, info.replyTo, info.extendedData));
        }
    }

    новый GenericPostman мы получим тем же способом. Также надо будет поменять базового Postman также, как у тебя. Чтобы все четыре его наследника работали корректно.
    Вроде вот так.
    ... << RSDN@Home 1.1.4 beta 5 rev. 395>>
    Уйдемте отсюда, Румата! У вас слишком богатые погреба.
    Re[5]: А generic-и: попытка упростить описание
    От: eao197 Беларусь http://eao197.blogspot.com
    Дата: 25.06.05 11:01
    Оценка:
    Здравствуйте, Sinclair

    Спасибо. Интересно.
    Но, как и в C++, не слишком компактно.
    ... << RSDN@Home 1.1.4 beta 7 rev. 447>>


    SObjectizer: <микро>Агентно-ориентированное программирование на C++.
    Re[5]: А generic-и: попытка упростить описание
    От: eao197 Беларусь http://eao197.blogspot.com
    Дата: 25.06.05 11:12
    Оценка:
    Здравствуйте, Sinclair


    Проясни мне одну вещь. Вот был у меня GenericPostman от трех параметров. И использовал я его так, как ты показал:
    Router.RegisterPostman(new GenericPostman<SomeDestination, SomeMessageType, SomeOtherDestination>()); // это простой случай


    А затем у нас появляется GenericPostman с четырьмя шаблонными параметрами. Останется ли приведенная выше запись неизменной или мне придется переписывать:
    Router.RegisterPostman(
        new GenericPostman<
                SomeDestination, SomeMessageType, SomeOtherDestination, NoExtendedInfo >()); // это простой случай


    Если придется переписывать, то я так понимаю, мне потребуется оставить GenericPostman как есть и создать ExtendedGenericPostman, который будет уже зависеть от четырех шаблонных параметров?

    Кстати, в C#-generic-ах может ли быть параметром шаблона скаляр (как в моем случае true/false)?
    ... << RSDN@Home 1.1.4 beta 7 rev. 447>>


    SObjectizer: <микро>Агентно-ориентированное программирование на C++.
    Re[6]: А generic-и: попытка упростить описание
    От: Sinclair Россия https://github.com/evilguest/
    Дата: 25.06.05 11:22
    Оценка:
    Здравствуйте, eao197, Вы писали:
    E>Спасибо. Интересно.
    E>Но, как и в C++, не слишком компактно.
    На самом деле проблема именно в том, что не хватает возможности ограничивать сигнатуру конструктора.

    Да, в реальном приложении код мог бы быть значительно более компактным. Я просто не очень хорошо себе представляю задачу — зело велика. Если бы приложение с самого начала проектировалось под С#, то в нем возможно вообще бы были применены другие средства. Я попытался написать 1:1 отображение исходной архитектуры + уродливые костыли для замены отсутствующей функциональности.
    ... << RSDN@Home 1.1.4 beta 5 rev. 395>>
    Уйдемте отсюда, Румата! У вас слишком богатые погреба.
    Re[6]: А generic-и: попытка упростить описание
    От: Sinclair Россия https://github.com/evilguest/
    Дата: 25.06.05 11:42
    Оценка:
    Здравствуйте, eao197, Вы писали:
    E>Проясни мне одну вещь. Вот был у меня GenericPostman от трех параметров. И использовал я его так, как ты показал:
    E>
    E>Router.RegisterPostman(new GenericPostman<SomeDestination, SomeMessageType, SomeOtherDestination>()); // это простой случай
    E>

    Угу.
    E>А затем у нас появляется GenericPostman с четырьмя шаблонными параметрами. Останется ли приведенная выше запись неизменной или мне придется переписывать:
    Останется. Все в порядке. Дженерики с разным числом аргументов — разные дженерики.
    E>Кстати, в C#-generic-ах может ли быть параметром шаблона скаляр (как в моем случае true/false)?
    Увы, нет. Позволено только типы.
    ... << RSDN@Home 1.1.4 beta 5 rev. 395>>
    Уйдемте отсюда, Румата! У вас слишком богатые погреба.
    Re[7]: А generic-и: попытка упростить описание
    От: eao197 Беларусь http://eao197.blogspot.com
    Дата: 25.06.05 11:42
    Оценка:
    Здравствуйте, Sinclair, Вы писали:

    S>Да, в реальном приложении код мог бы быть значительно более компактным. Я просто не очень хорошо себе представляю задачу — зело велика.


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

    S> Если бы приложение с самого начала проектировалось под С#, то в нем возможно вообще бы были применены другие средства.


    Так это понятно. Мне просто интересно было, можно ли трюки с C++ шаблонами применять в C#/Java generic-ах.
    ... << RSDN@Home 1.1.4 beta 7 rev. 447>>


    SObjectizer: <микро>Агентно-ориентированное программирование на C++.
    Re[7]: А generic-и: попытка упростить описание
    От: eao197 Беларусь http://eao197.blogspot.com
    Дата: 25.06.05 12:09
    Оценка:
    Здравствуйте, Sinclair, Вы писали:

    E>>А затем у нас появляется GenericPostman с четырьмя шаблонными параметрами. Останется ли приведенная выше запись неизменной или мне придется переписывать:

    S>Останется. Все в порядке. Дженерики с разным числом аргументов — разные дженерики.

    Но тогда получается, что у меня есть две параллельные системы классов и фабрик:
    GenericMessage<To,Msg,ReplyTo>
    MessageFactory<MessageType,To,Msg,ReplyTo>
    Postman<MessageType,To,Msg,ReplyTo>
    GenericPostman<To,Msg,ReplyTo>

    и
    GenericMessage<To,Msg,ReplyTo,Extended>
    MessageFactory<MessageType,To,Msg,ReplyTo,Extended>
    Postman<MessageType,To,Msg,ReplyTo,Extended>
    GenericPostman<To,Msg,ReplyTo,Extended>


    Причем их основной код будет одним и тем же. И если мне потребуется что-то исправить или доделать, то делать это нужно будет в каждой из систем независимо друг от друга (естественно, попытавшись что-то вынести в общий базовый класс)?
    ... << RSDN@Home 1.1.4 beta 7 rev. 447>>


    SObjectizer: <микро>Агентно-ориентированное программирование на C++.
    Re[8]: А generic-и: попытка упростить описание
    От: Sinclair Россия https://github.com/evilguest/
    Дата: 25.06.05 20:13
    Оценка: +1
    Здравствуйте, eao197, Вы писали:

    E>Но тогда получается, что у меня есть две параллельные системы классов и фабрик:

    E>
    E>GenericMessage<To,Msg,ReplyTo>
    E>MessageFactory<MessageType,To,Msg,ReplyTo>
    E>Postman<MessageType,To,Msg,ReplyTo>
    E>GenericPostman<To,Msg,ReplyTo>
    E>

    E>и
    E>
    E>GenericMessage<To,Msg,ReplyTo,Extended>
    E>MessageFactory<MessageType,To,Msg,ReplyTo,Extended>
    E>Postman<MessageType,To,Msg,ReplyTo,Extended>
    E>GenericPostman<To,Msg,ReplyTo,Extended>
    E>

    Верно.
    E>Причем их основной код будет одним и тем же.
    Нет.
    E> И если мне потребуется что-то исправить или доделать, то делать это нужно будет в каждой из систем независимо друг от друга (естественно, попытавшись что-то вынести в общий базовый класс)?
    Вряд ли. Основная часть кода, который спрятан в этих классах — оберточная. Там практически нечего исправлять. Скорее тебе захочется ввести еще пару параллельных классов.
    Еще раз намекаю, что решение было получено из прямого переноса плюсовых практик. Системы доставки сообщений на C# проектируются не так.
    ... << RSDN@Home 1.1.4 beta 5 rev. 395>>
    Уйдемте отсюда, Румата! У вас слишком богатые погреба.
    Re[5]: А generic-и: а еще дальше?
    От: eao197 Беларусь http://eao197.blogspot.com
    Дата: 25.06.05 20:52
    Оценка:
    Здравствуйте, Sinclair, Вы писали:

    S>
    S>  Router.RegisterPostman(new GenericPostman<SomeDestination, SomeMessageType, SomeOtherDestination>()); // это простой случай
        
    S>  Router.RegisterPostman(new Postman<SpecificMessageType, SomeDestination, SomeMessageType, SomeOtherDestination>
    S>        ( new delegate(SomeDestination to, SomeMessageType msg, SomeOtherDestination replyTo)
    S>            {    return new SpecificMessageType(to, msg, replyTo) }
    S>        )
    S>    );
    S>}
    S>


    Хочу задать ламерский вопрос, но я не знаю, есть ли в C# понятие typedef или alias, чтобы можно было дать псевдоним сложной конструкции.
    Вот Sinclair показал, как с помощью generic-ов можно регистрировать почтальонов с привязкой к конкретным типам. Но, сигнатура GenericPostman-а в программе будет дублироваться: один раз, как показано выше, при регистрации почтальона. А второй раз при описании обработчика сообщения в агенте:
    class    SomeAgent
    {
        public void receiveSomeMessage( GenericMessage< SomeDestination, SomeMessageType, SomeOtherDestination > msg )
            { ... }
    };


    Имхо, такое дублирование не есть good. В C++ я избавился от него при помощи двух простых трюков. Во-первых, в шаблоне сообщения просто сделал typedef-ы:
    template< class Message, class To, class ReplyTo, bool Extended_info_required = false >
    struct so_msg_templ_t
        {
            typedef Message    message_type_t;
            typedef To    to_type_t;
            typedef ReplyTo    reply_to_type_t;
            enum { extended_info_required = Extended_info_required };
            ...
        };


    Во-вторых, сделал шаблонный класс почтальона, который зависит только от одного шаблонного параметра:
    template< class So_message >
    class    so_msg_templ_postman_t
        :    public postman_t
        {
            // Агрегируем функциональность ранее созданного шаблона so_postman_templ_t.
            typedef so_postman_templ_t<
                            // Вот здесь берем псевдонимы из So_message.
                            typename So_message::message_type_t,
                            So_message,
                            typename So_message::to_type_t,
                            typename So_message::reply_to_type_t,
                            So_message::extended_info_required >
                    real_postman_t;
                    
            real_postman_t    m_postman;
            ...
        };


    Что позволило мне указывать список шаблонных параметров всего в одном месте программы:
    typedef so_msg_templ_t< some_message_t, some_dest_t, some_another_dest_t > my_message_t;
    
    so_msg_templ_postman_t< my_message_t > my_postman;
    
    void
    some_agent_t::evt_receive_my_message( const my_message_t & msg ) {...}



    А вот такие фокусы с generic-ами возможны?
    ... << RSDN@Home 1.1.4 beta 7 rev. 447>>


    SObjectizer: <микро>Агентно-ориентированное программирование на C++.
    Re[6]: А generic-и: а еще дальше?
    От: AndrewVK Россия http://blogs.rsdn.org/avk
    Дата: 25.06.05 20:57
    Оценка: 18 (1)
    Здравствуйте, eao197, Вы писали:

    E>А вот такие фокусы с generic-ами возможны?


    Нет. Дженерики принципиально не годятся для кодогенерации, поскольку обязаны быть компилируемыми.
    ... << RSDN@Home 1.2.0 alpha rev. 500>>
    AVK Blog
    Re[7]: А generic-и: а еще дальше?
    От: eao197 Беларусь http://eao197.blogspot.com
    Дата: 25.06.05 21:02
    Оценка:
    Здравствуйте, AndrewVK, Вы писали:

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


    E>>А вот такие фокусы с generic-ами возможны?


    AVK>Нет. Дженерики принципиально не годятся для кодогенерации, поскольку обязаны быть компилируемыми.


    Понятно, только какая же это кодогенерация Это так, ничего особенного
    ... << RSDN@Home 1.1.4 beta 7 rev. 447>>


    SObjectizer: <микро>Агентно-ориентированное программирование на C++.
    Re[6]: А generic-и: а еще дальше?
    От: Sinclair Россия https://github.com/evilguest/
    Дата: 25.06.05 21:14
    Оценка:
    Здравствуйте, eao197, Вы писали:

    E>Хочу задать ламерский вопрос, но я не знаю, есть ли в C# понятие typedef или alias, чтобы можно было дать псевдоним сложной конструкции.

    И да и нет.
    Да — потому, что можно написать
    using IntList = System.Collections.Generic.List<int>;

    Нет — потому, что область видимости такого идентификатора — файл. Т.е. его нельзя спрятать внутрь класса. А это означает, что шаблонных тайпдефов нет.

    E>Вот Sinclair показал, как с помощью generic-ов можно регистрировать почтальонов с привязкой к конкретным типам. Но, сигнатура GenericPostman-а в программе будет дублироваться: один раз, как показано выше, при регистрации почтальона. А второй раз при описании обработчика сообщения в агенте:

    E>А вот такие фокусы с generic-ами возможны?
    Напрямую — нет. Но, имхо, если пошевелить мозгой, то можно получить как минимум для данного конкретного случая корректный результат...
    ... << RSDN@Home 1.1.4 beta 5 rev. 395>>
    Уйдемте отсюда, Румата! У вас слишком богатые погреба.
    Re[3]: А generic-и: попытка упростить описание
    От: VladD2 Российская Империя www.nemerle.org
    Дата: 26.06.05 01:39
    Оценка:
    Здравствуйте, Sinclair, Вы писали:

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

    E>>Признаю, что в исходном посте я сделал слишком объемное и сложное описание. Приношу свои извинения.
    E>>Попробую сделать его попроще.
    S>Спасибо. Я правильно понял, что суть проблемы сводится к следующему:
    S>1. У нас был универсальный класс нетипизированного сообщения
    S>2. У нас был шаблонный класс типизированного сообщения, унаследованный от нетипизированного
    S>3. У нас были нешаблонные классы-потомки нетипизированного сообшения
    S>4. У всех классов были конструкторы с тремя параметрами, что использовалось внутри кода доставки для порождения новых сообщений.

    Незнаю насколько ты угадал. Мне просто влом вникать даже в уменьшенный вариант. Если ты угодал, то можно обойтись и без рефлекшона. Нужно просто ввести делегат заменяющий конструктор:
    delegate Result Creator<Result, T1, T2, T3>(T1 t1, T2 t2, T3 t3);
    
    class Destination { }
    class Msg { }
    class Message
    {
        public Message(Destination to, Msg msd, Destination replyTo)  {  }
    }
    
    class Postman<MessageType>
    {
        public Postman(Creator<MessageType, Destination, Msg, Destination> creator) { _creator = creator; }
    
        Creator<MessageType, Destination, Msg, Destination> _creator;
     
        public void Deliver(Destination to, Msg msd, Destination replyTo)
        {
            MessageType message = _creator(to, msd, replyTo);
        }
    }
    
    class Program
    {
        static void Main(string[] args)
        {
            Postman<Message> postman = new Postman<Message>(
                delegate(Destination to, Msg msd, Destination replyTo) { return new Message(to, msd, replyTo); });
    
            postman.Deliver(new Destination(), new Msg(), new Destination());
        }
    }



    S>Поэтому на практике, для сообщений скорее всего будет применяться фабрика. И приплясываний с бубном не потребуется вообще.


    Вот именно.

    ЗЫ

    Кстати такие навороты с рефлекшоном для динамического создания объектов не нужны. Нужно использовать Activator. Примерно так:
    class Postman<MessageType>
    {
        public void Deliver(Destination to, Msg msd, Destination replyTo)
        {
            MessageType message = (MessageType)Activator.CreateInstance(typeof(MessageType), new object[] { to, msd, replyTo });
        }
    }
    ... << RSDN@Home 1.1.4 beta 7 rev. 466>>
    Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
     
    Подождите ...
    Wait...
    Пока на собственное сообщение не было ответов, его можно удалить.