简体   繁体   English

使用可变参数模板的递归元函数

[英]Recursive metafunction using variadic templates

I am writing a meta function replace_type<C, X, Y> that is supposed to replace all matches of type X in a compound type C with Y . 我写的元函数replace_type<C, X, Y>是应该更换类型的所有比赛X中的化合物类型CY I am currently working on properly getting this to work with callables in C . 我目前正在努力使这与C callables一起使用。

This works: 这有效:

template replace_type<
    typename C, typename X, typename Y,
    typename First
>
struct replace_type<C(First), X, Y>
{
    typedef typename replace_type<
        C, X, Y
    >::type type(
        typename replace_type<
            First, X, Y
        >::type
    );
};

template replace_type<
    typename C, typename X, typename Y,
    typename First, typename Second
>
struct replace_type<C(First, Second), X, Y>
{
    typedef typename replace_type<
        C, X, Y
    >::type type(
        typename replace_type<
            First, X, Y
        >::type,
        typename replace_type<
            Second, X, Y
        >::type
    );
};

But this obviously is very limited. 但这显然非常有限。 In my head it seemed obvious that I should be using a variadic template here instead, but when I actually tried applying it, I quickly noticed I have no idea of how to fit it into this scheme. 在我看来,我似乎应该在这里使用可变参数模板,但是当我真正尝试应用它时,我很快就注意到我不知道如何将它融入到这个方案中。

I thought of implementing it like this: 我想过像这样实现它:

template replace_type<
    typename C, typename X, typename Y,
    typename First, typename... Args
>
struct replace_type<C(First, Args...), X, Y>
{
    typedef typename replace_type<
        C, X, Y
    >::type type(
        typename replace_type<
            First, X, Y
        >::type,
        // How to recursively do the same with the rest of the arguments?
    );
};

This way, I could always access the first parameter to replace it appropriately, then move on to the next, and have another specialized metafunction ready for handling nullary functions as terminal condition for my recursion. 这样,我总是可以访问第一个参数来适当地替换它,然后继续下一个,并准备好另一个专门的元函数来处理nullary函数作为递归的终端条件。 Question is, as stated in the source code, how do I start the recursion in this context? 问题是,如源代码中所述,如何在此上下文中启动递归?

Update 更新

Minimal example: 最小的例子:

#include <type_traits>

namespace type_replace_helper
{
    template <typename, typename, typename>
    struct type_replace_base;
}

template <typename C, typename X, typename Y>
struct type_replace
{
    typedef typename std::conditional<
        std::is_same<C, X>::value,
        Y,
        typename type_replace_helper::type_replace_base<
            C, X, Y
        >::type
    >::type type;
};

namespace type_replace_helper
{
    template <typename C, typename X, typename Y>
    struct type_replace_base
    {
        typedef C type;
    };

    template <typename C, typename X, typename Y>
    struct type_replace_base<C(), X, Y>
    {
        typedef typename type_replace<
            C, X, Y
        >::type type();
    };

    template <
        typename C, typename X, typename Y,
        typename First
    >
    struct type_replace_base<C(First), X, Y>
    {
        typedef typename type_replace<
            C, X, Y
        >::type type(
            typename type_replace<
                First, X, Y
            >::type
        );
    };

    template <
        typename C, typename X, typename Y,
        typename First, typename Second
    >
    struct type_replace_base<C(First, Second), X, Y>
    {
        typedef typename type_replace<
            C, X, Y
        >::type type(
            typename type_replace<
                First, X, Y
            >::type,
            typename type_replace<
                Second, X, Y
            >::type
        );
    };
}

int main()
{
    static_assert(std::is_same<
        type_replace<int(int, int), int, long>::type,
        long(long, long)
    >::value, "int should be replaced by long");
    return 0;
}

Update 2 更新2

Thanks to Crazy Eddie I've been able to achieve what I wanted. 感谢Crazy Eddie,我已经能够实现我想要的。 Because it took me so long to understand this beast answer, I figured it might be helpful for others to read a more verbose solution. 因为我花了这么长时间来理解这个野兽的答案,我认为其他人阅读更详细的解决方案可能会有所帮助。

The thing I took probably the longest to actually realize: the problem is not really how to separate the function parameters, but transform them into a variadic list of replaced arguments. 我所做的事情可能是最长的实际意识:问题不在于如何分离函数参数,而是它们转换为替换参数的可变参数列表。 Thus, the primary objective here is to find a way to replace each argument properly, and store it into another separate argument list. 因此,这里的主要目标是找到一种正确替换每个参数的方法,并将其存储到另一个单独的参数列表中。 Eddy's solution uses the stack structure as a wrapper to distinct the two parameter lists, one replaced, one with things left to do. Eddy的解决方案使用stack结构作为包装来区分两个参数列表,一个被替换,一个还剩下要做的事情。

