具有对象的向量,这些对象具有不同类型的函数指针 [英] Vector with objects that have function pointers of varying type

查看:74
本文介绍了具有对象的向量,这些对象具有不同类型的函数指针的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试实现一个向量,该向量表示从基类继承的TimedCallback对象的列表. 它们包含一些基本变量,还有一个主要功能是函数指针. 该函数应该能够返回任何类型并具有任何参数. 我将它们作为lambda传递给了函数,到目前为止还没有真正的问题.

I'm trying to implement a vector that represents a list of TimedCallback objects who inherits from a base class. They hold some basic variables, plus a function pointer which is the main feature. The function should be able to return any type and have any params. I pass them as lambdas into the functions, not really any problems so far.

这是相关代码:

std::vector<std::unique_ptr<TimedCallbackBase>> m_TimerCallbackList;

struct TimedCallbackBase {
    TimedCallbackBase() = default;
    virtual ~TimedCallbackBase() = default;

    template<typename T> T run()
    {
        return dynamic_cast< TimedCallback<T> & >(*this).Run();
    }
    template<typename T> std::string name()
    {
        return dynamic_cast< TimedCallback<T> & >(*this).Name;
    }
    template<typename T> TaskTimer time()
    {
        return dynamic_cast< TimedCallback<T> & >(*this).Time;
    }
    template<typename T> int repeatcount()
    {
        return dynamic_cast< TimedCallback<T> & >(*this).RepeatCount;
    }
};

template <typename Fu>
struct TimedCallback : TimedCallbackBase {
    TimedCallback(const std::string& name, const std::string& time, Fu f, int r) : Name(name), Run(f), Time(time), RepeatCount(r) {}
    std::string Name;
    Fu Run;
    TaskTimer Time;
    int RepeatCount;
};

template<typename Fu>
void Schedule(const std::string& name, const std::string& time, Fu f, int repeatCount = 0) {
    TimedCallback cb(name, time, f, repeatCount);
    if (!vec_contains(m_TimerCallbackList, cb)) {
        m_TimerCallbackList.push_back(cb);
    }
    else { Global->Log->Warning(title(), "Callback '"+name+"' already exists."); }
}

我的问题是这种方法.我无法正确运行func指针.

My problem is this method. I am unable to properly run the func pointers.

void _RunTimers() {
    if (m_TimerCallbackList.size() > 0) {
        for (auto &t : m_TimerCallbackList) {
            if (t != nullptr) {

                std::string _name = t.get()->name(); // wrong syntax, or signature?
                TaskTimer  _time = t.get()->time();
                int  _repeatcount = t.get()->repeatcount();
                //auto _run = t.get()->run(); ??

                Global->t("Callback name: " + _name);
                Global->t("Callback time: " + _time.GetRealTimeAsString());
                Global->t("Callback count: " + its(_repeatcount));
            }
        }
    }
    else { Global->Log->Warning(title(), "No timed tasks to run at this time."); }
}

我打算使用这样的代码:

I intend to use the code like this:

Task->Schedule("testTimer", "10sec", [&]{ return Task->Test("I'm a 10sec timer."); });

_RunTimers();

我觉得我距离正确地完成工作还差得很远. 我不想为_RunTimers()指定任何模板;方法. 请帮助我了解如何做到这一点.

I feel like I am pretty far from doing this correctly. I don't want to specify any templates for the _RunTimers(); method. Please help me understand how this can possible.

我的意思是,我想完全有可能沿着

I mean, I guess it is totally possible to just define a bunch of typedefs along the lines of

using int_func = std::function<int()>;

对于每种可能的情况,然后重载包装对象,但我一直在寻找更具动态性和防更改性的东西.

for every possible case and then overload my wrapper object, but I was looking for something more dynamic and change-proof.

实施建议的更改后

注意:为避免歧义,我已将方法重命名. (这里不包括方法Test(),而只是对字符串参数执行std :: cout)

Note: I've renamed the methods for ambiguity's sake. (The method Test() is not included here, but simply does a std::cout of a string param)

主要:

Callback myCallback = make_callback(&TaskAssigner::Test, "I'm a 5sec timer.");

Task->ScheduleJob("timer1", "5sec", myCallback, -1);
Task->RunScheduledJobs();

实用程序:

typedef double RetVal;
typedef std::function<RetVal()> Callback;

template <class F, class... Args>
Callback make_callback(F&& f, Args&&... args)
{
    auto callable = std::bind(f, args...);                  // Here we handle the parameters
    return [callable]() -> RetVal{ return callable(); };    // Here we handle the return type
}

struct TimedCallback  {
    TimedCallback(cstR name, cstR time, Callback f, int r)
        : Name(name), Run(f), Time(time), RepeatCount(r) {}

    RetVal operator()() const { return Run(); }

    bool operator==(const TimedCallback& other) {
        if (Name == other.Name && RepeatCount == other.RepeatCount) { return true; }
        return false;
    }

const bool operator==(const TimedCallback& other) const {
    if (Name == other.Name && RepeatCount == other.RepeatCount) { return true; }
    return false;
}

    std::string Name;
    Callback Run;
    TaskTimer Time;
    int RepeatCount;
};

TaskAssigner .h:

void ScheduleJob(const std::string& name, const std::string& time, const Callback& func, int repeatCount = 0);

void RunScheduledJobs();

std::vector<TimedCallback> m_TimerCallbackList;

TaskAssigner.cpp:

void TaskAssigner::ScheduleJob(const std::string& name, const std::string& time, const Callback& func, int repeatCount) {

    TimedCallback cb(name, time, func, repeatCount);

    if (!vec_contains(m_TimerCallbackList, cb)) {
        m_TimerCallbackList.emplace_back(cb);
    }
    else { Global->Log->Warning(title(), "Callback '" + name + "' already added."); }
}

