Шаблоны члены класса, специализация: как лучше сделать?
От: silart  
Дата: 21.12.09 04:33
Оценка:
Добрый день!

Существует следующий класс:

class Static : public boost::noncopyable
{
public:
    /** Соединение между сигналом и слотами.\n */
    typedef boost::signals::connection        connection_t;

    /** Сигнал появления нового сэмпла от весового устройства.\n */
    typedef boost::signal<void (const std::string&)>    Measured;

    /** Сигнал конца взвешивания.\n */
    typedef boost::signal<void (const std::string&)>    Weighed;

    /** Сигнал ошибки.\n */
    typedef boost::signal<void (const std::string&, const std::string&)>    Error;

private:
    ...
    Measured measured;                            ///< Сигнал появления нового сэмпла
    Weighed weighed;                            ///< Сигнал измерения веса 
    Error error;                                ///< Сигнал ошибки

public:
    // ВНИМАНИЕ!!! КОД НЕ КОМПИЛИРУЕТСЯ

    // Очень хотелось бы получить примерно такой код:    
    template<typename T>
    connection_t connect();

    template <>
    connection_t connect<Measured>(boost::function<void (const std::string&)> handler)
    {
        return measured.connect(handler);
    }

    template <>
    connection_t connect<Weighed>(boost::function<void (const std::string&)> handler)
    {
        return weighed.connect(handler);
    }

    template <>
    connection_t connect<Error>(boost::function<void (const std::string&, const std::string&)> handler)
    {
        return error.connect(handler);
    }


В классе имеются сигналы boost, причем их может быть много и их тип может быть как одним и тем же, так и разным.
Проблема состоит в написании шаблона connect().
Дело в том, что типы Measured и Weighed, полученные с помощью typedef, по сути являются синонимами и компилятор не делает различий, когда их передаешь в качестве параметров шаблона.
Извратиться конечно в данном случае можно, но очень хочется решить данную проблему красиво и лаконично, чтобы клиентский код выглядел примерно так:

Static s;
s.connect<Static::Measured>(boost::bind(&X::OnMeasured, this, _1));
s.connect<Static::Weighed>(boost::bind(&X::OnWeighed, this, _1));
s.connect<Static::Error>(boost::bind(&X::OnError, this, _1, _2));


Посоветуйте пожалуйста, как можно решить данную проблему?
Re: Шаблоны члены класса, специализация: как лучше сделать?
От: Caracrist https://1pwd.org/
Дата: 21.12.09 05:58
Оценка:
Здравствуйте, silart, Вы писали:

S>Добрый день!


S>Посоветуйте пожалуйста, как можно решить данную проблему?

может както так?

    typedef struct { boost::signal<void (const std::string&)> value}    Measured;


    typedef struct { boost::signal<void (const std::string&)> value}    Weighed;


    typedef struct { boost::signal<void (const std::string&, const std::string&)> value}    Error;

~~~~~
~lol~~
~~~ Single Password Solution
Re: Шаблоны члены класса, специализация: как лучше сделать?
От: rg45 СССР  
Дата: 21.12.09 06:25
Оценка:
Здравствуйте, silart, Вы писали:

S>Добрый день!


S>Существует следующий класс:

S>...

S>В классе имеются сигналы boost, причем их может быть много и их тип может быть как одним и тем же, так и разным.

S>Проблема состоит в написании шаблона connect().
S>Дело в том, что типы Measured и Weighed, полученные с помощью typedef, по сути являются синонимами и компилятор не делает различий, когда их передаешь в качестве параметров шаблона.

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


S>


S>Static s;

S>s.connect<Static::Measured>(boost::bind(&X::OnMeasured, this, _1));
S>s.connect<Static::Weighed>(boost::bind(&X::OnWeighed, this, _1));
S>s.connect<Static::Error>(boost::bind(&X::OnError, this, _1, _2));
S>

S>Посоветуйте пожалуйста, как можно решить данную проблему?


Начнем с того, что у тебя Measured и Weighed — это один и тот же тип, и ты при этом пытаешься определить две перегрузки одинакового типа. От того, что ты прикрутил сюда еще и специализацию положение никак не улучшилось — все равно получаются две одинаковые функции. И можно изощряться сколько угодно, все равно не удастся завести два разных события одного типа, пользуясь только лишь типом обработчика.

Я бы сделал так:
#include <string>
#include <boost/signals.hpp>
#include <boost/function.hpp>

class Static : public boost::noncopyable
{
public:
    /** Соединение между сигналом и слотами.\n */
    typedef boost::signals::connection connection_t;

