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, по сути являются синонимами и компилятор не делает различий, когда их передаешь в качестве параметров шаблона.
Извратиться конечно в данном случае можно, но очень хочется решить данную проблему красиво и лаконично, чтобы клиентский код выглядел примерно так:
Здравствуйте, 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);
}
};
И клиентский код при этом выглядит не хуже — и компактно и понятно:
Ваше решение всем хорошо, нравится оно мне, но вот бы еще уйти от connect_xxx() и было бы ваще здорово!
Очень хочется, чтобы функции connect() выглядели именно так.
Возможно ли сделать такое?
Re[2]: Шаблоны члены класса, специализация: как лучше сделат
Здравствуйте, Caracrist, Вы писали:
C>Здравствуйте, silart, Вы писали:
S>>Добрый день!
S>>Посоветуйте пожалуйста, как можно решить данную проблему? C>может както так? C>
Здравствуйте, silart, Вы писали:
S>Здравствуйте, rg45!
S>Ваше решение всем хорошо, нравится оно мне, но вот бы еще уйти от connect_xxx() и было бы ваще здорово! S>Очень хочется, чтобы функции connect() выглядели именно так.
S>Возможно ли сделать такое?
Ну так или иначе, каким-то образом прийдется обеспечить уникальность перегрузок/специализаций в условиях, когда сигнатуры обработчиков разных событий могут совпадать. Можно пойти по варианту, предложенному Caracrist-ом здесь
. В этом варианте уникальность перегрузок/специализаций обеспечивается введением дополнительных типов. Сразу нужно подчеркнуть, что автовыведение шаблонных параметров функций connect работать не будет, эти функции всегда прийдется выполнять с явной спецификацией шаблонных параметров:
Большое спасибо всем откликнувшимся товарищам за конструктивные предложения!
Здравствуйте, 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; ///< Сигнал ошибки
Я отказался от слова "connect" в названии функций. Я думаю функция соединения типа on_xxx() выглядит гораздо лучше.
Я считаю выбор названий важным этапом!
Здравствуйте, Caracrist! Ваш вариант всем хорош, но он требует объявления новых типов, связанных с типами слотов вручную:
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]: Шаблоны члены класса, специализация: как лучше сделат
Здравствуйте, 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; ///< Сигнал ошибки
Упс! Передача объектов boost::function по значению. Наличие модификаторов const наводит на мысль, что ты хотел передавать параметры как константные ссылки. Потому как для параметров, передаваемых по значению, const особого смысла не имеет.
--
Справедливость выше закона. А человечность выше справедливости.
Re[8]: Шаблоны члены класса, специализация: как лучше сделат