Я пытаюсь создать общий компонент push.
У меня есть класс store<T>
который
void push(const T & t)
метод, вызываемый поставщиками данных.Когда push вызывается поставщиком, я хочу, чтобы значение вычислялось std::function< T2(const T&)>
и все клиенты (store<T2>
) уведомлены
с этим значением результата типа T2. Клиент магазина должен сначала подписаться на магазин через linker<T, T2>
объект.
template <class data> class store
{
data data_;
std::list< action<data > > m_links;
public:
void push(const data & e)
{
data_ = e;
for(action<data> a : m_links)
a(data_);
}
void subscribe(action<data> d)
{
m_links.push_back(d);
}
};
Линкерный объект:
template < class data1, class data2 > class linker
{
// source where come the push calls
store<data1> & m_source;
// targets to be notified after computation
std::list<store<data2> * > m_targets;
// computation function
std::function< data2(const data1 &)> m_func;
public:
linker(store<data1> & source, std::function< data2(const data1 &)> func)
: m_source(source), m_func(func)
{
m_source.subscribe([this](const data1 & d){this->push(d);});
}
// add a client
void add_target(store<data2> & target)
{
m_targets.push_back(&target);
}
void push(const data1 & e)
{
//compute result
data2 d2 = m_func(e);
// notify all
for(store<data2> * s2 : m_targets)
{
s2->push(d2);
}
}
};
Случай использования:
int main()
{
// function : just increment an int, return as double
std::function<double(const int &) > f_inc = [](const int& i){ return i+1;};
// function : display the data
std::function<int(const double&) > f_display = [](const double& d){ std::cout << "value=" << d << std::endl ; return 0;};
store<int> source;
store<double> target, target2;
linker<int, double> l(source, f_inc);
l.add_target(target);
l.add_target(target2);linker<double, int> display(target, f_display);
source.push(1);
return 0;
}
Я хочу подавить явную явность объекта ‘linker’. Мне это не удалось, потому что я не знаю, как справиться с тем фактом, что когда клиент хранилища подписывается на объект хранилища, объект не может хранить указатель для хранения, поскольку он не знает тип T2!
Я хотел бы написать что-то вроде этого:
std::function<double(const int &) > f_inc = [](const int& i){ return i+1;};
store<int> source;
store<double> target;
source.link_to(target, f_inc);
и сможете отписаться
source.unlink(target, f_inc);
или с идентификаторами:
id i = source.link_to(target, f_inc);
source.unsubscribe(i);
Я использую codeblocks + mingw 4.8.1 под Windows XP.
Я предполагаю, что существует шаблон проектирования для этого варианта использования …
PS: я не могу использовать повышение.
Мне кажется, что ясность Вы имеете в виду тот факт, что Linker
имеет параметры шаблона.
Я хотел бы представить что-то вроде:
class Broker {
public:
Broker(): _lastId(0) {}
//
// Notification
//
template <typename T>
void notify(store<T> const& source, T const& event) {
auto const it = _sources.find(&source);
if (it == _sources.end()) { return; }
for (size_t id: it->second) { _targets.find(id)->second->invoke(&event); }
} // notify
//
// Subscription
//
template <typename T, typename U>
size_t subscribe(Store<T> const& source, U&& callback) {
_targets[++_lastId] = std::unique_ptr<Action>(new ActionT<T>(callback));
_sources[&source].insert(_lastId);
return _lastId;
} // subscribe
template <typename T, typename U>
size_t subscribe(Store<T> const& source, U const& callback) {
return this->subscribe(source, U{callback});
} // subscribe
void unsubscribe(size_t id) {
auto const it = _targets.find(id);
if (it == _targets.end()) { return; }
void const* source = it->second->_source;
auto const it2 = _sources.find(source);
assert(it != _sources.end());
it2->second.erase(id);
if (it2->second.empty()) { _sources.erase(it2); }
_targets.erase(it);
} // unsubscribe
template <typename T>
void unsubscribe(store<T> const& source) {
auto const it = _sources.find(&source);
if (it == _sources.end()) { return; }
for (size_t id: it->second) { _targets.erase(id); }
_sources.erase(it);
} // unsubscribe
private:
//
// Action/ActionT<T> perform Type Erasure (here, we erase T)
//
struct Action {
Action(void const* source): _source(source) {}
virtual void invoke(void const*) = 0;
void const* _source;
}; // struct Action
template <typename T>
class ActionT: Action {
public:
ActionT(store<T> const& source, std::function<void(T)> f):
Action(&source),
_callback(std::move(f))
{}
virtual void invoke(void const* event) {
_callback(T(*static_cast<T const*>(event));
}
private:
std::function<void(T)> _callback;
}; // class ActionT
using Targets = std::map<size_t, std::unique_ptr<Action>>;
using Sources = std::map<void const*, std::set<size_t>>;
size_t _lastId;
Targets _targets;
Sources _sources;
}; // class Broker
Как видите, довольно сложный … и самое худшее? Это все еще небезопасно. В частности, существуют проблемы на всю жизнь:
Есть несколько способов обойти это, вы можете посмотреть в сигнал / слотов которые помогают реализовать эту логику, не привязывая ее к определенному объекту.
Других решений пока нет …