    /** Обработчик появления нового сэмпла от весового устройства.\n */
    typedef void MeasuredHandler(const std::string&);

    /** Обработчик конца взвешивания.\n */
    typedef void WeighedHandler(const std::string&);

    /** Обработчик ошибки.\n */
    typedef void ErrorHandler(const std::string&, const std::string&);

private:
    boost::signal<MeasuredHandler> measured;                            ///< Сигнал появления нового сэмпла
    boost::signal<WeighedHandler> weighed;                            ///< Сигнал измерения веса 
    boost::signal<ErrorHandler> error;                                ///< Сигнал ошибки

public:
    connection_t connect_measured(const boost::function<MeasuredHandler>& handler)
    {
        return measured.connect(handler);
    }

    connection_t connect_weighed(const boost::function<WeighedHandler>& handler)
    {
        return weighed.connect(handler);
    }

    connection_t connect_error(const boost::function<ErrorHandler>& handler)
    {
        return error.connect(handler);
    }
};


И клиентский код при этом выглядит не хуже — и компактно и понятно:
Static s;
s.connect_measured(boost::bind(&X::OnMeasured, this, _1));
s.connect_weighed(boost::bind(&X::OnWeighed, this, _1));  
s.connect_error(boost::bind(&X::OnError, this, _1, _2));
--
Справедливость выше закона. А человечность выше справедливости.
Re[2]: Шаблоны члены класса, специализация: как лучше сделат
От: silart  
Дата: 21.12.09 06:33
Оценка:
Здравствуйте, rg45!

Ваше решение всем хорошо, нравится оно мне, но вот бы еще уйти от connect_xxx() и было бы ваще здорово!
Очень хочется, чтобы функции connect() выглядели именно так.

Возможно ли сделать такое?
Re[2]: Шаблоны члены класса, специализация: как лучше сделат
От: Caracrist https://1pwd.org/
Дата: 21.12.09 07:23
Оценка:
Здравствуйте, Caracrist, Вы писали:

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


S>>Добрый день!


S>>Посоветуйте пожалуйста, как можно решить данную проблему?

C>может както так?
C>

C>    typedef struct { boost::signal<void (const std::string&)> value}    Measured;


C>    typedef struct { boost::signal<void (const std::string&)> value}    Weighed;


C>    typedef struct { boost::signal<void (const std::string&, const std::string&)> value}    Error;
C>

C>


Судя по вопросам
Автор: silart
Дата: 21.12.09
, меня либо не заметили, либо не поняли...

class Static : public boost::noncopyable
{
public:
    /** Соединение между сигналом и слотами.\n */
    typedef boost::signals::connection        connection_t;

    /** Сигнал появления нового сэмпла от весового устройства.\n */
    typedef struct { typedef void (*type)(const std::string&); boost::signal<type> value; }   Measured;

    /** Сигнал конца взвешивания.\n */
    typedef struct { typedef void (*type)(const std::string&); boost::signal<type> value; }    Weighed;

    /** Сигнал ошибки.\n */
    typedef struct { typedef void (*type)(const std::string&, const std::string&); boost::signal<type> value;}    Error;

private:
    ...
    Measured measured;                            ///< Сигнал появления нового сэмпла
    Weighed weighed;                            ///< Сигнал измерения веса 
    Error error;                                ///< Сигнал ошибки

public:
    // ВНИМАНИЕ!!! КОД НЕ КОМПИЛИРУЕТСЯ

    // Очень хотелось бы получить примерно такой код:    
    template<typename T>
    connection_t connect(boost::function<typename T::type> handler);

    template <>
    connection_t connect<Measured>(boost::function<Measured::type> handler)
    {
        return measured.connect(handler);
    }

    template <>
    connection_t connect<Weighed>(boost::function<Weighed::type> handler)
    {
        return weighed.connect(handler);
    }

