首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >解包可变模板参数

解包可变模板参数
EN

Stack Overflow用户
提问于 2018-07-16 11:39:34
回答 2查看 1.1K关注 0票数 0

我有一个模板函数:

代码语言:javascript
复制
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)?我怎么写“函数”?

代码语言:javascript
复制
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的实现:

代码语言:javascript
复制
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; }
};

谢谢

代码语言:javascript
复制
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级。它在堆栈上显示每个函数/操作数。

代码语言:javascript
复制
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级。它表示堆栈上的特定操作数。

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

代码语言:javascript
复制
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”。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2018-07-21 18:18:41

为了回答具体问题,下面是尽可能接近现有代码的各种FNfNcompile。不过,首先,既然您说您是在C++11工作,那么我们需要一个与C++14类似的std::make_index_sequence。您可以搜索其他更聪明的人,不太可能遇到编译器模板限制.

代码语言:javascript
复制
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

代码语言:javascript
复制
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);
}

怎么回事:

  • 要实际调用函数指针,我们需要同时访问多个函数参数,因此要用模板实例化确定的多个opd1指针替换命名成员opd2,需要使用std::array<F*, sizeof...(T)>,因为sizeof...(T)是提供给模板的参数类型的数量。
  • 为了与您声明的F2构造函数兼容,any_to_Fstar<T> ... _opd声明了许多构造函数参数,以匹配T模板参数的数量,这些参数都具有相同的F*类型。(但是现在fNcompile只使用函数指针的附加构造函数,然后设置数组成员。)
  • 要获得这些指针并在一个表达式中将它们全部传递给fn,我们需要扩展某种变量包。这里是index_sequence出现的地方:
代码语言:javascript
复制
- `index_sequence_for<T...>` is a type alias for `index_sequence` with a sequence of numbers counting up from zero as template arguments.  For example, if `sizeof...(T)` is 4, then `index_sequence_for<T...>` is `index_sequence<0, 1, 2, 3>`.
- `f` just calls a private function `f_helper`, passing it an object of that `index_sequence_for<T...>` type.
- The compiler can deduce the template argument list for `f_helper` from matching the `index_sequence` types: `Inds...` must be that same sequence of numbers counting up from zero.
- In the `f_helper` body, the expression `fn(*(T*) opd[Inds]->f() ...)` is instantiated by expanding both the template parameter packs `T` and `Inds` to get one list of function arguments for calling `fn`.

然而,使用void指针和reinterpret_cast是危险的,在C++中很少有必要使用。使用模板几乎总是有一种更安全的方法。所以我会重新设计它,让它更像:

代码语言:javascript
复制
#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);
}

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

票数 0
EN

Stack Overflow用户

发布于 2018-07-16 11:53:47

我觉得你不需要各种模板。相反:

代码语言:javascript
复制
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和任何返回some_type的髓/一元/二进制some_func

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/51360969

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档