简单的MPL类型的地图模板

时间:2014-02-27 06:36:02

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

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

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

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

(像这样的东西会是理想的,但我会很满意一个简单的一对一类型地图,真的)

1 个答案:

答案 0 :(得分:0)

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

模板:

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模板,让每个“Map节点”处理2个头类型(键和值)。然后用法看起来像这样:typedef TMap<Foo, int, Bar, float, void, string> tMap;,看起来更灵活,但可能会隐藏错误。
  • Variadic模板和模板别名使这个非常流畅,只有20行左右的代码。