【问题标题】:Running any functor with half of arguments运行任何带有一半参数的仿函数
【发布时间】:2019-09-18 11:02:18
【问题描述】:

我想达到什么目的: 我有以下类 Request 它有它自己的模板化功能。我想用两种状态来称呼它:
1) 提供参数;
2)只有一个参数,其他的应该被默认创建

template <typename TCmd> class Request{
    public:
    Request(TCmd applyingCommand): cmd(applyingCommand){}
    template<typename ...TReplyData> void onSuccess(TReplyData... args){
        //  cmd(true, args...);
    }
    void onFail(){
         // cmd(false) //here I want to create a wrapper, who calls the cmd with false + empty arguments
                       //which should be created by their constructor.
    }
    TCmd cmd;
};

我想如何使用它:

auto lambda = [](bool isSucceed, int v, std::vector<uint> vec){
        //doing smth
        qDebug() << "insideLamda" << isSucceed << v << vec;
    };
    std::function<void(bool, int, std::vector<uint>)> fu = lambda;
    Request req(fu);
    req.onSuccess(4, std::vector<uint>{1,2});
    req.onFail();

所以这是我的想法如何实现它,但我坚持使用元组和可变参数模板 重点在于创建以下函数包装器

template <typename ...Args> class CmdFu
{
public:
    explicit CmdFu(std::function<void(Args...)> f): m_function(f){
    }
    template <typename ...ProvidedArgs>void call(ProvidedArgs... args){
        m_function(args...);
    }
    template <typename ...ProvidedArgs>void callWithDefault(ProvidedArgs...args){ //here
        auto neededIndecies = std::make_index_sequence<sizeof... (Args)>{};
        size_t sizeOfRemainingIndecies = sizeof... (Args) - sizeof... (args);
        callDefault(neededIndecies, args...);
    }
private:
    template<class T> T create(){
        T t; return t;
    }
     template <typename ...ProvidedArgs, size_t...indecies> void callDefault( std::index_sequence<indecies...>, ProvidedArgs...args){
        auto providedTuple = std::make_tuple(args...);
        auto providedIndecies = std::index_sequence_for<ProvidedArgs...>();
        //Approach #1: I create whole default tuple and somehow applying my provided Tuple to it
        // NeededTuple t;
        // ResultTuple r??
        // std::apply(m_function, r);

        //Aprroach #2: I make std::index_sequence with remaining indexes, like 2,3,4,5 and create remaining tuple
        //then make the resulting tuple with std::tuple_cat
        // also std::apply
    }
    size_t sizeOfNeededIndecies;
    std::function<void(Args...)> m_function;
    using NeededTuple = std::tuple<Args...>;
};

这是我的主要问题:
1) 如何将我自己的参数设置为默认元组?
2) 如何创建以 sizeOfRemainingIndecies 为起始的 index_sequence ?
3) 是否可以检查调用签名以避免 call() 中的运行时崩溃?

【问题讨论】:

    标签: c++ c++17 variadic-templates stdtuple


    【解决方案1】:

    如果您不使用 std::function 包装,但保留函子,您可能已经这样做了

    auto lambda = [](bool isSucceed, int v = 0, std::vector<uint> vec = {}){
        //doing smth
        qDebug() << "insideLamda" << isSucceed << v << vec;
    };
    Request req(lambda);
    req.onSuccess(4, std::vector<uint>{1,2});
    req.onFail();
    

    Demo

    对于CmdFu 实现,您可以这样做:

    template <typename ...Args> class CmdFu
    {
    public:
        explicit CmdFu(std::function<void(Args...)> f): m_function(f){}
    
        template <typename ...Ts>
        void call(Ts&&... args){
            m_function(std::forward<Ts>(args)...);
        }
    
        template <typename ...Ts>
        void callWithDefault(Ts&&...args)
        {
            callDefaultImpl<sizeof...(Ts)>(std::make_index_sequence<sizeof...(Args)
                                                                    - sizeof...(Ts)>{},
                                           std::forward<Ts>(args)...);
        }
    private:
    
        template <std::size_t Offset, size_t...Is, typename ...Ts>
        void callDefaultImpl(std::index_sequence<Is...>, Ts&&...args){
            m_function(std::forward<Ts>(args)...,
                       std::tuple_element_t<Offset + Is, std::tuple<Args...>>{}...);
        }
        std::function<void(Args...)> m_function;
    };
    

    Demo

    1) 如何将我自己的参数设置为默认元组?

    通过以下方式创建默认缺失参数:

    std::tuple_element_t<Offset + Is, std::tuple<Args...>>{}...
    

    2) 如何创建以 sizeOfRemainingIndecies 为起始的 index_sequence ?

    您可以创建常规索引序列和偏移量:

    template <std::size_t Offset, std::size_t... Is>
    constexpr std::index_sequence<(Offset + Is)...>
    index_sequence_with_offset(std::index_sequence<Is...>)
    { return {}; }
    

    3) 是否可以检查调用签名以避免 call() 内部的运行时崩溃?

    如果签名不匹配,它将无法编译,如果需要,您可以使用 SFINAE 丢弃无效的重载。有几种可能的语法,例如:

    template <typename ...Ts>
    auto call(Ts&&... args)
    -> decltype(m_function(std::forward<Ts>(args)...), void())
    {
        m_function(std::forward<Ts>(args)...);
    }
    

    【讨论】:

    • 在我看来,这也比 OP 的原始想法更好,因为它允许并且 使 用户明确指定要使用的默认值。 (我的意思是,至少据我所知——我当然不比他们更了解 OP 的用例)
    • 不幸的是,我不能在 lambda 中使用参数的默认值。我使用它的方式会使其语法过于繁重。它需要很多参数,并且会在函数内部动态构建。此外,我会将 cmd 保留在共享指针中,并且出于其他原因无论如何都需要包装器。 (将其保留在向量上将是多态方法)
    • @MrBaPHuk:CmdFu 的部分也为了完整性添加了。
    • Offset :0 不知道这是合法的,而且如此简单。谢谢!关于 sfinae 我试图通过 decltype 来实现,但语法有点混乱。它在运行时失败,因为整个代码有点复杂。提供实际使用 std::any 和虚拟非模板类创建的元组。我会尽快尝试并将其作为答案提交
    • 一切正常,尽管对于 decltype,m_function 应该在模板之前声明。并特别感谢完美转发。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-02
    • 1970-01-01
    • 2014-10-08
    • 1970-01-01
    • 1970-01-01
    • 2019-11-13
    相关资源
    最近更新 更多