功能插件和任务队列尝试

Functional plugin and task queue attempt

我正在尝试对一个系统的概念进行验证,在该系统中,我可以通过主要依赖类型和静态函数来对要根据上下文调用的任务进行排队。我希望它像这样工作:

int main()
{
    PluginManager pm;
    ContextId ctxId = pm.makeContext<Plugin>(1, "un");
    pm.queueTask<Plugin>(ctxId, Plugin::execute, 2, "deux");
    pm.run();
}

下面的代码似乎快到了,但是链接无法解析 ITask 方法来获取任务的上下文 ID 并使用它的组件执行任务。我相信我可能会通过模板恶作剧丢失实际类型的信息,但我不知道在哪里。

#include <iostream>
#include <map>
#include <deque>
using namespace std;

// Function to unroll a tuple of parameters on a function
template <typename Function, typename Params, size_t... Index>
auto dispatchParams(Function f, Params p, std::index_sequence<Index...>)
{
    return f(std::get<Index>(p)...);
}

// Function preparing index sequence to call dispatchParams
template <typename Function, typename... Params>
auto callWithTuple(Function f, tuple<Params...> args)
{
    return dispatchParams(f, args, std::make_index_sequence<sizeof...(Params)>());
}

// Plugin identifier
enum class PluginEnum
{
    PluginA
};

// An identification object
struct ContextId
{
    PluginEnum pluginId;
    int instanceId;

    bool operator<(const ContextId& other) const
    {
        return instanceId < other.instanceId;
    }
};

// Iterface for specific context, for storage
struct IContext
{
    ContextId identity;
};

// Interface for template Task, for storage
struct ITask
{
    virtual void execute(IContext* ctx);
    virtual ContextId getContextId();
};

// Task storing what function is to be called on what context with what arguments
template <typename PLUGIN_TYPE, typename FUNCTION, typename... ARGS>
struct Task : ITask
{
    using CONTEXT_TYPE = typename PLUGIN_TYPE::Context;
    using TUPLE = tuple<CONTEXT_TYPE*, ARGS...>;

    Task(ContextId id, FUNCTION function, ARGS... args)
        : id(id)
        , function(function)
        , argsTuple(make_tuple(nullptr, args...))
    {}

    virtual void execute(IContext* ctx)
    {
        get<0>(argsTuple) = static_cast<CONTEXT_TYPE*>(ctx);
        callWithTuple(function, argsTuple);
    }

    virtual ContextId getContextId() override { return id; }

    ContextId id;
    FUNCTION function;
    TUPLE argsTuple;
};

// The class registering the plugin context, queueing calls and executing them
struct PluginManager
{
    template <typename PLUGIN_TYPE, typename... ARGS>
    ContextId makeContext(ARGS... args)
    {
        // Define dependent type
        using CONTEXT_TYPE = typename PLUGIN_TYPE::Context;

        ContextId id{PLUGIN_TYPE::type, 123};
        CONTEXT_TYPE* ctx = new CONTEXT_TYPE(id, args...);
        _contexts[id] = static_cast<IContext*>(ctx);
        return id;
    }

    template <typename PLUGIN_TYPE, typename FUNCTION, typename... ARGS>
    void queueTask(ContextId ctxId, FUNCTION function, ARGS... args)
    {
        using TASK_TYPE = Task<PLUGIN_TYPE, FUNCTION, ARGS...>;

        TASK_TYPE* task = new TASK_TYPE(ctxId, function, args...);
        _tasks.push_back(static_cast<ITask*>(task));
    }

    void run()
    {
        while (!_tasks.empty())
        {
            ITask* task     = _tasks.front();
            IContext* ctx   = _contexts[task->getContextId()];
            task->execute(ctx);
            _tasks.pop_front();
        }
    }

    map<ContextId, IContext*> _contexts;
    deque<ITask*> _tasks;
};

// The plugin we're trying to register
struct Plugin
{
    static const PluginEnum type = PluginEnum::PluginA;

    // Definition of the plugin context / data object
    struct Context : IContext
    {
        Context(ContextId id, int number, const char* string)
            : IContext{id}
            , _dataNumber(number)
            , _dataString(string)
        {}

        const char* getString() { return _dataString; }
        int         getNumber() { return _dataNumber; }
        const char* _dataString;
        int         _dataNumber;
    };


    static void execute(Plugin::Context* context, int number, const char* string)
    {
        cout << "String is " << context->getString() << "\n";
        cout << "Number is " << context->getNumber() << "\n";
        cout << "Passed number is " << number << "\n";
        cout << "Passed string is " << string << "\n";
    }
};

int main()
{
    PluginManager pm;
    ContextId ctxId = pm.makeContext<Plugin>(1, "un");
    pm.queueTask<Plugin>(ctxId, Plugin::execute, 2, "deux");
    pm.run();
}

您的 ITask 虚拟方法未声明为抽象方法。这意味着链接器会查找它们的实现。只需将其更改为:

virtual void execute(IContext* ctx) = 0;
virtual ContextId getContextId() = 0;