繁体   English   中英

解压缩可变参数模板参数

[英]unpack variadic template parameters

我有一个模板功能:

template<typename R, typename... T>
void function(const std::string& id, R (*f)(T...)) {
    switch (sizeof...(T)) {
        case 0: f0compile<R>(reinterpret_cast<void (*)()>(f)); break;
        case 1: f1compile<R, T>(reinterpret_cast<void (*)()>(f)); break;
        case 2: f2compile<R, T1, T2>(reinterpret_cast<void (*)()>(f)); break;
    }
    ...
}

如何调用这些函数(f0compile,f1compile,f2compile)? 如何编写“功能”?

template<typename R>
void f0compile(void (*f)()) {
    new F0<R>(f):
    ...
}
template<typename R, typename T>
void f1compile(void (*f)()) {
    new F1<R,T>(f);
    ...
}
template<typename R, typename T1, typename T2>
void f2compile(void (*f)()) {
    new F2<R,T1,T2>(f);
    ...
}

感谢您对这些可变参数模板的帮助。

我添加F0 F1 F2的实现:

template <typename R> struct F0 : F {
    F0(void (*_fn)()) : F(typeid(R))
        , fn(reinterpret_cast<R(*)()>(_fn))
    {}
    const void* f() { res = fn(); return &res; }
    R res; R (*fn)();
    void d() { delete this; }
};

template <typename R, typename T> struct F1 : F {
    F1(void (*_fn)(), F* _opd) : F(typeid(R))
        , fn(reinterpret_cast<R(*)(T)>(_fn))
        , opd(autocast<T>(_opd))
    {}
    const void* f() { res = fn(*(T*) opd->f()); return &res; }
    F* opd;
    R res; R (*fn)(T);
    void d() { opd->d(); delete this; }
};

template <typename R, typename T1, typename T2> struct F2 : F {
    F2(void (*_fn)(), F* _opd1, F* _opd2) : F(typeid(R))
        , fn(reinterpret_cast<R(*)(T1,T2)>(_fn))
        , opd1(autocast<T1>(_opd1))
        , opd2(autocast<T2>(_opd2))
    {}
    const void* f() { res = fn(*(T1*) opd1->f(), *(T2*) opd2->f()); return &res; }
    F* opd1; F* opd2;
    R res; R (*fn)(T1,T2);
    void d() { opd1->d(); opd2->d(); delete this; }
};

谢谢

struct F {
            F(const std::type_info& _type) : type(_type) {}
            virtual ~F() {}
            const std::type_info& type;
            virtual const void* f() = 0;
            virtual void d() = 0;
        };      

增加了F类。 它呈现堆栈上的每个函数/操作数

template <typename T> struct Opd : F {
        Opd(T _opd) : F(typeid(T)), res(_opd) { }
        const void* f() { return &res; }
        T res;
        void d() { delete this; }
    };

添加了类Opd。 它表示堆栈上的特定操作数。

真正的程序是这样的(简体):

double foo(double op1, double op2) {
    return op1 + op2;
}

#include <functional>
#include <stack>
#include <type_traits>

class Expression {
    public:
        struct F {
            F(const std::type_info& _type) : type(_type) {}
            virtual ~F() {}
            const std::type_info& type;
            virtual const void* f() = 0;
            virtual void d() = 0;
        };
    public:
        Expression() : m_cexpr(NULL) {}
        ~Expression() {
            if (m_cexpr) m_cexpr->d();
        }
        // function
        template<typename R, typename... T> void function(R (*f)(T...), void (*compile)(void (*)(), std::stack<F*>&)) {
            m_f = std::make_pair(reinterpret_cast<void (*)()>(f), compile);
        }
        template<typename R, typename T1, typename T2> static void f2compile(void (*f)(), std::stack<F*>& s) {
            auto opd2 = s.top();
            s.pop();
            auto opd1 = s.top();
            s.pop();
            s.push(new F2<R,T1,T2>(f, opd1, opd2));
        }
        void compile() {
            if (m_cexpr) m_cexpr->d();
            std::stack<F*> s;
            s.push(new Opd<double>(1));
            s.push(new Opd<double>(2));
            m_f.second(m_f.first, s);
            m_cexpr = s.top();
            s.pop();
            assert(s.empty());
        }
        void* execute() { 
            return const_cast<void*>(m_cexpr->f()); 
        }
        const std::type_info& type() { 
            return m_cexpr->type; 
        }
    private:
        F* m_cexpr;
        std::pair<void (*)(), void (*)(void (*)(), std::stack<F*>&)> m_f;
        template <typename T> struct Opd : F {
            Opd(T _opd) : F(typeid(T)), res(_opd) {}
            const void* f() { return &res; }
            T res;
            void d() { delete this; }
        };
        template <typename R, typename T1, typename T2> struct F2 : F {
            F2(void (*_fn)(), F* _opd1, F* _opd2) : F(typeid(R))
                , fn(reinterpret_cast<R(*)(T1,T2)>(_fn))
                , opd1(_opd1)
                , opd2(_opd2)
            {}
            const void* f() { res = fn(*(T1*) opd1->f(), *(T2*) opd2->f()); return &res; }
            F* opd1; F* opd2;
            R res; R (*fn)(T1,T2);
            void d() { opd1->d(); opd2->d(); delete this; }
        };
};

