gpt4 book ai didi

c++11:通过 std::function 推送泛型类型

转载 作者:塔克拉玛干 更新时间:2023-11-03 07:25:21 25 4
gpt4 key购买 nike

我正在尝试构建一个通用的推送组件。

我有一个类store<T>哪个

  • 有一个T t;成员(member)
  • 有一个void push(const T & t)数据提供者调用的方法。

当提供者调用推送时,我想要一个由 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;
}

我想抑制“链接器”对象的显式性。我没有成功,因为我不知道如何处理这样一个事实,即当商店客户端订阅商店对象时,该对象无法存储要存储的指针,因为它不知道类型 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);

或带有 ids:

id i = source.link_to(target, f_inc);
source.unsubscribe(i);

我在 windows xp 下使用 codeblocks + mingw 4.8.1。我想这个用例存在设计模式......

ps:我不能用boost。

最佳答案

在我看来,明确性是指 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

如您所见,相当复杂……而且最糟糕的是什么?它仍然不安全。具体来说,存在生命周期问题:

  • 如果您注册了一个引用外部世界的回调,则必须在这些引用消失之前将其删除
  • 当源消失时将其移除会更干净(以防止泄漏)

有一些方法可以解决这个问题,您可能需要查看 signal/slots这有助于在不将其绑定(bind)到特定对象的情况下实现此逻辑。

关于c++11:通过 std::function<T(const U&)> 推送泛型类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20216866/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com