【发布时间】:2017-10-31 08:13:27
【问题描述】:
因此,如果您愿意,我正在创建一种事件处理程序,并且正在编写“事件侦听器包装器”。
基本思想是这样的: 当你想订阅一个事件时,你创建一个在事件触发时应该调用的函数。
您将此侦听器函数放入一个包装器中,以将该函数传递给调度程序。
调度程序获取一个事件,为您的侦听器找到包装器,并使用事件设置的参数值调用底层函数。
只要听众都只接受我的EventBase 类的一个参数,我已经有了一些工作。然后我必须将它输入到传递侦听器的正确事件中。
我想要的是让我的侦听器函数具有“任何”类型的参数,并以一种允许我根据触发的事件使用我想要的任何参数调用它的方式存储函数。每个侦听器函数只会接收一种类型的事件,或者它本身的事件。这将允许我不必在每个侦听器中键入强制转换每个事件,而是会传递正确的事件。
我为这个包装器找到了一些几乎完美的代码,但有一些我似乎无法修复的小问题。我会在下面解释。
@hmjd 编写的代码:
#include <iostream>
#include <string>
#include <functional>
#include <memory>
void myFunc1(int arg1, float arg2)
{
std::cout << arg1 << ", " << arg2 << '\n';
}
void myFunc2(const char *arg1)
{
std::cout << arg1 << '\n';
}
class DelayedCaller
{
public:
template <typename TFunction, typename... TArgs>
static std::unique_ptr<DelayedCaller> setup(TFunction&& a_func,
TArgs&&... a_args)
{
return std::unique_ptr<DelayedCaller>(new DelayedCaller(
std::bind(std::forward<TFunction>(a_func),
std::forward<TArgs>(a_args)...)));
}
void call() const { func_(); }
private:
using func_type = std::function<void()>;
DelayedCaller(func_type&& a_ft) : func_(std::forward<func_type>(a_ft)) {}
func_type func_;
};
int main()
{
auto caller1(DelayedCaller::setup(&myFunc1, 123, 45.6));
auto caller2(DelayedCaller::setup(&myFunc2, "A string"));
caller1->call();
caller2->call();
return 0;
}
我在这里做的第一件事是我必须用std::shared_ptr 替换std::unique_ptr。不知道为什么真的。这几乎可以工作。在我的用例中,我需要存储一个方法函数(意味着绑定需要传递包含方法对象?),并且在存储函数时我不知道参数值是什么,这就是事件来决定。所以我的调整如下:
class DelayedCaller
{
public:
template <typename TFunction, typename TClass>
static std::shared_ptr<DelayedCaller> setup(TFunction&& a_func,
TClass && a_class)
{
auto func = std::bind(std::forward<TFunction>(a_func),
std::forward<TClass>(a_class),
std::placeholders::_1);
return std::shared_ptr<DelayedCaller>(new DelayedCaller(func));
}
template <typename T>
void call( T v ) const { func_(v); }
private:
using func_type = std::function<void( )>;
DelayedCaller(func_type&& a_ft) : func_(std::forward<func_type>(a_ft)) {}
func_type func_;
};
为了测试,我删除了参数包,并将其替换为持有该函数的类对象的直接参数。我还为绑定提供了 1 个参数的占位符(理想情况下,稍后由 void call() 函数替换)。
它是这样创建的:
eventManager->subscribe(EventDemo::descriptor, DelayedCaller::setup(
&AppBaseLogic::getValueBasic,
this
));
问题是:在这一行:
return std::shared_ptr<DelayedCaller>(new DelayedCaller(func));
我得到“没有匹配函数调用'DelayedCaller::DelayedCaller(std::_Bind(AppBaseLogic*, std::_Placeholder)>&)' return std::shared_ptr(new DelayedCaller(func));"
这只发生在使用placeholder::_1 时。如果我用正确类型的已知值替换它,它就可以工作,除了函数在没有任何有用数据的情况下被调用。
所以,我想我需要一种方法来使用我不知道类型的占位符来存储函数?
如果我把事情的名字弄错了,请原谅我。我对c++很陌生,最近几天才开始学习。
**编辑:**
好的,所以我只是在更新为什么我需要存储这样的函数。 我的事件调度程序中有一个如下所示的地图:
std::map< const char*, std::vector<DelayedCaller> > _observers;
我希望能够像这样调用“延迟调用者”中的函数:
void Dispatcher::post( const EventBase& event ) const
{
// Side Note: I had to do this instead of map.find() and map.at() because
// passing a "const char*" was not evaluating as equal to event.type() even
// though event.type() is also a const char*. So instead I am checking it
// myself, which is fine because it gives me a little more control.
std::string type(event.type());
for( auto const &x : _observers ) {
std::string type2(x.first);
if ( type == type2 ) {
auto&& observers = x.second;
for( auto&& observer : observers ) {
// event may be any descendant of EventBase.
observer.slot->call(event);
}
break;
}
}
}
我的听众目前看起来像这样:
void AppBaseLogic::getValue(const EventBase &e) {
const EventDemo& demoEvent = static_cast<const EventDemo&>( e );
std::cout << demoEvent.type();
}
我正在尝试存储每个函数,以便参数看起来像这样:
void AppBaseLogic::getValue(const EventAnyDescendant &e) {
std::cout << e.type();
}
希望这会有所帮助。感谢大家花时间帮助我。
关于 lambda 的旁注:有人建议使用它们,我知道它们是什么或如何使用它们,但我将对它们进行一些研究,看看是否更有意义。尽管从我所看到的情况来看,我担心它们的可维护性。
【问题讨论】:
-
嗯,为什么不单独使用
std::function? -
我之前问过的类似问题stackoverflow.com/questions/43396129/…
-
“让我根据触发的事件使用我想要的任何参数调用它”。这就是重点:每个事件都需要一个特定的签名,因此请按事件类型创建函数列表
std::vector<std::function<void(intx, int y)>> onClick; std::vector<std::function<void(char)>> onKeyPressed;... -
为每种类型的事件创建不同向量的想法“可能”奏效。我将不得不做一些截然不同的事情。现在事件存储在地图中,通过“event_Key”:要调用的函数向量。这样,如果发出事件并且没有任何侦听器,则无关紧要。我真的不知道如何在添加侦听器时自动动态地创建一个新的向量列表,但我可能能够做到。另一个问题的一部分是我想让一些事件模板化。因此 EventMsg
、EventMsg 的列表不同、EventMsg ...
标签: c++ c++11 variadic-templates variadic stdbind