From a2353bc0411329fa89e837bd06c869b5367381ba Mon Sep 17 00:00:00 2001 From: Tom Jackson Date: Mon, 13 Mar 2017 11:09:00 -0700 Subject: [PATCH] get_ptr(mapOfMaps, key, key...) Summary: Since this is a not-uncommon pattern now: ```lang=cpp if (auto found1 = get_ptr(m, k1)) { if (auto found2 = get_ptr(*found, k2)) { return use(*found2); } } ``` This diff enables: ```lang=cpp if (auto found = get_ptr(m, k1, k2)) { return use(*found); } ``` Note that it works for const and non-const maps, returning a correspondingly mutable pointer. Reviewed By: luciang, yfeldblum Differential Revision: D3812701 fbshipit-source-id: 77ace9f5dca6cdc4cae7e6dfb9e5fc1075b5b571 --- folly/MapUtil.h | 37 +++++++++++++++++++++++++++++++++++ folly/test/MapUtilTest.cpp | 40 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+) diff --git a/folly/MapUtil.h b/folly/MapUtil.h index 54ed0105..063bb0f4 100644 --- a/folly/MapUtil.h +++ b/folly/MapUtil.h @@ -148,4 +148,41 @@ typename Map::mapped_type* get_ptr( return (pos != map.end() ? &pos->second : nullptr); } +namespace detail { +template < + class T, + size_t pathLength, + class = typename std::enable_if<(pathLength > 0)>::type> +struct NestedMapType { + using type = typename NestedMapType::type::mapped_type; +}; + +template +struct NestedMapType { + using type = typename T::mapped_type; +}; +} + +/** + * Given a map of maps and a path of keys, return a pointer to the nested value, + * or nullptr if the key doesn't exist in the map. + */ +template +auto get_ptr( + const Map& map, + const Key1& key1, + const Key2& key2, + const Keys&... keys) -> + typename detail::NestedMapType::type const* { + auto pos = map.find(key1); + return pos != map.end() ? get_ptr(pos->second, key2, keys...) : nullptr; +} + +template +auto get_ptr(Map& map, const Key1& key1, const Key2& key2, const Keys&... keys) + -> typename detail::NestedMapType::type* { + auto pos = map.find(key1); + return pos != map.end() ? get_ptr(pos->second, key2, keys...) : nullptr; +} + } // namespace folly diff --git a/folly/test/MapUtilTest.cpp b/folly/test/MapUtilTest.cpp index 7f28291f..1fa91df0 100644 --- a/folly/test/MapUtilTest.cpp +++ b/folly/test/MapUtilTest.cpp @@ -17,6 +17,7 @@ #include #include +#include #include @@ -98,3 +99,42 @@ TEST(MapUtil, get_ptr) { *get_ptr(m, 1) = 4; EXPECT_EQ(4, m.at(1)); } + +TEST(MapUtil, get_ptr_path_simple) { + using std::map; + map>>> m{{1, {{2, {{3, {{4, 5}}}}}}}}; + EXPECT_EQ(5, *get_ptr(m, 1, 2, 3, 4)); + EXPECT_TRUE(get_ptr(m, 1, 2, 3, 4)); + EXPECT_FALSE(get_ptr(m, 1, 2, 3, 0)); + EXPECT_TRUE(get_ptr(m, 1, 2, 3)); + EXPECT_FALSE(get_ptr(m, 1, 2, 0)); + EXPECT_TRUE(get_ptr(m, 1, 2)); + EXPECT_FALSE(get_ptr(m, 1, 0)); + EXPECT_TRUE(get_ptr(m, 1)); + EXPECT_FALSE(get_ptr(m, 0)); + const auto& cm = m; + ++*get_ptr(m, 1, 2, 3, 4); + EXPECT_EQ(6, *get_ptr(cm, 1, 2, 3, 4)); + EXPECT_TRUE(get_ptr(cm, 1, 2, 3, 4)); + EXPECT_FALSE(get_ptr(cm, 1, 2, 3, 0)); +} + +TEST(MapUtil, get_ptr_path_mixed) { + using std::map; + using std::unordered_map; + using std::string; + unordered_map>> m{{"a", {{1, {{"b", 7}}}}}}; + EXPECT_EQ(7, *get_ptr(m, "a", 1, "b")); + EXPECT_TRUE(get_ptr(m, "a", 1, "b")); + EXPECT_FALSE(get_ptr(m, "b", 1, "b")); + EXPECT_FALSE(get_ptr(m, "a", 2, "b")); + EXPECT_FALSE(get_ptr(m, "a", 1, "c")); + EXPECT_TRUE(get_ptr(m, "a", 1, "b")); + EXPECT_TRUE(get_ptr(m, "a", 1)); + EXPECT_TRUE(get_ptr(m, "a")); + const auto& cm = m; + ++*get_ptr(m, "a", 1, "b"); + EXPECT_EQ(8, *get_ptr(cm, "a", 1, "b")); + EXPECT_TRUE(get_ptr(cm, "a", 1, "b")); + EXPECT_FALSE(get_ptr(cm, "b", 1, "b")); +} -- 2.34.1