解压缩可变参数模板参数

时间:2018-07-16 11:39:35

标签: c++ c++11

我有一个模板功能:

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”。

2 个答案:

答案 0 :(得分:3)

我认为您不需要可变参数模板。相反:

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的{​​{1}}。

答案 1 :(得分:0)

为回答特定的问题,下面的变量FNfNcompile与现有代码尽可能接近。不过,首先,由于您说的是在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);
}

发生了什么事

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

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

  • 要获得这些指针并将它们全部传递到一个表达式中的F*,我们需要扩展某种可变参数包。这是fNcompile出现的地方:

    • fnindex_sequence的类型别名,其数字序列从零开始作为模板参数。例如,如果index_sequence_for<T...>为4,则index_sequencesizeof...(T)

    • index_sequence_for<T...>仅调用私有函数index_sequence<0, 1, 2, 3>,并向其传递f类型的对象。

    • 编译器可以通过匹配f_helper类型来推导index_sequence_for<T...>的模板参数列表:f_helper必须是从零开始计数的相同数字序列。

    • index_sequence主体中,通过扩展模板参数包Inds...f_helper实例化表达式fn(*(T*) opd[Inds]->f() ...),以获得一个函数参数列表致电T

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

void

支持参数和结果类型的引用和reinterpret_cast变体也是可能的,但是有点棘手,例如,使用#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); } 函数作为{{1 }}功能。