diff options
Diffstat (limited to 'libbuild2/variable.txx')
-rw-r--r-- | libbuild2/variable.txx | 1074 |
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 |