TEST_CASE("expression") {
    Expression e;
    e.function(foo, e.f2compile<double, double, double>);
    e.compile();
    e.execute();
    REQUIRE(e.type() == typeid(double));
    REQUIRE(*static_cast<double*>(e.execute()) == 3);
}

我的问题是如何使用可变参数模板编写更好的代码c ++ 11。 如何使用可变参数模板编写函数“ fNcompile”和函数“ FN”。

我认为您不需要可变参数模板。 代替:

template<typename R>
void fcompile(void (*f)()) {
    new F0<R>(reinterpret_cast<void (*)()>(f));
    ...
}
template<typename R, typename T>
void fcompile(void (*f)(T)) {
    new F1<R,T>(reinterpret_cast<void (*)()>(f));
    ...
}
template<typename R, typename T1, typename T2>
void fcompile(void (*f)(T1, T2)) {
    new F1<R,T1,T2>(reinterpret_cast<void (*)()>(f));
    ...
}

现在,您可以调用fcompile<some_type>(some_func)来获取任何some_type以及返回void的任何some_type / unary / binary some_func

为了回答特定的问题,下面是可变参数FNfNcompilefNcompile尽可能接近您的现有代码。 但是,首先,由于您说的是在C ++ 11中工作,因此我们需要C ++ 14中等效的std::make_index_sequence 这是一个简单的例子。 您可以搜索其他更聪明的方法来避免受到编译器模板限制的影响...

namespace cxx_compat {
    template <typename T, T... Values>
    struct integer_sequence {
        static constexpr std::size_t size() const
        { return sizeof...(Values); }
    };

    template <typename T, T Smallest, T... Values>
    struct make_integer_sequence_helper {
        static_assert(Smallest > 0,
            "make_integer_sequence argument must not be negative");
        using type = typename make_integer_sequence_helper<
            T, Smallest-1, Smallest-1, Values...>::type;
    };
    template <typename T, T... Values>
    struct make_integer_sequence_helper<T, 0, Values...> {
        using type = integer_sequence<T, Values...>;
    };

    template <typename T, T N>
    using make_integer_sequence =
        typename make_integer_sequence_helper<T, N>::type;

    template <std::size_t... Values>
    using index_sequence = integer_sequence<std::size_t, Values...>;

    template <std::size_t N>
    using make_index_sequence = make_integer_sequence<std::size_t, N>;

    template <typename... T>
    using index_sequence_for = make_index_sequence<sizeof...(T)>;
} // end namespace cxx_compat

现在,实际的FNfNcompile

template <typename R, typename ...T> struct FN : F {
private:
    template <typename T>
    using any_to_Fstar = F*;
public:
    FN(void (*_fn)(), any_to_Fstar<T> ... _opd) : F(typeid(R))
        , fn(reinterpret_cast<R(*)(T...)>(_fn))
        , opd{_opd...}
    {}
    FN(R (*_fn)(T...)) : F(typeid(R)), fn(_fn), opd() {}
    const void* f() {
        f_helper(cxx_compat::index_sequence_for<T...>{});
        return &res;
    }
    std::array<F*, sizeof...(T)> opd;
    R res; R (*fn)(T...);
    void d() {
        for (F* o : opd)
            o->d();
        delete this;
    }
private:
    template <std::size_t... Inds>
    void f_helper(cxx_compat::index_sequence<Inds...>)
    { res = fn(*(T*) opd[Inds]->f() ...); }
};

template<typename R, typename... T>
static void fNcompile(void (*f)(), std::stack<F*>& s) {
    auto* f_obj = new FN<R, T...>(f);
    for (std::size_t ind = sizeof...(T); ind > 0;) {
        f_obj->opd[--ind] = s.top();
        s.pop();
    }
    s.push(f_obj);
}

这是怎么回事:

  • 为了实际调用函数指针,我们需要同时访问多个函数参数,因此要使用由模板实例化确定的F*指针替换命名成员opd1opd2 ,我们使用std::array<F*, sizeof...(T)> ,因为sizeof...(T)是提供给模板的参数类型的数量。

  • 为了与您声明的F2构造函数兼容, any_to_Fstar<T> ... _opd声明了许多构造函数参数以匹配T模板参数的数量,所有这些参数均具有相同的F*类型。 (但是现在fNcompile使用附加的构造函数,而只使用函数指针,然后再设置数组成员。)

  • 要获得这些指针和他们都传递给fn在一个表达,我们需要扩大某种可变参数组。 这是index_sequence来源:

    • index_sequence_for<T...>index_sequence的类型别名,具有index_sequence的数字序列作为模板参数。 例如,如果sizeof...(T)为4,则index_sequence_for<T...>index_sequence<0, 1, 2, 3>

    • f只是调用一个私有函数f_helper ,将其传递给那个index_sequence_for<T...>类型的对象。

    • 编译器可以通过匹配index_sequence类型来推导f_helper的模板参数列表: Inds...必须是相同的数字序列, index_sequence计数。

    • f_helper主体中,通过扩展模板参数包TInds来实例化表达式fn(*(T*) opd[Inds]->f() ...) ,以获取用于调用fn的函数参数列表。