    template <>
    connection_t connect<Error>(boost::function<Error::type> handler)
    {
        return error.connect(handler);
    }
};
~~~~~
~lol~~
~~~ Single Password Solution
Re[3]: Шаблоны члены класса, специализация: как лучше сделат
От: rg45 СССР  
Дата: 21.12.09 09:38
Оценка:
Здравствуйте, silart, Вы писали:

S>Здравствуйте, rg45!


S>Ваше решение всем хорошо, нравится оно мне, но вот бы еще уйти от connect_xxx() и было бы ваще здорово!

S>Очень хочется, чтобы функции connect() выглядели именно так.

S>Возможно ли сделать такое?


Ну так или иначе, каким-то образом прийдется обеспечить уникальность перегрузок/специализаций в условиях, когда сигнатуры обработчиков разных событий могут совпадать. Можно пойти по варианту, предложенному Caracrist-ом здесь
Автор: Caracrist
Дата: 21.12.09
. В этом варианте уникальность перегрузок/специализаций обеспечивается введением дополнительных типов. Сразу нужно подчеркнуть, что автовыведение шаблонных параметров функций connect работать не будет, эти функции всегда прийдется выполнять с явной спецификацией шаблонных параметров:
connect<Measured>(/*...*/);
connect<Weighed>(/*...*/);
connect<Error>(/*...*/);

С учетом необходимости введения дополнительных типов, неужели, это лучше, чем:
connect_measured(/*...*/);
connect_weighed(/*...*/);
connect_error(/*...*/);

???
--
Справедливость выше закона. А человечность выше справедливости.
Re[4]: Шаблоны члены класса, специализация: как лучше сделат
От: silart  
Дата: 23.12.09 03:05
Оценка: 1 (1) +1
Большое спасибо всем откликнувшимся товарищам за конструктивные предложения!

Здравствуйте, rg45, я выбрал ваш вариант. Вот что у меня получилось:

Типы слотов:
public:
    /** Соединение между сигналом и слотами.\n */
    typedef boost::signals::connection        connection_t;

    /** Тип слота появления нового сэмпла от весового устройства.\n */
    typedef void Measured(const std::string&);

    /** Тип слота конца взвешивания.\n */
    typedef void Weighed(const std::string&);

    /** Тип слота ошибки.\n */
    typedef void Error(const std::string&, const std::string&);


Объявление сигналов:
    boost::signal<Measured> measured;            ///< Сигнал появления нового сэмпла
    boost::signal<Weighed> weighed;                ///< Сигнал измерения веса 
    boost::signal<Error> error;                ///< Сигнал ошибки


Функции соединения сигналов со слотами:
    connection_t on_measured(const boost::function<Measured> handler);
    connection_t on_weighed(const boost::function<Weighed> handler);
    connection_t on_error(const boost::function<Error> handler);


Клиентский код:

        m_Weigher = factory().make_Static(TtoA::from(config));

        m_measured_con    = m_Weigher->on_measured(boost::bind(&CoPlatformWeigher::OnMeasured, this, _1));
        m_weighed_con    = m_Weigher->on_weighed(boost::bind(&CoPlatformWeigher::OnWeighed, this, _1));
        m_error_con    = m_Weigher->on_error(boost::bind(&CoPlatformWeigher::OnRuntimeError, this, _1, _2));


Я отказался от слова "connect" в названии функций. Я думаю функция соединения типа on_xxx() выглядит гораздо лучше.
Я считаю выбор названий важным этапом!

Здравствуйте, Caracrist! Ваш вариант всем хорош, но он требует объявления новых типов, связанных с типами слотов вручную:

typedef struct { boost::signal<void (const std::string&)> value}    Measured;


Я попытался применить шаблоны для данной операции, но решение получилось громозким и возникли другие проблемы. Поэтому я выбрал вариант rg45.
Re[5]: Шаблоны члены класса, специализация: как лучше сделат
От: rg45 СССР  
Дата: 23.12.09 06:14
Оценка: 2 (1)
Здравствуйте, silart, Вы писали:

S>Функции соединения сигналов со слотами:

S>
S>    connection_t on_measured(const boost::function<Measured> handler);
S>    connection_t on_weighed(const boost::function<Weighed> handler);
S>    connection_t on_error(const boost::function<Error> handler);
S>


S>Клиентский код:


S>
S>        m_Weigher = factory().make_Static(TtoA::from(config));

S>        m_measured_con    = m_Weigher->on_measured(boost::bind(&CoPlatformWeigher::OnMeasured, this, _1));
S>        m_weighed_con    = m_Weigher->on_weighed(boost::bind(&CoPlatformWeigher::OnWeighed, this, _1));
S>        m_error_con    = m_Weigher->on_error(boost::bind(&CoPlatformWeigher::OnRuntimeError, this, _1, _2));
S>


S>Я отказался от слова "connect" в названии функций. Я думаю функция соединения типа on_xxx() выглядит гораздо лучше.

S>Я считаю выбор названий важным этапом!

По поводу важности выбора названий поддерживаю обеими руками. И хочу изложить свою точку зрения по этому вопросу. При реализации событий (паттерн "Наблюдатель") мы, как минимум, имеем дело со следующими элементами: само событие, обработчик события и операция подписки на событие. Названия событий часто удачно описываются причастиями краткой формы: Measured, Weighed, ErrorOccured и т.д. Форма on_xxx хорошо подходит для названий обработчиков событий: on_measured, on_weighed, on_error. А вот операции подписки на события (т.е. добавление обработчиков событий) я бы описывал в форме: add_xxx_handler: add_measured_handler, add_weighed_handler, add_error_handler. Думаю, что при такой системе код будет пониматься слету без дополнительного документирования.
--
Справедливость выше закона. А человечность выше справедливости.
Re[6]: Шаблоны члены класса, специализация: как лучше сделат
От: silart  
Дата: 23.12.09 08:18
Оценка:
Здравствуйте, rg45, Вы писали:

R>По поводу важности выбора названий поддерживаю обеими руками. И хочу изложить свою точку зрения по этому вопросу. При реализации событий (паттерн "Наблюдатель") мы, как минимум, имеем дело со следующими элементами: само событие, обработчик события и операция подписки на событие. Названия событий часто удачно описываются причастиями краткой формы: Measured, Weighed, ErrorOccured и т.д. Форма on_xxx хорошо подходит для названий обработчиков событий: on_measured, on_weighed, on_error. А вот операции подписки на события (т.е. добавление обработчиков событий) я бы описывал в форме: add_xxx_handler: add_measured_handler, add_weighed_handler, add_error_handler. Думаю, что при такой системе код будет пониматься слету без дополнительного документирования.


Спасибо за дельные советы!

Вот что у меня получилось:

Типы слотов:

