From: Nick Terrell Date: Thu, 30 Mar 2017 19:48:11 +0000 (-0700) Subject: Path variants for get_(ref_)default() X-Git-Tag: v2017.04.03.00~7 X-Git-Url: http://plrg.eecs.uci.edu/git/?p=folly.git;a=commitdiff_plain;h=cc84c39c1c042b20af82b8f54d72a6bf65deb58e Path variants for get_(ref_)default() Summary: Useful for the same reason as the `get_ptr()` path function, but when you want to use a default value. Reviewed By: luciang, yfeldblum Differential Revision: D4785728 fbshipit-source-id: 70fd56f9ffa7a9edd6740f6dd712d3a251bf9fb0 --- diff --git a/folly/MapUtil.h b/folly/MapUtil.h index 77e092f6..d6be5711 100644 --- a/folly/MapUtil.h +++ b/folly/MapUtil.h @@ -18,6 +18,7 @@ #include #include +#include namespace folly { @@ -166,6 +167,8 @@ typename Map::mapped_type* get_ptr( return (pos != map.end() ? &pos->second : nullptr); } +// TODO: Remove the return type computations when clang 3.5 and gcc 5.1 are +// the minimum supported versions. namespace detail { template < class T, @@ -179,6 +182,25 @@ template struct NestedMapType { using type = typename T::mapped_type; }; + +template +struct DefaultType; + +template +struct DefaultType { + using type = Default; +}; + +template +struct DefaultType { + using type = typename DefaultType::type; +}; + +template +auto extract_default(const KeysDefault&... keysDefault) -> + typename DefaultType::type const& { + return std::get(std::tie(keysDefault...)); +} } /** @@ -203,4 +225,53 @@ auto get_ptr(Map& map, const Key1& key1, const Key2& key2, const Keys&... keys) return pos != map.end() ? get_ptr(pos->second, key2, keys...) : nullptr; } +/** + * Given a map and a path of keys, return the value corresponding to the nested + * value, or a given default value if the path doesn't exist in the map. + * The default value is the last parameter, and is copied when returned. + */ +template < + class Map, + class Key1, + class Key2, + class... KeysDefault, + typename = typename std::enable_if::type> +auto get_default( + const Map& map, + const Key1& key1, + const Key2& key2, + const KeysDefault&... keysDefault) -> + typename detail::NestedMapType::type { + if (const auto* ptr = get_ptr(map, key1)) { + return get_default(*ptr, key2, keysDefault...); + } + return detail::extract_default(keysDefault...); +} + +/** + * Given a map and a path of keys, return a reference to the value corresponding + * to the nested value, or the given default reference if the path doesn't exist + * in the map. + * The default value is the last parameter, and must be a lvalue reference. + */ +template < + class Map, + class Key1, + class Key2, + class... KeysDefault, + typename = typename std::enable_if::type, + typename = typename std::enable_if::type>::value>::type> +auto get_ref_default( + const Map& map, + const Key1& key1, + const Key2& key2, + KeysDefault&&... keysDefault) -> + typename detail::NestedMapType::type + const& { + if (const auto* ptr = get_ptr(map, key1)) { + return get_ref_default(*ptr, key2, keysDefault...); + } + return detail::extract_default(keysDefault...); +} } // namespace folly diff --git a/folly/test/MapUtilTest.cpp b/folly/test/MapUtilTest.cpp index 33ffe066..32ffdba4 100644 --- a/folly/test/MapUtilTest.cpp +++ b/folly/test/MapUtilTest.cpp @@ -141,6 +141,19 @@ TEST(MapUtil, get_ptr_path_mixed) { } namespace { +template +struct element_type { + using type = typename std::decay::type; +}; + +template +struct element_type { + using type = T; +}; + +template +using element_type_t = typename element_type::type; + template struct Compiles : std::false_type {}; @@ -148,7 +161,7 @@ template struct Compiles< T, void_t::type>>(), + std::declval>>(), std::declval(), std::declval()))>> : std::true_type {}; } @@ -158,4 +171,78 @@ TEST(MapUtil, get_default_temporary) { EXPECT_TRUE(Compiles::value); EXPECT_FALSE(Compiles::value); EXPECT_FALSE(Compiles::value); + + EXPECT_TRUE(Compiles::value); + EXPECT_TRUE(Compiles::value); + EXPECT_FALSE(Compiles::value); +} + +TEST(MapUtil, get_default_path) { + using std::map; + map> m; + m[4][2] = 42; + EXPECT_EQ(42, get_default(m, 4, 2, 42)); + EXPECT_EQ(42, get_default(m, 1, 3, 42)); +} + +TEST(MapUtil, get_default_path_mixed) { + using std::map; + using std::unordered_map; + using std::string; + map> m; + int key1 = 42; + const string key2 = "hello"; + constexpr StringPiece value = "world"; + constexpr StringPiece dflt = "default"; + m[key1][key2] = value; + EXPECT_EQ(value, get_default(m, 42, key2, dflt)); + EXPECT_EQ(value, get_default(m, key1, "hello", dflt)); + EXPECT_EQ(dflt, get_default(m, 0, key2, dflt)); + EXPECT_EQ(dflt, get_default(m, key1, "bad", "default")); +} + +TEST(MapUtil, get_ref_default_path) { + using std::map; + map> m; + m[4][2] = 42; + const int dflt = 13; + EXPECT_EQ(42, get_ref_default(m, 4, 2, dflt)); + EXPECT_EQ(dflt, get_ref_default(m, 1, 3, dflt)); +} + +TEST(MapUtil, get_ref_default_path_mixed) { + using std::map; + using std::unordered_map; + using std::string; + map> m; + int key1 = 42; + const string key2 = "hello"; + constexpr StringPiece value = "world"; + constexpr StringPiece dflt = "default"; + m[key1][key2] = value; + EXPECT_EQ(value, get_ref_default(m, 42, key2, dflt)); + EXPECT_EQ(value, get_ref_default(m, key1, "hello", dflt)); + EXPECT_EQ(dflt, get_ref_default(m, 0, key2, dflt)); + EXPECT_EQ(dflt, get_ref_default(m, key1, "bad", dflt)); +} + +namespace { +template +struct GetRefDefaultPathCompiles : std::false_type {}; + +template +struct GetRefDefaultPathCompiles< + T, + void_t>>>(), + std::declval(), + std::declval(), + std::declval()))>> : std::true_type {}; +} + +TEST(MapUtil, get_ref_default_path_temporary) { + EXPECT_TRUE(GetRefDefaultPathCompiles::value); + EXPECT_TRUE(GetRefDefaultPathCompiles::value); + EXPECT_FALSE(GetRefDefaultPathCompiles::value); + EXPECT_FALSE(GetRefDefaultPathCompiles::value); }