但是,使用void指针和reinterpret_cast是危险的,在C ++中很少需要。 使用模板几乎总是一种更安全的方法。 因此,我将其重新设计为更像:

#include <type_traits>
#include <typeinfo>
#include <stdexcept>
#include <memory>
#include <stack>

namespace cxx_compat {
    // Define integer_sequence and related templates as above.

    template <typename T, typename... Args>
    std::unique_ptr<T> make_unique(Args&& ... args)
    {
        return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
    }
} // end namespace cxx_compat

class bad_expression_type : public std::logic_error
{
public:
    bad_expression_type(const std::type_info& required,
                        const std::type_info& passed)
        : logic_error("bad_argument_type"),
          required_type(required),
          passed_type(passed) {}
    const std::type_info& required_type;
    const std::type_info& passed_type;
};

class Expression
{
public:
    class F
    {
    public:
        F() noexcept = default;
        F(const F&) = delete;
        F& operator=(const F&) = delete;
        virtual ~F() = default;
        virtual const std::type_info& type() const noexcept = 0;
        virtual void compile(std::stack<std::unique_ptr<F>>&) = 0;

        template <typename R>
        R call_R() const;
    };
    using F_ptr = std::unique_ptr<F>;
    using F_stack = std::stack<F_ptr>;

    template <typename R>
    class Typed_F : public F
    {
    public:
        const std::type_info& type() const noexcept override
        { return typeid(R); }
        virtual R call() const = 0;
    };

    // Accepts any callable: function pointer, lambda, std::function,
    // other class with operator().
    template <typename R, typename... T, typename Func,
        typename = typename std::enable_if<std::is_convertible<
            decltype(std::declval<const Func&>()(std::declval<T>()...)),
            R>::value>::type>
    void function(Func func)
    {
        store_func<R, T...>(std::move(func));
    }
    // Overload for function pointer that does not need explicit
    // template arguments:
    template <typename R, typename... T>
    void function(R (*fptr)(T...))
    {
        store_func<R, T...>(fptr);
    }

    template <typename T>
    void constant(const T& value)
    {
        store_func<T>([value](){ return value; });
    }

    void compile(F_stack& stack)
    {
        m_cexpr->compile(stack);
    }

private:
    template <typename Func, typename R, typename... T>
    class F_Impl : public Typed_F<R>
    {
    public:
        F_Impl(Func func) : m_func(std::move(func)) {}
        void compile(F_stack& stack) override {
            take_args_helper(stack, cxx_compat::index_sequence_for<T...>{});
        }
        R call() const override {
            return call_helper(cxx_compat::index_sequence_for<T...>{});
        }
    private:
        template <typename Arg>
        int take_one_arg(std::unique_ptr<Typed_F<Arg>>& arg, F_stack& stack)
        {
            auto* fptr = dynamic_cast<Typed_F<Arg>*>(stack.top().get());
            if (!fptr)
                throw bad_expression_type(
                    typeid(Arg), stack.top()->type());
            arg.reset(fptr);
            stack.top().release();
            stack.pop();
            return 0;
        }
        template <std::size_t... Inds>
        void take_args_helper(F_stack& stack, cxx_compat::index_sequence<Inds...>)
        {
            using int_array = int[];
            (void) int_array{ take_one_arg(std::get<Inds>(m_args), stack) ..., 0 };
        }
        template <std::size_t... Inds>
        R call_helper(cxx_compat::index_sequence<Inds...>) const {
            return m_func(std::get<Inds>(m_args)->call()...);
        }

        Func m_func;
        std::tuple<std::unique_ptr<Typed_F<T>>...> m_args;
    };

    template <typename R, typename... T, typename Func>
    void store_func(Func func)
    {
        m_cexpr = cxx_compat::make_unique<F_Impl<Func, R, T...>>(
            std::move(func));
    }

    F_ptr m_cexpr;
};

template <typename R>
R Expression::F::call_R() const
{
    auto* typed_this = dynamic_cast<const Typed_F<R>*>(this);
    if (!typed_this)
        throw bad_expression_type(typeid(R), type());
    return typed_this->call();
}

TEST_CASE("expression") {
    Expression a;
    a.constant(1.0);
    Expression b;
    b.constant(2.0);
    Expression c;
    c.function(+[](double x, double y) { return x+y; });

    Expression::F_stack stack;
    a.compile(stack);
    REQUIRE(stack.size() == 1);
    b.compile(stack);
    REQUIRE(stack.size() == 2);
    c.compile(stack);
    REQUIRE(stack.size() == 1);

    REQUIRE(stack.top() != nullptr);
    REQUIRE(stack.top()->type() == typeid(double));
    REQUIRE(stack.top()->call_R<double>() == 3.0);
}

例如,使用std::string(*)()函数作为unsigned int(*)(const std::string&)的参数,也可以支持参数和结果类型的引用和const变体,但有点棘手。 unsigned int(*)(const std::string&)函数。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM