【问题标题】:Functional plugin and task queue attempt功能插件和任务队列尝试
【发布时间】:2021-02-24 02:12:29
【问题描述】:

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

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

【问题讨论】:

    标签: c++ templates functional-programming template-meta-programming


    【解决方案1】:

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

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

    【讨论】:

    • 哦!工作太晚会忘记的那种事情^^!很好,感谢您的阅读。
    猜你喜欢
    • 2018-03-12
    • 2018-02-10
    • 2021-09-02
    • 2017-04-17
    • 2023-03-19
    • 1970-01-01
    • 2014-12-03
    • 2019-07-25
    • 1970-01-01
    相关资源
    最近更新 更多