【问题标题】:Simple MPL-like type map template简单的类似 MPL 的类型映射模板
【发布时间】:2014-02-27 06:36:01
【问题描述】:

我正在尝试找到一种优雅的方式来实现类似非常简单的boost::mpl 类型映射。
在我的上下文中,使用 MPL 或任何其他 boost 库不是一个选项。此外,我真正需要的唯一操作是初始化和查找。存储编译时值也很好(如果我使用基于boost 的解决方案,我可以使用boost::fusion::set 来做到这一点)。
我知道如何使用特征类做我想做的事,但我认为语法有点过于冗长,并且还有一些其他的小缺点,例如你不能在类声明中定义特征特化,而且,通常它不会整齐地放在一个地方。

这是我想要使用特征实现的目标:

// base class definitions
class Foo {};
class Bar {};
class Baz {};

// traits base
template<typename T>
struct MyTraits {};

// traits specializations
template<>
struct MyTraits<Foo> {
    static constexpr const char* name = "foo";
    typedef double type;
};
template<>
struct MyTraits<Bar> {
    static constexpr const char* name = "bar";
    typedef std::string type;
};

// generic worker function
template<typename T>
void doStuff(const T& arg) {
    std::cout << "got a " << MyTraits<T>::name << " - size: " << sizeof(typename MyTraits<T>::type) << std::endl;
}

int main(int, char*[]) {
    Foo foo;
    Bar bar;
    Baz baz;

    doStuff(foo);
    doStuff(bar);
    // this fails, as expected:
    //doStuff(baz);
}

这是我希望能够使用的语法:

typedef TMapTemplate<
    TMapEntry<Foo, double, "foo">,
    TMapEntry<Bar, string, "bar">
> tTraitsMap;

// generic worker function
template<typename T>
void doStuff(const T& arg) {
    std::cout << "got a " << at<tTraitsMap,T>::value<1>() << " - size: " << sizeof(at<tTraitsMap,T>::type<0>) << std::endl;
}

(这样的东西会很理想,但我会很高兴有一个简单的一对一类型映射,真的)

【问题讨论】:

  • 如果你不能使用 Boost,你将如何使用fusion::set
  • @TemplateRex: 我不会使用融合... @ 解决类型到值的问题(因为 mpl 没有提供)。进行了相应的编辑以澄清
  • 只是好奇,为什么不能为此使用 Boost?假设您将使用bcp toolboost::mpl::map 中提取所有依赖项,这会构成库解决方案还是您自己的私有代码?
  • @TemplateRex:这个 issue 出现的项目有严格的库控制政策,boost 是禁区。我想如果需要,我可以按照您的建议提取相关代码,并且可以接受。在实践中,特质类可以很好地满足我们的需求,所以我的问题的实际方面到此结束。但是,我一直认为这种映射不会太难用新的 C++11 语言结构(如可变参数模板、元组等)来表达,所以这更像是一个风格练习题。

标签: c++ templates c++11 map boost-mpl


【解决方案1】:

所以,我最终修补了一些或多或少符合我想要的东西。它无论如何都不完美,但确实符合我想要的简单版本:

模板:

struct NotFound {};

template <typename TK, typename TV>
struct TMapEntry {
};

template <typename... TEntries>
struct TMap {
};

template <typename TK, typename TV, typename... TEntries>
struct TMap<TMapEntry<TK, TV>, TEntries...> {
    typedef TMap<TEntries...> tNext;
    template<typename T>
    struct HeadResolver {
        static constexpr bool match = is_same<TK, T>::value;
    };

    template<typename T>
    using at = typename conditional<HeadResolver<T>::match, TV, typename tNext::template at<T>>::type;
};

template<>
struct TMap<> {
    template<typename T>
    using at = NotFound;
};

用法:

class Foo {};
class Bar {};
class Baz {};

void test() {
    typedef TMap<> tEmptyMap;
    static_assert(is_same<tEmptyMap::at<Foo>, NotFound>::value, "found something in empty map");
    static_assert(is_same<tEmptyMap::at<int>, NotFound>::value, "found something in empty map");

    typedef TMap<
        TMapEntry<Foo, int>,
        TMapEntry<Bar, float>,
        TMapEntry<void, string>
    > tMap;
    static_assert(is_same<tMap::at<Foo>, int>::value, "map mismatch");
    static_assert(is_same<tMap::at<Bar>, float>::value, "map mismatch");
    static_assert(is_same<tMap::at<void>, string>::value, "map mismatch");
    static_assert(is_same<tMap::at<Baz>, NotFound>::value, "map mismatch");
} 

这样做时我想到的几点:

  • 它不检查或保证密钥的唯一性。
  • 我可以完全删除 TMapEntry 模板,让每个“地图节点”处理 2 种头类型(键和值)。然后用法看起来像这样:typedef TMap&lt;Foo, int, Bar, float, void, string&gt; tMap;,看起来更灵活,但可能会隐藏错误。
  • 可变参数模板和模板别名使这一过程非常流畅,只需 20 行左右的代码。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-11-13
    • 2012-01-14
    • 1970-01-01
    • 1970-01-01
    • 2011-09-16
    • 1970-01-01
    • 2021-08-15
    相关资源
    最近更新 更多