c ++ 11: отправка универсального типа с помощью std :: function & lt; T (const U & amp;) & gt;

Я пытаюсь создать общий компонент 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: я не могу использовать повышение.

2

Решение

Мне кажется, что ясность Вы имеете в виду тот факт, что 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

Как видите, довольно сложный … и самое худшее? Это все еще небезопасно. В частности, существуют проблемы на всю жизнь:

  • если вы регистрируете обратный вызов, имеющий ссылки на внешний мир, вы должны удалить его до того, как эти ссылки умрут
  • было бы чище удалить источники, когда они исчезнут (чтобы предотвратить утечку)

Есть несколько способов обойти это, вы можете посмотреть в сигнал / слотов которые помогают реализовать эту логику, не привязывая ее к определенному объекту.

1

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]