diff options
author | Boris Kolpackov <boris@codesynthesis.com> | 2024-02-19 10:34:40 +0200 |
---|---|---|
committer | Boris Kolpackov <boris@codesynthesis.com> | 2024-02-19 12:44:32 +0200 |
commit | bed6b6a9170253e010cbffd59202add4edfd1c2b (patch) | |
tree | ee33d1ad1f43bfc0966b8f85f7f84ad6272b1e71 /libbuild2/variable.txx | |
parent | 70d63c266ffd313c03f6cf68e7080bbcd3c8c064 (diff) |
Add string_map buildfile value type
This exposes the std::map<std::string,std::string> type to buildfiles.
New functions:
$size(<string-map>)
$keys(<string-map>)
Subscript can be used to lookup a value by key. The result is [null] if
there is no value associated with the specified key. For example:
map = [string_map] a@1 b@2 c@3
b = ($map[b]) # 2
if ($map[z] == [null])
...
Note that append (+=) is overriding (like std::map::insert_or_assign())
while prepend (=+) is not (like std::map::insert()). In a sense, whatever
appears last (from left to right) is kept, which is consistent with what
we expect to happen when specifying the same key repeatedly in a literal
representation. For example:
map = [string_map] a@0 b@2 a@1 # a@1 b@2
map += b@0 c@3 # a@1 b@0 c@3
map =+ b@1 d@4 # a@1 b@0 c@3 d@4
Example of iteration:
map = [string_map] a@1 b@2 c@3
for p: $map
{
k = $first($p)
v = $second($p)
}
While the subscript is mapped to key lookup only, index-based access can be
implemented (with a bit of overhead) using the $keys() function:
map = [string_map] a@1 b@2 c@3
keys = $keys($m)
for i: $integer_sequence(0, $size($keys))
{
k = ($keys[$i])
v = ($map[$k])
}
Also, this commit changes the naming of other template-based value types (not
exposed as buildfile value types) to use C++ template id-like names (e.g.,
map<string,optional<bool>>).
Diffstat (limited to 'libbuild2/variable.txx')
-rw-r--r-- | libbuild2/variable.txx | 151 |
1 files changed, 121 insertions, 30 deletions
diff --git a/libbuild2/variable.txx b/libbuild2/variable.txx index bc4132f..485f9dc 100644 --- a/libbuild2/variable.txx +++ b/libbuild2/variable.txx @@ -591,7 +591,7 @@ namespace build2 } template <typename T> - static names_view + names_view vector_reverse (const value& v, names& s, bool) { auto& vv (v.as<vector<T>> ()); @@ -604,7 +604,7 @@ namespace build2 } template <typename T> - static int + int vector_compare (const value& l, const value& r) { auto& lv (l.as<vector<T>> ()); @@ -637,6 +637,8 @@ namespace build2 vector_value_type (value_type&& v) : value_type (move (v)) { + // Note: vector<T> always has a convenience alias. + // type_name = value_traits<T>::type_name; type_name += 's'; name = type_name.c_str (); @@ -706,7 +708,7 @@ namespace build2 } template <typename K, typename V> - static names_view + names_view pair_vector_reverse (const value& v, names& s, bool) { auto& vv (v.as<vector<pair<K, V>>> ()); @@ -719,7 +721,7 @@ namespace build2 } template <typename K, typename V> - static int + int pair_vector_compare (const value& l, const value& r) { auto& lv (l.as<vector<pair<K, V>>> ()); @@ -754,10 +756,13 @@ namespace build2 pair_vector_value_type (value_type&& v) : value_type (move (v)) { - type_name = value_traits<K>::type_name; - type_name += '_'; + // vector<pair<K,V>> + // + type_name = "vector<pair<"; + type_name += value_traits<K>::type_name; + type_name += ','; type_name += value_traits<V>::type_name; - type_name += "_pair_vector"; + type_name += ">>"; name = type_name.c_str (); } }; @@ -773,10 +778,13 @@ namespace build2 pair_vector_value_type (value_type&& v) : value_type (move (v)) { - type_name = value_traits<K>::type_name; - type_name += "_optional_"; + // vector<pair<K,optional<V>>> + // + type_name = "vector<pair<"; + type_name += value_traits<K>::type_name; + type_name += ",optional<"; type_name += value_traits<V>::type_name; - type_name += "_pair_vector"; + type_name += ">>>"; name = type_name.c_str (); } }; @@ -789,11 +797,13 @@ namespace build2 pair_vector_value_type (value_type&& v) : value_type (move (v)) { - type_name = "optional_"; + // vector<pair<optional<K>,V>> + // + type_name = "vector<pair<optional<"; type_name += value_traits<K>::type_name; - type_name += '_'; + type_name += ">,"; type_name += value_traits<V>::type_name; - type_name += "_pair_vector"; + type_name += ">>"; name = type_name.c_str (); } }; @@ -847,7 +857,9 @@ namespace build2 "element", var)); - p.emplace (move (v.first), move (v.second)); + // Poor man's emplace_or_assign(). + // + p.emplace (move (v.first), V ()).first->second = move (v.second); } } @@ -872,9 +884,7 @@ namespace build2 "element", var)); - // Poor man's emplace_or_assign(). - // - p.emplace (move (v.first), V ()).first->second = move (v.second); + p.emplace (move (v.first), move (v.second)); } } @@ -889,7 +899,7 @@ namespace build2 } template <typename K, typename V> - static names_view + names_view map_reverse (const value& v, names& s, bool) { auto& vm (v.as<map<K, V>> ()); @@ -902,7 +912,7 @@ namespace build2 } template <typename K, typename V> - static int + int map_compare (const value& l, const value& r) { auto& lm (l.as<map<K, V>> ()); @@ -926,6 +936,59 @@ namespace build2 return 0; } + // Note that unlike json_value, we don't provide index support for maps. + // There are two reasons for this: Firstly, consider map<uint64_t,...>. + // Secondly, even something like map<string,...> may contain integers as + // keys (in JSON, there is a strong convention for object member names not + // to be integers). Instead, we provide the $keys() function which allows + // one to implement an index-based access with a bit of overhead, if needed. + // + template <typename K, typename V> + value + map_subscript (const value& val, value* val_data, + value&& sub, + const location& sloc, + const location& bloc) + { + // Process subscript even if the value is null to make sure it is valid. + // + K k; + try + { + k = convert<K> (move (sub)); + } + catch (const invalid_argument& e) + { + fail (sloc) << "invalid " << value_traits<map<K, V>>::value_type.name + << " value subscript: " << e << + info (bloc) << "use the '\\[' escape sequence if this is a " + << "wildcard pattern"; + } + + value r; + if (!val.null) + { + const auto& m (val.as<map<K, V>> ()); + auto i (m.find (k)); + if (i != m.end ()) + { + // Steal the value if possible. + // + r = (&val == val_data + ? V (move (const_cast<V&> (i->second))) + : V (i->second)); + } + } + + // Typify null values so that type-specific subscript (e.g., for + // json_value) gets called for chained subscripts. + // + if (r.null) + r.type = &value_traits<V>::value_type; + + return r; + } + // Make sure these are static-initialized together. Failed that VC will make // sure it's done in the wrong order. // @@ -937,11 +1000,15 @@ namespace build2 map_value_type (value_type&& v) : value_type (move (v)) { - type_name = value_traits<K>::type_name; - type_name += '_'; + // map<K,V> + // + type_name = "map<"; + type_name += value_traits<K>::type_name; + type_name += ','; type_name += value_traits<V>::type_name; - type_name += "_map"; + type_name += '>'; name = type_name.c_str (); + subscript = &map_subscript<K, V>; } }; @@ -956,11 +1023,15 @@ namespace build2 map_value_type (value_type&& v) : value_type (move (v)) { - type_name = value_traits<K>::type_name; - type_name += "_optional_"; + // map<K,optional<V>> + // + type_name = "map<"; + type_name += value_traits<K>::type_name; + type_name += ",optional<"; type_name += value_traits<V>::type_name; - type_name += "_map"; + type_name += ">>"; name = type_name.c_str (); + // @@ TODO: subscript } }; @@ -972,18 +1043,38 @@ namespace build2 map_value_type (value_type&& v) : value_type (move (v)) { - type_name = "optional_"; + // map<optional<K>,V> + // + type_name = "map<optional<"; type_name += value_traits<K>::type_name; - type_name += '_'; + type_name += ">,"; type_name += value_traits<V>::type_name; - type_name += "_map"; + type_name += '>'; name = type_name.c_str (); + // @@ TODO: subscript + } + }; + + // Convenience aliases for certain map<T,T> cases. + // + template <> + struct map_value_type<string, string>: value_type + { + map_value_type (value_type&& v) + : value_type (move (v)) + { + name = "string_map"; + subscript = &map_subscript<string, string>; } }; template <typename K, typename V> const map<K, V> value_traits<map<K, V>>::empty_instance; + // Note that custom iteration would be better (more efficient, return typed + // value), but we don't yet have pair<> as value type so we let the generic + // implementation return an untyped pair. + // template <typename K, typename V> const map_value_type<K, V> value_traits<map<K, V>>::value_type = build2::value_type // VC14 wants = @@ -992,7 +1083,7 @@ namespace build2 sizeof (map<K, V>), nullptr, // No base. true, // Container. - nullptr, // No element (not named). + nullptr, // No element (pair<> not a value type yet). &default_dtor<map<K, V>>, &default_copy_ctor<map<K, V>>, &default_copy_assign<map<K, V>>, @@ -1003,7 +1094,7 @@ namespace build2 nullptr, // No cast (cast data_ directly). &map_compare<K, V>, &default_empty<map<K, V>>, - nullptr, // Subscript. + nullptr, // Subscript (patched in by map_value_type above). nullptr // Iterate. }; |