aboutsummaryrefslogtreecommitdiff
path: root/libbuild2/variable.txx
diff options
context:
space:
mode:
Diffstat (limited to 'libbuild2/variable.txx')
-rw-r--r--libbuild2/variable.txx523
1 files changed, 451 insertions, 72 deletions
diff --git a/libbuild2/variable.txx b/libbuild2/variable.txx
index b1c4112..0b831e9 100644
--- a/libbuild2/variable.txx
+++ b/libbuild2/variable.txx
@@ -27,34 +27,6 @@ namespace build2
return false;
}
- // This one will be SFINAE'd out unless T is a simple value.
- //
- template <typename T>
- auto
- convert (names&& ns) ->
- decltype (value_traits<T>::convert (move (ns[0]), nullptr))
- {
- size_t n (ns.size ());
-
- if (n == 0)
- {
- if (value_traits<T>::empty_value)
- return T ();
- }
- else if (n == 1)
- {
- return convert<T> (move (ns[0]));
- }
- else if (n == 2 && ns[0].pair != '\0')
- {
- return convert<T> (move (ns[0]), move (ns[1]));
- }
-
- throw invalid_argument (
- string ("invalid ") + value_traits<T>::type_name +
- (n == 0 ? " value: empty" : " value: multiple names"));
- }
-
[[noreturn]] LIBBUILD2_SYMEXPORT void
convert_throw (const value_type* from, const value_type& to);
@@ -229,13 +201,13 @@ namespace build2
template <typename T>
names_view
- simple_reverse (const value& v, names& s)
+ simple_reverse (const value& v, names& s, bool reduce)
{
const T& x (v.as<T> ());
- // Represent an empty simple value as empty name sequence rather than
- // a single empty name. This way, for example, during serialization we
- // end up with a much saner looking:
+ // Unless requested otherwise, represent an empty simple value as empty
+ // name sequence rather than a single empty name. This way, for example,
+ // during serialization we end up with a much saner looking:
//
// config.import.foo =
//
@@ -245,6 +217,8 @@ namespace build2
//
if (!value_traits<T>::empty (x))
s.emplace_back (value_traits<T>::reverse (x));
+ else if (!reduce)
+ s.push_back (name ());
return s;
}
@@ -477,6 +451,7 @@ namespace build2
convert (names&& ns)
{
vector<T> v;
+ v.reserve (ns.size ()); // Normally there won't be any pairs.
// Similar to vector_append() below except we throw instead of issuing
// diagnostics.
@@ -492,7 +467,7 @@ namespace build2
if (n.pair != '@')
throw invalid_argument (
- string ("invalid pair character: '") + n.pair + "'");
+ string ("invalid pair character: '") + n.pair + '\'');
}
v.push_back (value_traits<T>::convert (move (n), r));
@@ -509,6 +484,8 @@ namespace build2
? v.as<vector<T>> ()
: *new (&v.data_) vector<T> ());
+ p.reserve (p.size () + ns.size ()); // Normally there won't be any pairs.
+
// Convert each element to T while merging pairs.
//
for (auto i (ns.begin ()); i != ns.end (); ++i)
@@ -589,8 +566,8 @@ namespace build2
}
template <typename T>
- static names_view
- vector_reverse (const value& v, names& s)
+ names_view
+ vector_reverse (const value& v, names& s, bool)
{
auto& vv (v.as<vector<T>> ());
s.reserve (vv.size ());
@@ -602,7 +579,7 @@ namespace build2
}
template <typename T>
- static int
+ int
vector_compare (const value& l, const value& r)
{
auto& lv (l.as<vector<T>> ());
@@ -624,6 +601,68 @@ namespace build2
return 0;
}
+ // Provide subscript for vector<T> for efficiency.
+ //
+ template <typename T>
+ value
+ vector_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.
+ //
+ size_t i;
+ try
+ {
+ i = static_cast<size_t> (convert<uint64_t> (move (sub)));
+ }
+ catch (const invalid_argument& e)
+ {
+ fail (sloc) << "invalid " << value_traits<vector<T>>::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& v (val.as<vector<T>> ());
+ if (i < v.size ())
+ {
+ const T& e (v[i]);
+
+ // Steal the value if possible.
+ //
+ r = &val == val_data ? T (move (const_cast<T&> (e))) : T (e);
+ }
+ }
+
+ // 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<T>::value_type;
+
+ return r;
+ }
+
+ // Provide iterate for vector<T> for efficiency.
+ //
+ template <typename T>
+ void
+ vector_iterate (const value& val,
+ const function<void (value&&, bool first)>& f)
+ {
+ const auto& v (val.as<vector<T>> ()); // Never NULL.
+
+ for (auto b (v.begin ()), i (b), e (v.end ()); i != e; ++i)
+ {
+ f (value (*i), i == b);
+ }
+ }
+
// Make sure these are static-initialized together. Failed that VC will make
// sure it's done in the wrong order.
//
@@ -635,6 +674,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 ();
@@ -651,7 +692,8 @@ namespace build2
nullptr, // Patched above.
sizeof (vector<T>),
nullptr, // No base.
- &value_traits<T>::value_type,
+ true, // Container.
+ &value_traits<T>::value_type, // Element type.
&default_dtor<vector<T>>,
&default_copy_ctor<vector<T>>,
&default_copy_assign<vector<T>>,
@@ -661,7 +703,9 @@ namespace build2
&vector_reverse<T>,
nullptr, // No cast (cast data_ directly).
&vector_compare<T>,
- &default_empty<vector<T>>
+ &default_empty<vector<T>>,
+ &vector_subscript<T>,
+ &vector_iterate<T>
};
// vector<pair<K, V>> value
@@ -701,8 +745,8 @@ namespace build2
}
template <typename K, typename V>
- static names_view
- pair_vector_reverse (const value& v, names& s)
+ names_view
+ pair_vector_reverse (const value& v, names& s, bool)
{
auto& vv (v.as<vector<pair<K, V>>> ());
s.reserve (2 * vv.size ());
@@ -714,7 +758,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>>> ());
@@ -749,10 +793,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 ();
}
};
@@ -768,10 +815,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 ();
}
};
@@ -784,11 +834,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 ();
}
};
@@ -803,7 +855,8 @@ namespace build2
nullptr, // Patched above.
sizeof (vector<pair<K, V>>),
nullptr, // No base.
- nullptr, // No element.
+ true, // Container.
+ nullptr, // No element (not named).
&default_dtor<vector<pair<K, V>>>,
&default_copy_ctor<vector<pair<K, V>>>,
&default_copy_assign<vector<pair<K, V>>>,
@@ -813,7 +866,244 @@ namespace build2
&pair_vector_reverse<K, V>,
nullptr, // No cast (cast data_ directly).
&pair_vector_compare<K, V>,
- &default_empty<vector<pair<K, V>>>
+ &default_empty<vector<pair<K, V>>>,
+ nullptr, // Subscript.
+ nullptr // Iterate.
+ };
+
+ // set<T> value
+ //
+ template <typename T>
+ set<T> value_traits<set<T>>::
+ convert (names&& ns)
+ {
+ set<T> s;
+
+ // Similar to set_append() below except we throw instead of issuing
+ // diagnostics.
+ //
+ for (auto i (ns.begin ()); i != ns.end (); ++i)
+ {
+ name& n (*i);
+ name* r (nullptr);
+
+ if (n.pair)
+ {
+ r = &*++i;
+
+ if (n.pair != '@')
+ throw invalid_argument (
+ string ("invalid pair character: '") + n.pair + '\'');
+ }
+
+ s.insert (value_traits<T>::convert (move (n), r));
+ }
+
+ return s;
+ }
+
+ template <typename T>
+ void
+ set_append (value& v, names&& ns, const variable* var)
+ {
+ set<T>& s (v ? v.as<set<T>> () : *new (&v.data_) set<T> ());
+
+ // Convert each element to T while merging pairs.
+ //
+ for (auto i (ns.begin ()); i != ns.end (); ++i)
+ {
+ name& n (*i);
+ name* r (nullptr);
+
+ if (n.pair)
+ {
+ r = &*++i;
+
+ if (n.pair != '@')
+ {
+ diag_record dr (fail);
+
+ dr << "unexpected pair style for "
+ << value_traits<T>::value_type.name << " value "
+ << "'" << n << "'" << n.pair << "'" << *r << "'";
+
+ if (var != nullptr)
+ dr << " in variable " << var->name;
+ }
+ }
+
+ try
+ {
+ s.insert (value_traits<T>::convert (move (n), r));
+ }
+ catch (const invalid_argument& e)
+ {
+ diag_record dr (fail);
+
+ dr << e;
+ if (var != nullptr)
+ dr << " in variable " << var->name;
+
+ dr << info << "while converting ";
+ if (n.pair)
+ dr << " element pair '" << n << "'@'" << *r << "'";
+ else
+ dr << " element '" << n << "'";
+ }
+ }
+ }
+
+ template <typename T>
+ void
+ set_assign (value& v, names&& ns, const variable* var)
+ {
+ if (v)
+ v.as<set<T>> ().clear ();
+
+ set_append<T> (v, move (ns), var);
+ }
+
+ template <typename T>
+ names_view
+ set_reverse (const value& v, names& s, bool)
+ {
+ auto& sv (v.as<set<T>> ());
+ s.reserve (sv.size ());
+
+ for (const T& x: sv)
+ s.push_back (value_traits<T>::reverse (x));
+
+ return s;
+ }
+
+ template <typename T>
+ int
+ set_compare (const value& l, const value& r)
+ {
+ auto& ls (l.as<set<T>> ());
+ auto& rs (r.as<set<T>> ());
+
+ auto li (ls.begin ()), le (ls.end ());
+ auto ri (rs.begin ()), re (rs.end ());
+
+ for (; li != le && ri != re; ++li, ++ri)
+ if (int r = value_traits<T>::compare (*li, *ri))
+ return r;
+
+ if (li == le && ri != re) // l shorter than r.
+ return -1;
+
+ if (ri == re && li != le) // r shorter than l.
+ return 1;
+
+ return 0;
+ }
+
+ // Map subscript to set::contains().
+ //
+ template <typename T>
+ value
+ set_subscript (const value& val, value*,
+ value&& sub,
+ const location& sloc,
+ const location& bloc)
+ {
+ // Process subscript even if the value is null to make sure it is valid.
+ //
+ T k;
+ try
+ {
+ k = convert<T> (move (sub));
+ }
+ catch (const invalid_argument& e)
+ {
+ fail (sloc) << "invalid " << value_traits<set<T>>::value_type.name
+ << " value subscript: " << e <<
+ info (bloc) << "use the '\\[' escape sequence if this is a "
+ << "wildcard pattern";
+ }
+
+ bool r (false);
+ if (!val.null)
+ {
+ const auto& s (val.as<set<T>> ());
+ r = s.find (k) != s.end ();
+ }
+
+ return value (r);
+ }
+
+ // Provide iterate for set<T> for efficiency.
+ //
+ template <typename T>
+ void
+ set_iterate (const value& val,
+ const function<void (value&&, bool first)>& f)
+ {
+ const auto& v (val.as<set<T>> ()); // Never NULL.
+
+ for (auto b (v.begin ()), i (b), e (v.end ()); i != e; ++i)
+ {
+ f (value (*i), i == b);
+ }
+ }
+
+ // Make sure these are static-initialized together. Failed that VC will make
+ // sure it's done in the wrong order.
+ //
+ template <typename T>
+ struct set_value_type: value_type
+ {
+ string type_name;
+
+ set_value_type (value_type&& v)
+ : value_type (move (v))
+ {
+ // set<T>
+ //
+ type_name = "set<";
+ type_name += value_traits<T>::type_name;
+ type_name += '>';
+ name = type_name.c_str ();
+ }
+ };
+
+ // Convenience aliases for certain set<T> cases.
+ //
+ template <>
+ struct set_value_type<string>: value_type
+ {
+ set_value_type (value_type&& v)
+ : value_type (move (v))
+ {
+ name = "string_set";
+ }
+ };
+
+ template <typename T>
+ const set<T> value_traits<set<T>>::empty_instance;
+
+ template <typename T>
+ const set_value_type<T>
+ value_traits<set<T>>::value_type = build2::value_type // VC14 wants =.
+ {
+ nullptr, // Patched above.
+ sizeof (set<T>),
+ nullptr, // No base.
+ true, // Container.
+ &value_traits<T>::value_type, // Element type.
+ &default_dtor<set<T>>,
+ &default_copy_ctor<set<T>>,
+ &default_copy_assign<set<T>>,
+ &set_assign<T>,
+ &set_append<T>,
+ &set_append<T>, // Prepend the same as append.
+ &set_reverse<T>,
+ nullptr, // No cast (cast data_ directly).
+ &set_compare<T>,
+ &default_empty<set<T>>,
+ &set_subscript<T>,
+ &set_iterate<T>
};
// map<K, V> value
@@ -839,7 +1129,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);
}
}
@@ -864,9 +1156,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));
}
}
@@ -881,8 +1171,8 @@ namespace build2
}
template <typename K, typename V>
- static names_view
- map_reverse (const value& v, names& s)
+ names_view
+ map_reverse (const value& v, names& s, bool)
{
auto& vm (v.as<map<K, V>> ());
s.reserve (2 * vm.size ());
@@ -894,7 +1184,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>> ());
@@ -918,6 +1208,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.
//
@@ -929,11 +1272,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>;
}
};
@@ -948,11 +1295,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
}
};
@@ -964,18 +1315,42 @@ 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.
+ //
+ // BTW, one negative consequence of returning untyped pair is that
+ // $first()/$second() don't return types values either, which is quite
+ // unfortunate for something like json_map.
+ //
template <typename K, typename V>
const map_value_type<K, V>
value_traits<map<K, V>>::value_type = build2::value_type // VC14 wants =
@@ -983,7 +1358,8 @@ namespace build2
nullptr, // Patched above.
sizeof (map<K, V>),
nullptr, // No base.
- nullptr, // No element.
+ true, // Container.
+ 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>>,
@@ -993,7 +1369,9 @@ namespace build2
&map_reverse<K, V>,
nullptr, // No cast (cast data_ directly).
&map_compare<K, V>,
- &default_empty<map<K, V>>
+ &default_empty<map<K, V>>,
+ nullptr, // Subscript (patched in by map_value_type above).
+ nullptr // Iterate.
};
// variable_cache
@@ -1014,8 +1392,8 @@ namespace build2
: 0);
shared_mutex& m (
- ctx.mutexes.variable_cache[
- hash<variable_cache*> () (this) % ctx.mutexes.variable_cache_size]);
+ ctx.mutexes->variable_cache[
+ hash<variable_cache*> () (this) % ctx.mutexes->variable_cache_size]);
slock sl (m);
ulock ul (m, defer_lock);
@@ -1070,6 +1448,7 @@ namespace build2
e.stem_version = sver;
+ e.value.extra = 0; // For consistency (we don't really use it).
e.value.version++; // Value changed.
}
else