参数包模板元编程深度优先搜索

问题描述 投票:0回答:3

我有一个像这样的参数类:

template <class KEY, class VALUE>
class parameter
{
   VALUE v;
   ...
};

我有一个参数包类:

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

parameter_packs
包含多个
parameters
和/或
parameter_packs

我想要一个如下所示的函数(或方法):

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

对具有

parameter
值的第一个
KEY
进行深度优先遍历并返回其值。当然,这应该在编译时完成,并且应该具有恒定的运行时间。注意
KEY
这里只是一个空类,它从未被创建或使用。

我希望能够从左侧或右侧执行此操作。

这个想法是您可以拥有一组默认参数,您可以将它们保存到变量中并在将它们传递给函数之前覆盖它们。我不认为 boost::parameter 允许保存(由于对临时变量的引用),并且我相信当您加倍参数时它会出错。

c++ c++11 template-meta-programming
3个回答
2
投票

我可能弄错了,但它不是与您正在寻找的下面的代码类似吗?

#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;
}

输出:

42

GCC 4.6编译

编辑以允许修改运行时值:

要在运行时修改值,只需将参数类修改为以下内容:

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;

不再有常量值成员。

请注意,该值是一个静态变量,这意味着使用特定键更改一个条目的值会更改所有其他条目的值。为了克服这个问题,可以简单地修改“标签系统”,以便两个不同类型的标签可以被认为是相同的键,然后允许用户指定应该使用哪个元函数 get_param 来正确识别所需的键(而不是总是使用 std::is_same)。 因此,现在允许运行时修改值的完整示例如下所示:

#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;
}

输出:

42
666
0
42
666
0

0
投票

是我到目前为止所拥有的。我可以找到正确的

parameter
,我可以手动
std::get
找到它,但似乎在这个时候我无法编写最终的包装器来自动为我做这件事。

正确的解决方案可能应该使用 Boost.MPL,以免重新发明常用技术(例如序列和

push_back
)。它还可以从一些惰性实例化中受益,因为现在我的解决方案涉及每个节点。


0
投票

这个问题很久以前就被问到了,但我遇到了类似的问题,所以我给出了可能的方法来解决它(需要 C++17 for std::apply)

#include <iostream>
#include <tuple>
#include <string>

template <class KEY, class VALUE>
struct parameter { VALUE v; };

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

struct key1; struct key2; struct key3; struct key4; struct key5;

using P1 = parameter<key1, int>;
using P2 = parameter<key2, float>;
using P3 = parameter<key3, std::string>;
using P4 = parameter<key4, const char *>;
using P5 = parameter<key5, std::tuple<int, float, bool>>;
using PP1 = parameter_pack<P1, P2>;
using PP2 = parameter_pack<PP1, P3, P4>;
using PP3 = parameter_pack<PP2, P5>;


// Try to match the first parameter.
// If it matchs, returns it, if not remove it from the list and
// call get again on the remaining
template <class Key, class SomeKey, class Val, class...Ps>
auto &get(parameter<SomeKey, Val>& p, Ps&...ps) {
       if constexpr (std::is_same_v<Key, SomeKey>) {
           return p;
       } else {
           return get<Key>(ps...);
       }
}

// Replace the first parameter_pack by its elements
// And recall get on the result.
template <class Key, class... P, class...Ps>
auto &get(parameter_pack<P...>&p, Ps&...ps) {
    return std::apply([&ps...] (auto&...p_)-> auto & { return get<Key>(p_..., ps...); }, p.elems);
}

int main() {
    PP3 pp;
    get<key1>(pp).v = 1;
    get<key2>(pp).v = 1.23456789;
    get<key3>(pp).v = std::string{"Hello World!"};
    get<key4>(pp).v = "litteral string";
    get<key5>(pp).v = std::make_tuple(1, 2.3, true);

    std::cout << get<key1>(pp).v << '\n'
              << get<key2>(pp).v << '\n'
              << get<key3>(pp).v << '\n'
              << get<key4>(pp).v << '\n';
   std::apply([&] (auto&...t) { std::cout << ( (std::to_string(t) + ' ') +...) << '\n'; }, get<key5>(pp).v);

}

希望它对某人有用。

© www.soinside.com 2019 - 2024. All rights reserved.