开发者

Parameter pack template metaprogramming depth first search

I have a parameter class like so:

template <class KEY, class VALUE&开发者_JAVA百科gt;
class parameter
{
   VALUE v;
   ...
};

And I have a parameter pack class:

template <class... ELEMENTS>
class parameter_pack 
{
  std::tuple<ELEMENTS...> elems;
};

parameter_packs contain a number of parameters and/or parameter_packs.

I want a function (or method) like the following:

template <class KEY_TO_GET, class PARAMETER_PACK>
... get_value(const PARAMETER_PACK& p) { ... }

that does a depth first traversal for the first parameter that has the KEY value and returns its value. Of course this should be done at compile time, and should have constant runtime. Note KEY here is just a empty class, it is never created or used.

I'd like to be able to do this from the left or from the right.

The idea being you can have a set of default parameters which you can save to a variable and override before passing them to functions. I don't think boost::parameter allows saving (due to references to temporaries), and I believe it gives errors when you double up on a parameter.


I might have got it wrong, but isn't it something similar to the code below what you are looking for?

#include <iostream>
#include <utility>

class nill
{

};

template<typename KEY, typename VALUE, VALUE v>
class parameter
{
    public:
        typedef KEY key_type;
        typedef VALUE value_type;
        static constexpr VALUE value = v;
};

template<typename ...>
class parameter_pack
{
};

template<typename KEY, typename P>
class get_param;

template<typename KEY, typename H, typename ...TAIL>
class get_param<KEY, parameter_pack<H, TAIL...>>
{
    private:
        typedef typename get_param<KEY, H>::type result;

    public:
        typedef typename std::conditional<std::is_same<result, nill>::value,
                typename get_param<KEY, parameter_pack<TAIL...>>::type, result>::type
                type;
};

template<typename KEY>
class get_param<KEY, parameter_pack<>>
{
    public:
        typedef nill type;
};

template<typename KEY, typename K, typename V, V v>
class get_param<KEY, parameter<K, V, v>>
{
    public:
        typedef typename std::conditional<std::is_same<K, KEY>::value,
                parameter<K, V, v> , nill>::type type;
};

template<unsigned T>
class tag
{
};

int main()
{
    typedef parameter_pack
            <
                parameter_pack
                <
                    parameter<tag<0>, int, 0>
                >,
                parameter_pack
                <
                    parameter<tag<4>, int, 42>,
                    parameter<tag<2>, int, 1>,
                    parameter_pack
                    <
                        parameter<tag<3>, int, 100>,
                        parameter<tag<4>, int, 5>,
                        parameter<tag<0>, int, 33>,
                        parameter<tag<2>, int, 666>
                    >
                >,
                parameter<tag<1>, int, -1>
            > pack;

    typedef typename get_param<tag<4> , pack>::type param;

    std::cout << param::value << '\n';

    return 0;
}

Output:

42

Compiled by GCC 4.6

EDITED TO ALLOW RUN TIME VALUE MODIFICATION:

For run time modification of valuess just modify the parameter class to the following:

template<typename KEY, typename VALUE>
class parameter
{
    public:
        typedef KEY key_type;
        typedef VALUE value_type;
        static VALUE value;
};

template<typename KEY, typename VALUE>
VALUE parameter<KEY, VALUE>::value;

which does not have a constant value member anymore.

Note that the value is a static variable tough, which means changing the value of one entry with a particular key changes the values of all the other entries. To overcome this problem, one may simply modify the "tagging system" so that two tags of different types may be considered the same key and then allowing the user to specify which meta function get_param should use to corectly identify the desired key (instead of always using std::is_same). So the complete example which now allows run time modification of values is presented below:

#include <iostream>
#include <utility>

class nill
{

};

template<typename KEY, typename VALUE>
class parameter
{
    public:
        typedef KEY key_type;
        typedef VALUE value_type;
        static VALUE value;
};

template<typename KEY, typename VALUE>
VALUE parameter<KEY, VALUE>::value;

template<typename ...>
class parameter_pack
{
};

template<template<typename> class MF, typename P>
class get_param;

template<template<typename> class MF, typename H, typename ...TAIL>
class get_param<MF, parameter_pack<H, TAIL...>>
{
    private:
        typedef typename get_param<MF, H>::type result;

    public:
        typedef typename std::conditional<std::is_same<result, nill>::value,
                typename get_param<MF, parameter_pack<TAIL...>>::type, result>::type
                type;
};

template<template<typename> class MF>
class get_param<MF, parameter_pack<>>
{
    public:
        typedef nill type;
};

template<template<typename> class MF, typename K, typename V>
class get_param<MF, parameter<K, V>>
{
    public:
        typedef typename std::conditional<MF<K>::value,
                parameter<K, V> , nill>::type type;
};

template<unsigned T, unsigned U = 0>
class tag
{
};

template<typename K1, typename K2>
class compare_tag;

template<unsigned T1, unsigned U1, unsigned T2, unsigned U2>
class compare_tag<tag<T1, U1>, tag<T2, U2>>
{
    public:
        static constexpr bool value = T1 == T2;
};

template<typename T>
class find4 : public compare_tag<T, tag<4>>
{};

template<typename T>
class find2 : public compare_tag<T, tag<2>>
{};

template<typename T>
class find1 : public compare_tag<T, tag<1>>
{};

int main()
{
    typedef parameter_pack
            <
                parameter_pack
                <
                    parameter<tag<0, 0>, int>
                >,
                parameter_pack
                <
                    parameter<tag<4, 0>, int>,
                    parameter<tag<2, 0>, int>,
                    parameter_pack
                    <
                        parameter<tag<3, 0>, int>,
                        parameter<tag<4, 1>, int>,
                        parameter<tag<0, 1>, int>,
                        parameter<tag<2, 1>, int>
                    >
                >,
                parameter<tag<1, 0>, int>
            > pack;

    std::cin >> get_param<find4, pack>::type::value;
    std::cin >> get_param<find2, pack>::type::value;
    std::cin >> get_param<find1, pack>::type::value;

    std::cout << get_param<find4, pack>::type::value << '\n';
    std::cout << get_param<find2, pack>::type::value << '\n';
    std::cout << get_param<find1, pack>::type::value << '\n';

    return 0;
}

Output:

42
666
0
42
666
0


This is what I have so far. I can locate the proper parameter, I can std::get my way to it by hand, but it seems that at this hour I can't write the final wrapper that automatically does it for me for the life of me.

A proper solution should probably use Boost.MPL so as to not reinvent common techniques (e.g. sequences & push_back). It could also benefit from some lazy instantiations, because right now my solution touches every node.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