Once the parameter list has been replaced one by one and was stored in the stack structure, all that's left to do is pull them out again as a list and construct the function like so: typedef T type(Params...); 一旦参数列表被逐个替换并存储在stack结构中,剩下要做的就是将它们作为列表再次拉出并构造如下的函数: typedef T type(Params...); , and that's it. 就是这样。

In my coding style this displays as: 在我的编码风格中,它显示为:

template <typename...>
struct stack {};

// Definition only to specialize for actual stacks
template <
    typename X, typename Y,
    typename Stack, typename... Todo
>
struct list_converter;

// No more arguments to convert, return the gathered stack
template <
    typename X, typename Y,
    typename... Elems
>
struct list_converter<X, Y, stack<Elems...>>
{
    typedef stack<Elems...> type;
};

// Push replaced argument to stack and go to the next argument
template <
    typename X, typename Y,
    typename... Elems,
    typename First, typename... Todo
>
struct list_converter<X, Y, stack<Elems...>, First, Todo...>
{
    typedef typename list_converter<
        X, Y,
        stack<
            typename replace_type<First, X, Y>::type,
            Elems...
        >,
        Todo...
    >::type type;
};

// Definition only again for stack specialization
template <
    typename C, typename X, typename Y,
    typename Stack
>
struct function_builder;

// Pull out argument list from the stack and build a function
template <
    typename C, typename X, typename Y,
    typename... Elems
>
struct function_builder<C, X, Y, stack<Elems...>>
{
    typedef typename replace_type<
        C, X, Y
    >::type type(Elems...);
};

// Specialization for function replacements
// Builds function with replaced return type, and converted
// argument list (recursion starts with empty stack)
template <
    typename C, typename X, typename Y,
    typename... Params
>
struct replace_type<C(Params...), X, Y>
{
    typedef typename function_builder<
        C, X, Y,
        typename list_converter<
            X, Y,
            stack<>,
            Params...
        >::type
    >::type type;
};

Please forgive me if there are some syntactical errors in the code above; 如果上面的代码中存在一些语法错误,请原谅我; it is quite a large file already and I tried to only extract relevant information. 它已经是一个非常大的文件,我试图只提取相关信息。

Expansion of variadic packs can handle a so-called pattern. 可变背包的扩展可以处理所谓的模式。 So you can use one partial specialization for everything. 所以你可以使用一个部分专业化的一切。

template replace_type<
    typename R, typename... Args,
    typename X, typename Y
>
struct replace_type<R(Args...), X, Y>
{
    typedef typename replace_type<
        R, X, Y
    >::type type(
        typename replace_type<
            Args, X, Y
        >::type...
    );
};
template < typename ... A >
struct stack { };

template < typename Stack, typename T >
struct push_front;

template < typename T, typename ... A >
struct push_front<stack<A...>,T> {
    typedef stack<T, A ... > type;
};

template < typename Ret, typename Args >
struct build_fun;

template < typename Ret, typename ... A >
struct build_fun<Ret, stack<A...> > {
    typedef Ret(*fptr)(A...);
    typedef decltype(*static_cast<fptr>(0)) type;
};

template < typename Match, typename Rep, typename Target >
struct replace_match { typedef Target type; };

template < typename Match, typename Rep >
struct replace_match<Match, Rep, Match> { typedef Rep type; };

template < typename Match, typename Rep, typename ... Types >
struct replace;

template < typename Match, typename Rep, typename Head, typename ... Tail >
struct replace<Match,Rep,Head,Tail...>
{
    typedef typename replace_match<Match,Rep,Head>::type my_match;

    typedef typename replace<Match, Rep, Tail...>::type next_set;

    typedef typename push_front<next_set, my_match>::type type;
};

template < typename Match, typename Rep >
struct replace<Match,Rep>
{
    typedef stack<> type;
};

template < typename Sig, typename Match, typename Rep>
struct replace_fun_args;

template < typename R, typename Match, typename Rep, typename ... Args >
struct replace_fun_args
{
    typedef typename replace<Match, Rep, Args...>::type arg_stack;
    typedef typename build_fun<R,arg_stack>::type type;
};

#include <iostream>
#include <typeinfo>

int main() {

    replace<int,char,double,unsigned int, int, char*>::type t;

    std::cout << typeid(build_fun<void,decltype(t)>::type).name() << std::endl;
}

There's probably a way to just use packs instead of the stack template...need to look up how to build a pack from types. 可能有一种方法只使用包而不是stack模板...需要查找如何从类型构建包。

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

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