    /** Соединение между сигналом и слотами.\n */
    typedef boost::signals::connection        connection_t;

    /** Тип слота появления нового сэмпла от весового устройства.\n */
    typedef void Measured(const std::string&);

    /** Тип слота конца взвешивания.\n */
    typedef void Weighed(const std::string&);

    /** Тип слота ошибки.\n */
    typedef void Failed(const std::string&, const std::string&);


Объявление сигналов:

    boost::signal<Measured> measured;            ///< Сигнал появления нового сэмпла
    boost::signal<Weighed> weighed;                ///< Сигнал измерения веса 
    boost::signal<Failed> failed;                ///< Сигнал ошибки


Функции соединения сигналов со слотами:

    connection_t add_measured_handler(const boost::function<Measured> handler);
    connection_t add_weighed_handler(const boost::function<Weighed> handler);
    connection_t add_failed_handler(const boost::function<Failed> handler);


Клиентский код:

        m_Weigher = factory().make_Static(TtoA::from(config));

        m_measured_con    = m_Weigher->add_measured_handler(boost::bind(&CoPlatformWeigher::OnMeasured, this, _1));
        m_weighed_con    = m_Weigher->add_weighed_handler(boost::bind(&CoPlatformWeigher::OnWeighed, this, _1));
        m_error_con    = m_Weigher->add_failed_handler(boost::bind(&CoPlatformWeigher::OnFailed, this, _1, _2));


Теперь событие ошибки называется failed.
Re[7]: Шаблоны члены класса, специализация: как лучше сделат
От: rg45 СССР  
Дата: 23.12.09 10:14
Оценка:
Здравствуйте, silart, Вы писали:

S>
S>    connection_t add_measured_handler(const boost::function<Measured> handler);
S>    connection_t add_weighed_handler(const boost::function<Weighed> handler);
S>    connection_t add_failed_handler(const boost::function<Failed> handler);
S>


Упс! Передача объектов boost::function по значению. Наличие модификаторов const наводит на мысль, что ты хотел передавать параметры как константные ссылки. Потому как для параметров, передаваемых по значению, const особого смысла не имеет.
--
Справедливость выше закона. А человечность выше справедливости.
Re[8]: Шаблоны члены класса, специализация: как лучше сделат
От: silart  
Дата: 26.12.09 14:24
Оценка: +1
Здравствуйте, rg45!

Ну да, немного ошибся! copy-paste до добра не доводят!
Действительно, "const" не имеет значения.

    connection_t add_measured_handler(boost::function<Measured> handler);
    connection_t add_weighed_handler(boost::function<Weighed> handler);
    connection_t add_failed_handler(boost::function<Failed> handler);
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.