aboutsummaryrefslogtreecommitdiff
path: root/libbuild2/variable.txx
diff options
context:
space:
mode:
Diffstat (limited to 'libbuild2/variable.txx')
-rw-r--r--libbuild2/variable.txx1074
1 files changed, 842 insertions, 232 deletions
diff --git a/libbuild2/variable.txx b/libbuild2/variable.txx
index 8fa1d7c..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);
@@ -128,6 +100,7 @@ namespace build2
{
size_t n (ns.size ());
+ diag_record dr;
if (value_traits<T>::empty_value ? n <= 1 : n == 1)
{
try
@@ -137,19 +110,23 @@ namespace build2
(n == 0
? T ()
: value_traits<T>::convert (move (ns.front ()), nullptr)));
-
- return;
}
- catch (const invalid_argument&) {} // Fall through.
+ catch (const invalid_argument& e)
+ {
+ dr << fail << e;
+ }
}
+ else
+ dr << fail << "invalid " << value_traits<T>::value_type.name
+ << " value: " << (n == 0 ? "empty" : "multiple names");
- diag_record dr (fail);
-
- dr << "invalid " << value_traits<T>::value_type.name
- << " value '" << ns << "'";
+ if (!dr.empty ())
+ {
+ if (var != nullptr)
+ dr << " in variable " << var->name;
- if (var != nullptr)
- dr << " in variable " << var->name;
+ dr << info << "while converting '" << ns << "'";
+ }
}
template <typename T>
@@ -158,6 +135,7 @@ namespace build2
{
size_t n (ns.size ());
+ diag_record dr;
if (value_traits<T>::empty_value ? n <= 1 : n == 1)
{
try
@@ -167,19 +145,23 @@ namespace build2
(n == 0
? T ()
: value_traits<T>::convert (move (ns.front ()), nullptr)));
-
- return;
}
- catch (const invalid_argument&) {} // Fall through.
+ catch (const invalid_argument& e)
+ {
+ dr << fail << e;
+ }
}
+ else
+ dr << fail << "invalid " << value_traits<T>::value_type.name
+ << " value: " << (n == 0 ? "empty" : "multiple names");
- diag_record dr (fail);
-
- dr << "invalid " << value_traits<T>::value_type.name
- << " value '" << ns << "'";
+ if (!dr.empty ())
+ {
+ if (var != nullptr)
+ dr << " in variable " << var->name;
- if (var != nullptr)
- dr << " in variable " << var->name;
+ dr << info << "while converting '" << ns << "'";
+ }
}
template <typename T>
@@ -188,6 +170,7 @@ namespace build2
{
size_t n (ns.size ());
+ diag_record dr;
if (value_traits<T>::empty_value ? n <= 1 : n == 1)
{
try
@@ -197,30 +180,34 @@ namespace build2
(n == 0
? T ()
: value_traits<T>::convert (move (ns.front ()), nullptr)));
-
- return;
}
- catch (const invalid_argument&) {} // Fall through.
+ catch (const invalid_argument& e)
+ {
+ dr << fail << e;
+ }
}
+ else
+ dr << fail << "invalid " << value_traits<T>::value_type.name
+ << " value: " << (n == 0 ? "empty" : "multiple names");
- diag_record dr (fail);
-
- dr << "invalid " << value_traits<T>::value_type.name
- << " value '" << ns << "'";
+ if (!dr.empty ())
+ {
+ if (var != nullptr)
+ dr << " in variable " << var->name;
- if (var != nullptr)
- dr << " in variable " << var->name;
+ dr << info << "while converting '" << ns << "'";
+ }
}
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 =
//
@@ -230,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;
}
@@ -241,14 +230,228 @@ namespace build2
return value_traits<T>::compare (l.as<T> (), r.as<T> ());
}
- // vector<T> value
+ // pair<F, S> value
//
+ template <typename F, typename S>
+ pair<F, S> pair_value_traits<F, S>::
+ convert (name&& l, name* r,
+ const char* type, const char* what, const variable* var)
+ {
+ if (!l.pair)
+ {
+ diag_record dr (fail);
+
+ dr << type << ' ' << what << (*what != '\0' ? " " : "")
+ << "pair expected instead of '" << l << "'";
+
+ if (var != nullptr)
+ dr << " in variable " << var->name;
+ }
+
+ if (l.pair != '@')
+ {
+ diag_record dr (fail);
+
+ dr << "unexpected pair style for "
+ << type << ' ' << what << (*what != '\0' ? " " : "")
+ << "key-value pair '"
+ << l << "'" << l.pair << "'" << *r << "'";
+
+ if (var != nullptr)
+ dr << " in variable " << var->name;
+ }
+
+ try
+ {
+ F f (value_traits<F>::convert (move (l), nullptr));
+
+ try
+ {
+ S s (value_traits<S>::convert (move (*r), nullptr));
+
+ return pair<F, S> (move (f), move (s));
+ }
+ catch (const invalid_argument& e)
+ {
+ diag_record dr (fail);
+
+ dr << e;
+ if (var != nullptr)
+ dr << " in variable " << var->name;
+
+ dr << info << "while converting second have of pair '" << *r << "'"
+ << endf;
+ }
+ }
+ catch (const invalid_argument& e)
+ {
+ diag_record dr (fail);
+
+ dr << e;
+ if (var != nullptr)
+ dr << " in variable " << var->name;
+
+ dr << info << "while converting first have of pair '" << l << "'"
+ << endf;
+ }
+ }
+
+ template <typename F, typename S>
+ pair<F, optional<S>> pair_value_traits<F, optional<S>>::
+ convert (name&& l, name* r,
+ const char* type, const char* what, const variable* var)
+ {
+ if (l.pair && l.pair != '@')
+ {
+ diag_record dr (fail);
+
+ dr << "unexpected pair style for "
+ << type << ' ' << what << (*what != '\0' ? " " : "")
+ << "key-value pair '"
+ << l << "'" << l.pair << "'" << *r << "'";
+
+ if (var != nullptr)
+ dr << " in variable " << var->name;
+ }
+
+ try
+ {
+ F f (value_traits<F>::convert (move (l), nullptr));
+
+ try
+ {
+ optional<S> s;
+
+ if (l.pair)
+ s = value_traits<S>::convert (move (*r), nullptr);
+
+ return pair<F, optional<S>> (move (f), move (s));
+ }
+ catch (const invalid_argument& e)
+ {
+ diag_record dr (fail);
+
+ dr << e;
+ if (var != nullptr)
+ dr << " in variable " << var->name;
+
+ dr << info << "while converting second have of pair '" << *r << "'"
+ << endf;
+ }
+ }
+ catch (const invalid_argument& e)
+ {
+ diag_record dr (fail);
+
+ dr << e;
+ if (var != nullptr)
+ dr << " in variable " << var->name;
+ dr << info << "while converting first have of pair '" << l << "'"
+ << endf;
+ }
+ }
+
+ template <typename F, typename S>
+ pair<optional<F>, S> pair_value_traits<optional<F>, S>::
+ convert (name&& l, name* r,
+ const char* type, const char* what, const variable* var)
+ {
+ if (l.pair && l.pair != '@')
+ {
+ diag_record dr (fail);
+
+ dr << "unexpected pair style for "
+ << type << ' ' << what << (*what != '\0' ? " " : "")
+ << "key-value pair '"
+ << l << "'" << l.pair << "'" << *r << "'";
+
+ if (var != nullptr)
+ dr << " in variable " << var->name;
+ }
+
+ try
+ {
+ optional<F> f;
+
+ if (l.pair)
+ {
+ f = value_traits<F>::convert (move (l), nullptr);
+ l = move (*r); // Shift.
+ }
+
+ try
+ {
+ S s (value_traits<S>::convert (move (l), nullptr));
+
+ return pair<optional<F>, S> (move (f), move (s));
+ }
+ catch (const invalid_argument& e)
+ {
+ diag_record dr (fail);
+
+ dr << e;
+ if (var != nullptr)
+ dr << " in variable " << var->name;
+
+ dr << info << "while converting second have of pair '" << *r << "'"
+ << endf;
+ }
+ }
+ catch (const invalid_argument& e)
+ {
+ diag_record dr (fail);
+
+ dr << e;
+ if (var != nullptr)
+ dr << " in variable " << var->name;
+
+ dr << info << "while converting first have of pair '" << l << "'"
+ << endf;
+ }
+ }
+
+ template <typename F, typename S>
+ void pair_value_traits<F, S>::
+ reverse (const F& f, const S& s, names& ns)
+ {
+ ns.push_back (value_traits<F>::reverse (f));
+ ns.back ().pair = '@';
+ ns.push_back (value_traits<S>::reverse (s));
+ }
+
+ template <typename F, typename S>
+ void pair_value_traits<F, optional<S>>::
+ reverse (const F& f, const optional<S>& s, names& ns)
+ {
+ ns.push_back (value_traits<F>::reverse (f));
+ if (s)
+ {
+ ns.back ().pair = '@';
+ ns.push_back (value_traits<S>::reverse (*s));
+ }
+ }
+
+ template <typename F, typename S>
+ void pair_value_traits<optional<F>, S>::
+ reverse (const optional<F>& f, const S& s, names& ns)
+ {
+ if (f)
+ {
+ ns.push_back (value_traits<F>::reverse (*f));
+ ns.back ().pair = '@';
+ }
+ ns.push_back (value_traits<S>::reverse (s));
+ }
+
+ // vector<T> value
+ //
template <typename T>
vector<T> value_traits<vector<T>>::
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.
@@ -264,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));
@@ -281,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)
@@ -309,19 +514,19 @@ namespace build2
{
p.push_back (value_traits<T>::convert (move (n), r));
}
- catch (const invalid_argument&)
+ catch (const invalid_argument& e)
{
diag_record dr (fail);
- dr << "invalid " << value_traits<T>::value_type.name;
+ 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 << "'";
-
- if (var != nullptr)
- dr << " in variable " << var->name;
}
}
}
@@ -361,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 ());
@@ -374,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>> ());
@@ -396,27 +601,99 @@ 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>
- value_traits<vector<T>>::value_type_ex::
- value_type_ex (value_type&& v)
- : value_type (move (v))
+ void
+ vector_iterate (const value& val,
+ const function<void (value&&, bool first)>& f)
{
- type_name = value_traits<T>::type_name;
- type_name += 's';
- name = type_name.c_str ();
+ 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.
+ //
+ template <typename T>
+ struct vector_value_type: value_type
+ {
+ string type_name;
+
+ 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 ();
+ }
+ };
+
template <typename T>
const vector<T> value_traits<vector<T>>::empty_instance;
template <typename T>
- const typename value_traits<vector<T>>::value_type_ex
+ const vector_value_type<T>
value_traits<vector<T>>::value_type = build2::value_type // VC14 wants =.
{
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>>,
@@ -426,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
@@ -444,63 +723,14 @@ namespace build2
for (auto i (ns.begin ()); i != ns.end (); ++i)
{
name& l (*i);
+ name* r (l.pair ? &*++i : nullptr);
- if (!l.pair)
- {
- diag_record dr (fail);
+ p.push_back (value_traits<pair<K, V>>::convert (
+ move (l), r,
+ value_traits<vector<pair<K, V>>>::value_type.name,
+ "element",
+ var));
- dr << value_traits<vector<pair<K, V>>>::value_type.name
- << " key-value pair expected instead of '" << l << "'";
-
- if (var != nullptr)
- dr << " in variable " << var->name;
- }
-
- name& r (*++i); // Got to have the second half of the pair.
-
- if (l.pair != '@')
- {
- diag_record dr (fail);
-
- dr << "unexpected pair style for "
- << value_traits<vector<pair<K, V>>>::value_type.name
- << " key-value '" << l << "'" << l.pair << "'" << r << "'";
-
- if (var != nullptr)
- dr << " in variable " << var->name;
- }
-
- try
- {
- K k (value_traits<K>::convert (move (l), nullptr));
-
- try
- {
- V v (value_traits<V>::convert (move (r), nullptr));
-
- p.emplace_back (move (k), move (v));
- }
- catch (const invalid_argument&)
- {
- diag_record dr (fail);
-
- dr << "invalid " << value_traits<V>::value_type.name
- << " element value '" << r << "'";
-
- if (var != nullptr)
- dr << " in variable " << var->name;
- }
- }
- catch (const invalid_argument&)
- {
- diag_record dr (fail);
-
- dr << "invalid " << value_traits<K>::value_type.name
- << " element key '" << l << "'";
-
- if (var != nullptr)
- dr << " in variable " << var->name;
- }
}
}
@@ -515,24 +745,20 @@ 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 ());
for (const auto& p: vv)
- {
- s.push_back (value_traits<K>::reverse (p.first));
- s.back ().pair = '@';
- s.push_back (value_traits<V>::reverse (p.second));
- }
+ value_traits<pair<K, V>>::reverse (p.first, p.second, s);
return s;
}
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>>> ());
@@ -543,9 +769,7 @@ namespace build2
for (; li != le && ri != re; ++li, ++ri)
{
- int r;
- if ((r = value_traits<K>::compare (li->first, ri->first)) != 0 ||
- (r = value_traits<V>::compare (li->second, ri->second)) != 0)
+ if (int r = value_traits<pair<K, V>>::compare (*li, *ri))
return r;
}
@@ -558,29 +782,81 @@ namespace build2
return 0;
}
+ // Make sure these are static-initialized together. Failed that VC will make
+ // sure it's done in the wrong order.
+ //
template <typename K, typename V>
- value_traits<vector<pair<K, V>>>::value_type_ex::
- value_type_ex (value_type&& v)
- : value_type (move (v))
+ struct pair_vector_value_type: value_type
{
- type_name = value_traits<K>::type_name;
- type_name += '_';
- type_name += value_traits<V>::type_name;
- type_name += "_pair_vector";
- name = type_name.c_str ();
- }
+ string type_name;
+
+ pair_vector_value_type (value_type&& v)
+ : value_type (move (v))
+ {
+ // 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 += ">>";
+ name = type_name.c_str ();
+ }
+ };
+
+ // This is beyond our static initialization order control skills, so we hack
+ // it up for now.
+ //
+ template <typename K, typename V>
+ struct pair_vector_value_type<K, optional<V>>: value_type
+ {
+ string type_name;
+
+ pair_vector_value_type (value_type&& v)
+ : value_type (move (v))
+ {
+ // 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 += ">>>";
+ name = type_name.c_str ();
+ }
+ };
+
+ template <typename K, typename V>
+ struct pair_vector_value_type<optional<K>, V>: value_type
+ {
+ string type_name;
+
+ pair_vector_value_type (value_type&& v)
+ : value_type (move (v))
+ {
+ // vector<pair<optional<K>,V>>
+ //
+ type_name = "vector<pair<optional<";
+ type_name += value_traits<K>::type_name;
+ type_name += ">,";
+ type_name += value_traits<V>::type_name;
+ type_name += ">>";
+ name = type_name.c_str ();
+ }
+ };
template <typename K, typename V>
const vector<pair<K, V>> value_traits<vector<pair<K, V>>>::empty_instance;
template <typename K, typename V>
- const typename value_traits<std::vector<pair<K, V>>>::value_type_ex
+ const pair_vector_value_type<K, V>
value_traits<vector<pair<K, V>>>::value_type = build2::value_type // VC14 wants =
{
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>>>,
@@ -590,92 +866,304 @@ 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.
};
- // map<K, V> value
+ // set<T> value
//
- template <typename K, typename V>
- void
- map_append (value& v, names&& ns, const variable* var)
+ template <typename T>
+ set<T> value_traits<set<T>>::
+ convert (names&& ns)
{
- using std::map;
+ set<T> s;
- map<K, V>& p (v
- ? v.as<map<K, V>> ()
- : *new (&v.data_) map<K, V> ());
-
- // Verify we have a sequence of pairs and convert each lhs/rhs to K/V.
+ // Similar to set_append() below except we throw instead of issuing
+ // diagnostics.
//
for (auto i (ns.begin ()); i != ns.end (); ++i)
{
- name& l (*i);
+ name& n (*i);
+ name* r (nullptr);
- if (!l.pair)
+ if (n.pair)
{
- diag_record dr (fail);
-
- dr << value_traits<map<K, V>>::value_type.name << " key-value "
- << "pair expected instead of '" << l << "'";
+ r = &*++i;
- if (var != nullptr)
- dr << " in variable " << var->name;
+ if (n.pair != '@')
+ throw invalid_argument (
+ string ("invalid pair character: '") + n.pair + '\'');
}
- name& r (*++i); // Got to have the second half of the pair.
+ s.insert (value_traits<T>::convert (move (n), r));
+ }
- if (l.pair != '@')
- {
- diag_record dr (fail);
+ return s;
+ }
- dr << "unexpected pair style for "
- << value_traits<map<K, V>>::value_type.name << " key-value "
- << "'" << l << "'" << l.pair << "'" << r << "'";
+ 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> ());
- if (var != nullptr)
- dr << " in variable " << var->name;
- }
+ // Convert each element to T while merging pairs.
+ //
+ for (auto i (ns.begin ()); i != ns.end (); ++i)
+ {
+ name& n (*i);
+ name* r (nullptr);
- try
+ if (n.pair)
{
- K k (value_traits<K>::convert (move (l), nullptr));
-
- try
- {
- V v (value_traits<V>::convert (move (r), nullptr));
+ r = &*++i;
- p.emplace (move (k), move (v));
- }
- catch (const invalid_argument&)
+ if (n.pair != '@')
{
diag_record dr (fail);
- dr << "invalid " << value_traits<V>::value_type.name
- << " element value '" << r << "'";
+ dr << "unexpected pair style for "
+ << value_traits<T>::value_type.name << " value "
+ << "'" << n << "'" << n.pair << "'" << *r << "'";
if (var != nullptr)
dr << " in variable " << var->name;
}
}
- catch (const invalid_argument&)
+
+ try
+ {
+ s.insert (value_traits<T>::convert (move (n), r));
+ }
+ catch (const invalid_argument& e)
{
diag_record dr (fail);
- dr << "invalid " << value_traits<K>::value_type.name
- << " element key '" << l << "'";
-
+ 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
+ //
template <typename K, typename V>
void
- map_assign (value& v, names&& ns, const variable* var)
+ map_append (value& v, names&& ns, const variable* var)
{
- using std::map;
+ map<K, V>& p (v
+ ? v.as<map<K, V>> ()
+ : *new (&v.data_) map<K, V> ());
+
+ // Verify we have a sequence of pairs and convert each lhs/rhs to K/V.
+ //
+ for (auto i (ns.begin ()); i != ns.end (); ++i)
+ {
+ name& l (*i);
+ name* r (l.pair ? &*++i : nullptr);
+
+ pair<K, V> v (value_traits<pair<K, V>>::convert (
+ move (l), r,
+ value_traits<map<K, V>>::value_type.name,
+ "element",
+ var));
+
+ // Poor man's emplace_or_assign().
+ //
+ p.emplace (move (v.first), V ()).first->second = move (v.second);
+ }
+ }
+
+ template <typename K, typename V>
+ void
+ map_prepend (value& v, names&& ns, const variable* var)
+ {
+ map<K, V>& p (v
+ ? v.as<map<K, V>> ()
+ : *new (&v.data_) map<K, V> ());
+ // Verify we have a sequence of pairs and convert each lhs/rhs to K/V.
+ //
+ for (auto i (ns.begin ()); i != ns.end (); ++i)
+ {
+ name& l (*i);
+ name* r (l.pair ? &*++i : nullptr);
+
+ pair<K, V> v (value_traits<pair<K, V>>::convert (
+ move (l), r,
+ value_traits<map<K, V>>::value_type.name,
+ "element",
+ var));
+
+ p.emplace (move (v.first), move (v.second));
+ }
+ }
+
+ template <typename K, typename V>
+ void
+ map_assign (value& v, names&& ns, const variable* var)
+ {
if (v)
v.as<map<K, V>> ().clear ();
@@ -683,30 +1171,22 @@ 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)
{
- using std::map;
-
auto& vm (v.as<map<K, V>> ());
s.reserve (2 * vm.size ());
for (const auto& p: vm)
- {
- s.push_back (value_traits<K>::reverse (p.first));
- s.back ().pair = '@';
- s.push_back (value_traits<V>::reverse (p.second));
- }
+ value_traits<pair<K, V>>::reverse (p.first, p.second, s);
return s;
}
template <typename K, typename V>
- static int
+ int
map_compare (const value& l, const value& r)
{
- using std::map;
-
auto& lm (l.as<map<K, V>> ());
auto& rm (r.as<map<K, V>> ());
@@ -715,9 +1195,7 @@ namespace build2
for (; li != le && ri != re; ++li, ++ri)
{
- int r;
- if ((r = value_traits<K>::compare (li->first, ri->first)) != 0 ||
- (r = value_traits<V>::compare (li->second, ri->second)) != 0)
+ if (int r = value_traits<pair<const K, V>>::compare (*li, *ri))
return r;
}
@@ -730,39 +1208,170 @@ 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_traits<std::map<K, V>>::value_type_ex::
- value_type_ex (value_type&& v)
- : value_type (move (v))
+ value
+ map_subscript (const value& val, value* val_data,
+ value&& sub,
+ const location& sloc,
+ const location& bloc)
{
- type_name = value_traits<K>::type_name;
- type_name += '_';
- type_name += value_traits<V>::type_name;
- type_name += "_map";
- name = type_name.c_str ();
+ // 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.
+ //
template <typename K, typename V>
- const std::map<K, V> value_traits<std::map<K, V>>::empty_instance;
+ struct map_value_type: value_type
+ {
+ string type_name;
+ map_value_type (value_type&& v)
+ : value_type (move (v))
+ {
+ // map<K,V>
+ //
+ type_name = "map<";
+ type_name += value_traits<K>::type_name;
+ type_name += ',';
+ type_name += value_traits<V>::type_name;
+ type_name += '>';
+ name = type_name.c_str ();
+ subscript = &map_subscript<K, V>;
+ }
+ };
+
+ // This is beyond our static initialization order control skills, so we hack
+ // it up for now.
+ //
+ template <typename K, typename V>
+ struct map_value_type<K, optional<V>>: value_type
+ {
+ string type_name;
+
+ map_value_type (value_type&& v)
+ : value_type (move (v))
+ {
+ // 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 += ">>";
+ name = type_name.c_str ();
+ // @@ TODO: subscript
+ }
+ };
+
+ template <typename K, typename V>
+ struct map_value_type<optional<K>, V>: value_type
+ {
+ string type_name;
+
+ map_value_type (value_type&& v)
+ : value_type (move (v))
+ {
+ // map<optional<K>,V>
+ //
+ type_name = "map<optional<";
+ type_name += value_traits<K>::type_name;
+ type_name += ">,";
+ type_name += value_traits<V>::type_name;
+ 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 typename value_traits<std::map<K, V>>::value_type_ex
- value_traits<std::map<K, V>>::value_type = build2::value_type // VC14 wants =
+ const map_value_type<K, V>
+ value_traits<map<K, V>>::value_type = build2::value_type // VC14 wants =
{
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>>,
&map_assign<K, V>,
&map_append<K, V>,
- &map_append<K, V>, // Prepend is the same as append.
+ &map_prepend<K, V>,
&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
@@ -783,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);
@@ -839,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