将具有任意参数和占位符的函数存储在类中,然后再调用 [英] Store a function with arbitrary arguments and placeholders in a class and call it later

查看:84
本文介绍了将具有任意参数和占位符的函数存储在类中,然后再调用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

因此,我正在创建一种事件处理程序,如果愿意,我正在编写事件侦听器包装".

So I am creating a type of event handler and I am in the process of writing an "Event Listener Wrapper", if you will.

基本思想是这样的: 当您想要订阅事件时,可以创建一个函数,该事件触发时应调用该函数. <-已经完成了(我会解释)

The basic idea is this: When you want to subscribe to an event, you create a function that should be called when the event fires. <-- already have that done (kinda, I'll explain)

您将此侦听器函数放入包装器中,以将该函数传递到调度程序上.

You put this listener function into a wrapper to pass the function onto the dispatcher.

调度程序获取一个事件,为您的侦听器找到包装,然后使用该事件设置的参数值调用基础函数.

The dispatcher gets an event, finds the wrapper for you listener, and calls the underlying function with the parameter values set by the event.

只要侦听器都只接受我的EventBase类的一个参数,我就已经可以正常工作了.然后,我必须将其强制转换为传递侦听器的适当事件.

I already have something working so long as the listeners all only accept one argument of my EventBase class. Then I have to type cast that into the proper event that the listener is passed.

我想要的是让我的侦听器函数具有任何"类型的参数,并以一种方式存储函数,该方式使我可以根据触发的事件使用所需的任何参数来调用它.每个侦听器功能只会收到一种类型的事件,或者是它本身的事件.这将使我不必在每个侦听器中键入强制转换每个事件,而是将传递正确的事件.

What I want instead is for my listener functions to have "any" type of arguments, and store the function in a way that lets me call it with any arguments I want depending on the event fired. Each listener function would only ever receive one type of event, or the event it's self. This would allow me to not have to type cast each event in every listener, but instead the correct event would be passed.

我找到了这个包装的一些代码,它们几乎是完美的,还有一些我似乎无法解决的小问题.我会在下面解释.

I found a bit of code for this wrapper that is almost perfect, with a few minor issues that I can't seem to fix. I'll explain below.

@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.不知道为什么.这几乎可行.在我的用例中,我需要存储一个方法函数(意味着是否需要将绑定传递给包含方法的对象?),并且在存储该函数时,我不知道参数值是多少,这足以满足事件来决定.所以我的调整如下:

The first thing I did here was I had to replace std::unique_ptr with std::shared_ptr. Not sure why really. This almost works. In my use case, I need to store a method function (meaning bind needs to be passed the containing method object?), and at the time of storing the function I don't know what the argument value will be, thats up for the event to decide. So my adjustment is as follows:

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_;
};

为了进行测试,我删除了参数包,并用包含该函数的类对象的直接参数替换了它.我还为bind提供了1个参数的占位符(以后最好由void call()函数替换).

For the sake of testing, I removed the parameter pack and replaced it with a direct parameter to the class object holding the function. I also gave the bind a placeholder for 1 argument (ideally replaced by the void call() function later).

它是这样创建的:

eventManager->subscribe(EventDemo::descriptor, DelayedCaller::setup(
                                &AppBaseLogic::getValueBasic,
                                this
                                ));

问题是:在此行上:

return std::shared_ptr<DelayedCaller>(new DelayedCaller(func));

我收到没有匹配的函数来调用'DelayedCaller :: DelayedCaller(std :: _ Bind(AppBaseLogic *,std :: _ Placeholder< 1>)>&) 返回std :: shared_ptr(new DelayedCaller(func));"

I get "no matching function for call to 'DelayedCaller::DelayedCaller(std::_Bind(AppBaseLogic*, std::_Placeholder<1>)>&)' return std::shared_ptr(new DelayedCaller(func));"

这仅在使用placeholder::_1时发生.如果我将其替换为正确类型的已知值,则可以正常工作,所不同的是,该函数会在没有任何有用数据的情况下被调用.

This only happens when using the placeholder::_1. if I replace that with a known value of the correct type, it works, with the exception that the function gets called without any useful data of course.

那么,我想我需要一种方法来将函数与我不知道类型的占位符一起存储?

So, I guess I need a way to store the function with placeholders that I don't know the type of?

如果我弄错了名字,请原谅我.我刚接触c ++,最近几天才开始学习它.

Forgive me if I am getting names of things wrong. I am very new to c++, I have only started learning it the past few days.

****

好,所以我只是在更新为什么我需要存储这样的函数. 我的事件分配器中有一张地图,看起来像这样:

Ok, so I am just updating why I need to store functions like this. I have a map in my event dispatcher that looks like this:

std::map< const char*, std::vector<DelayedCaller> > _observers;

我希望能够在延迟调用者"内部调用该函数,如下所示:

I want to be able to call the function inside the "Delayed Caller" something like this:

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;
        }
    }
}

我的听众当前如下所示:

My listeners currently look like this:

void AppBaseLogic::getValue(const EventBase &e) {
    const EventDemo& demoEvent = static_cast<const EventDemo&>( e );
    std::cout << demoEvent.type();
}

我正在尝试存储每个函数,以使参数看起来像这样:

I am trying to store each function so that the argument may look like this:

void AppBaseLogic::getValue(const EventAnyDescendant &e) {
    std::cout << e.type();
}

希望这会有所帮助.谢谢大家花时间为我提供帮助.

Hopefully that helps. Thank you all for taking the time to help me with this.

关于lambda的旁注:有人建议了它们,我知道它们是什么或如何使用它们,但是我将对它们进行一些重新研究,以查看是否更有意义.从我所看到的情况来看,我担心它们的可维护性.

Side note on lambdas: Someone suggested them, I have know idea what they are or how to use them, but I am going to do some reaserch on them so see if that would make more sense. I am worried about maintainability with them though from what I have seen.

推荐答案

好,所以我知道这已经坐了一段时间了.我一直在对不同的事件模式进行大量研究,试图找到更接近我所追求的东西.在仔细研究了所有内容之后,并在那些在这里发表评论的人的建议下,我决定使用Signal/Slot模式,这可能是C ++中使用最广泛的事件模式.已经实现的方法是让我所有的逻辑类"(无论是用于gui还是用于计算)都保留对第三个信号事件持有者类"的引用,为简便起见,我将其称为事件代理.这和我所能得到的差不多.您可能希望拥有的任何事件都可以添加到此类中,并且可以通过引用事件代理的任何类从任何类中访问和调用该事件.我发现了 Simon Schneegans 制作的一个非常漂亮的信号类,但我正在积极尝试查找/学习如何做得更好(线程安全,也许更快?).如果有人像我一样有兴趣/正在寻求帮助,您可以在这里找到我的超级基本测试用例: https://github.com/Moonlight63/QtTestProject

Ok, So I know this has been sitting for a while. I've been doing heavy research into different event patterns trying to find something closer to what I was after. After pouring through everything, and with the advice of those who have left comments here, I have decided to use a Signal/Slot pattern, possibly the most widely used event pattern for C++. The way have have approached it is to have all of my "logic classes" (whether for a gui or for computation) keep a reference to a third "signal event holder class", which I am calling an event broker for simplicity. This is just about as good as I can get it. Any event that you might want to have can be added to this class, and it can be accessed and called from any class with a reference to the event broker. I found a pretty nice signal class made by Simon Schneegans, but I am actively trying to find/learn how to make something better (threadsafe, maybe faster?). If anyone is interested/looking for help like I was, you can find my super basic test case here: https://github.com/Moonlight63/QtTestProject

谢谢!

这篇关于将具有任意参数和占位符的函数存储在类中,然后再调用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