void TaskAssigner::RunScheduledJobs() {
    if (m_TimerCallbackList.size() > 0) {
        for (auto &t : m_TimerCallbackList) 
        {
            RetVal value = t();
            //Global->t("Callback result: " + std::to_string(value));
            Global->t("Callback name: " + t.Name);
            Global->t("Callback time: " + t.Time.GetRealTimeAsString());
            Global->t("Callback count: " + its(t.RepeatCount));
        }
    }
    else { Global->Log->Warning(title(), "No timed tasks to run at this time."); }
}

当前问题:

编译器说:C3848:类型为'const std :: _ Bind ,const char(&)[18]>'类型的表达式将丢失一些const易变限定符以进行调用 .....

Compiler says: C3848: expression having type 'const std::_Bind, const char(&)[18]>' would lose some const-volatile qualifiers in order to call .....

我尝试研究,有些人提到了VS 2013中有关绑定和自动的错误.解决方案包括键入签名而不是自动签名,或删除/添加适当的const(?).不知道这是同一个错误还是我的实现仍然不正确. (错误: https://stackoverflow.com/a/30344737/8263197 )

I tried researching and some have mentioned a bug with VS 2013 regarding bind and auto. Solutions include typing the signature rather than auto-ing it, or remove/add proper const(?). Not sure if this is the same bug or if my implementation is still incorrect. (Bug: https://stackoverflow.com/a/30344737/8263197)

我不确定如何使用此typedef使RetVal支持任何值. 我尝试过

I am unsure how exactly I can make RetVal support any value with this typedef. I tried

    template<typename T>
struct CallbackReturnValue {
    CallbackReturnValue(T v) : value(v) {}
    T value;
};

,但是我仍然需要模板化其他支持方法.我在这里误会什么?

but then I will still need to template the other supporting methods. What am I mistaking here?

推荐答案

似乎您正在尝试重新发明 std::function (通用多态函数包装器").与其处理模板和多个子类,不如尝试以下类似的方法.

It seems like you are trying to re-invent std::function ("a general-purpose polymorphic function wrapper"). Instead of dealing with templates and multiple child classes, I would try something more like the following.

typedef std::function<void()> Callback;

struct TimedCallback {
    TimedCallback(const std::string& name, const std::string& time, const Callback & f, int r) :
        Name(name),
        Run(f),
        Time(time),
        RepeatCount(r)
    {}

    // Not wise to differentiate names by case (run vs. Run),
    // but this form might be instructive as to how this
    // setup would fit into your existing _RunTimers().
    // By the way, _RunTimers is a reserved identifier.
    void run()
    {
        Run();
    }

    std::string Name;
    Callback Run;
    TaskTimer Time;
    int RepeatCount;
};

如果您需要回调返回的值,则需要更复杂的事情.但是,尝试一次迈出这一步.

Something more complex would be needed if you need the values returned by the callbacks. However, try taking this one step at a time.

经过澄清后,看来目的是将返回的值存储在某种容器中.为了使此工作有效,需要有一种可以将所有返回值都转换为的类型(例如,使所有返回值都是从公共基类派生的类).我将继续给出一种可行的方法.

After some clarifications, it seems the intent is to store returned values in some sort of container. To make this work, there would need to be a type that all returned values can be converted to (such as having all returned values be classes derived from a common base class). I'll proceed to give one way this could be made to work.

第一步是定义公共返回类型.通过将其设为typedef,我可以在以后的代码中抽象出这一选择.

The first step is to define the common return type. By making this a typedef, I can abstract away this choice in later code.

typedef /* fill this in */ RetVal;

接下来,我们修改Callbackrun()的定义以解决此类型.

Next, we revise the definition of Callback and run() to account for this type.

typedef std::function<RetVal()> Callback;

RetVal run()
{
    return Run();
}

TimedCallback的定义在其他方面保持不变,但是我将引入一个便利层,以使构造这些回调更加容易.受到"make_pair"和"make_tuple"等令人敬畏的名字的启发:

The definition of TimedCallback otherwise remains the same, but I would introduce a convenience layer to make it easier to construct these callbacks. Inspired by such awe-inspiring names as "make_pair" and "make_tuple":

template <class F, class... Args>
Callback make_callback(F&& f, Args&&... args)
{
    auto callable = std::bind(f, args...);               // Here we handle the parameters
    return [callable]() -> RetVal{ return callable(); }; // Here we handle the return type
}

啊,模板终于出现了!但是请注意,模板已本地化到此便捷功能.如果您的可调用对象已经采用方便的形式,则不必使用此模板.假设对象不是便利形式,这里是便利的示例用法.为了举例说明,我将假定一个字符串可以隐式转换为RetVal.

Ah, templates have finally made an appearance! But note that the template is localized to this convenience function; you don't necessarily need to use this template if your callable object is already in a convenient form. Assuming the object is not in a convenient form, here is a sample use of the convenience. For the sake of example, I am going to assume that a string is implicitly convertible to RetVal.

// Example function that takes a parameter.
std::string hello(const std::string & world)
{
    std::cout << "Hello " << world << "!\n";
    return world;
}

// Example main function to see that this can work.
int main()
{
    // Placeholder for m_TimerCallbackList.
    std::vector<TimedCallback> timer_list;

    // Instead of Task->Schedule(), I'll emplace on a vector.
    timer_list.emplace_back("testTimer", "10sec",
                            make_callback(hello, "Earth"),
                            1);

    // Instead of RunTimers(), I'll manually run the first callback.
    RetVal value = timer_list[0].run();
}

这篇关于具有对象的向量,这些对象具有不同类型的函数指针的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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