diff options
author | Boris Kolpackov <boris@codesynthesis.com> | 2021-01-22 08:39:10 +0200 |
---|---|---|
committer | Boris Kolpackov <boris@codesynthesis.com> | 2021-01-22 09:34:29 +0200 |
commit | 6e91cb7cdb0c4f000a79d20d8578890d56bcdc84 (patch) | |
tree | 7ba42ef488534c19bf2eafbd8d289e08036f11b7 | |
parent | d51892e33a0fe69e743e02d9620312133a7ac61d (diff) |
Add support for optional pair halves in variable values
-rw-r--r-- | libbuild2/variable.cxx | 12 | ||||
-rw-r--r-- | libbuild2/variable.hxx | 122 | ||||
-rw-r--r-- | libbuild2/variable.ixx | 25 | ||||
-rw-r--r-- | libbuild2/variable.txx | 513 |
4 files changed, 486 insertions, 186 deletions
diff --git a/libbuild2/variable.cxx b/libbuild2/variable.cxx index f5476b5..47703da 100644 --- a/libbuild2/variable.cxx +++ b/libbuild2/variable.cxx @@ -1832,8 +1832,20 @@ namespace build2 value_traits<vector<pair<string, string>>>; template struct LIBBUILD2_DEFEXPORT + value_traits<vector<pair<string, optional<string>>>>; + + template struct LIBBUILD2_DEFEXPORT + value_traits<vector<pair<optional<string>, string>>>; + + template struct LIBBUILD2_DEFEXPORT value_traits<std::map<string, string>>; template struct LIBBUILD2_DEFEXPORT + value_traits<std::map<string, optional<string>>>; + + template struct LIBBUILD2_DEFEXPORT + value_traits<std::map<optional<string>, string>>; + + template struct LIBBUILD2_DEFEXPORT value_traits<std::map<project_name, dir_path>>; } diff --git a/libbuild2/variable.hxx b/libbuild2/variable.hxx index a671978..a9dde7f 100644 --- a/libbuild2/variable.hxx +++ b/libbuild2/variable.hxx @@ -610,6 +610,9 @@ namespace build2 // static const build2::value_type value_type; // }; + template <typename T> + struct value_traits<const T>: value_traits<T> {}; + // Convert name to a simple value. Throw invalid_argument (with a message) // if the name is not a valid representation of value (in which case the // name remains unchanged for diagnostics). The second version is called for @@ -897,6 +900,9 @@ namespace build2 // half of a pair). If both are empty then this is an empty value (and not a // pair of two empties). // + // @@ Maybe we should redo this with optional<> to signify which half can + // be missing? + // template <> struct LIBBUILD2_SYMEXPORT value_traits<name_pair> { @@ -1004,9 +1010,67 @@ namespace build2 static const build2::value_type value_type; }; + // optional<T> + // + // This is an incomplete implementation meant to provide enough support only + // to be usable as elements of containers. + // + template <typename T> + struct value_traits<optional<T>> + { + static int compare (const optional<T>&, const optional<T>&); + }; + + // pair<F, S> + // + // Either F or S can be optional<T> making the corresponding half of the + // pair optional. + // + // This is an incomplete implementation meant to provide enough support only + // to be usable as elements of containers. + // + template <typename F, typename S> + struct pair_value_traits + { + static pair<F, S> + convert (name&&, name*, const char*, const char*, const variable*); + + static void + reverse (const F&, const S&, names&); + }; + + template <typename F, typename S> + struct pair_value_traits<F, optional<S>> + { + static pair<F, optional<S>> + convert (name&&, name*, const char*, const char*, const variable*); + + static void + reverse (const F&, const optional<S>&, names&); + }; + + template <typename F, typename S> + struct pair_value_traits<optional<F>, S> + { + static pair<optional<F>, S> + convert (name&&, name*, const char*, const char*, const variable*); + + static void + reverse (const optional<F>&, const S&, names&); + }; + + template <typename F, typename S> + struct value_traits<pair<F, S>>: pair_value_traits<F, S> + { + static int compare (const pair<F, S>&, const pair<F, S>&); + }; + // vector<T> // template <typename T> + struct vector_value_type; + + template <typename T> struct value_traits<vector<T>> { static_assert (sizeof (vector<T>) <= value::size_, "insufficient space"); @@ -1018,20 +1082,17 @@ namespace build2 static bool empty (const vector<T>& x) {return x.empty ();} static const vector<T> empty_instance; - - // Make sure these are static-initialized together. Failed that VC will - // make sure it's done in the wrong order. - // - struct value_type_ex: build2::value_type - { - string type_name; - value_type_ex (value_type&&); - }; - static const value_type_ex value_type; + static const vector_value_type<T> value_type; }; // vector<pair<K, V>> // + // Either K or V can be optional<T> making the corresponding half of the + // pair optional. + // + template <typename K, typename V> + struct pair_vector_value_type; + template <typename K, typename V> struct value_traits<vector<pair<K, V>>> { @@ -1045,20 +1106,16 @@ namespace build2 static bool empty (const vector<pair<K, V>>& x) {return x.empty ();} static const vector<pair<K, V>> empty_instance; - - // Make sure these are static-initialized together. Failed that VC will - // make sure it's done in the wrong order. - // - struct value_type_ex: build2::value_type - { - string type_name; - value_type_ex (value_type&&); - }; - static const value_type_ex value_type; + static const pair_vector_value_type<K, V> value_type; }; // map<K, V> // + // Either K or V can be optional<T> making the key or value optional. + // + template <typename K, typename V> + struct map_value_type; + template <typename K, typename V> struct value_traits<std::map<K, V>> { @@ -1073,16 +1130,7 @@ namespace build2 static bool empty (const map<K, V>& x) {return x.empty ();} static const map<K, V> empty_instance; - - // Make sure these are static-initialized together. Failed that VC will - // make sure it's done in the wrong order. - // - struct value_type_ex: build2::value_type - { - string type_name; - value_type_ex (value_type&&); - }; - static const value_type_ex value_type; + static const map_value_type<K, V> value_type; }; // Explicitly pre-instantiate and export value_traits templates for @@ -1102,10 +1150,22 @@ namespace build2 value_traits<vector<pair<string, string>>>; extern template struct LIBBUILD2_DECEXPORT + value_traits<vector<pair<string, optional<string>>>>; + + extern template struct LIBBUILD2_DECEXPORT + value_traits<vector<pair<optional<string>, string>>>; + + extern template struct LIBBUILD2_DECEXPORT value_traits<std::map<string, string>>; extern template struct LIBBUILD2_DECEXPORT - value_traits<std::map<project_name, dir_path>>; + value_traits<std::map<string, optional<string>>>; + + extern template struct LIBBUILD2_DECEXPORT + value_traits<std::map<optional<string>, string>>; + + extern template struct LIBBUILD2_DECEXPORT + value_traits<std::map<project_name, dir_path>>; // var_subprojects // Project-wide (as opposed to global) variable overrides (see context ctor // for details). diff --git a/libbuild2/variable.ixx b/libbuild2/variable.ixx index c8f9541..9b24b9d 100644 --- a/libbuild2/variable.ixx +++ b/libbuild2/variable.ixx @@ -728,6 +728,31 @@ namespace build2 : s); } + // optional<T> + // + template <typename T> + inline int value_traits<optional<T>>:: + compare (const optional<T>& l, const optional<T>& r) + { + return l + ? (r ? value_traits<T>::compare (*l, *r) : 1) + : (r ? -1 : 0); + } + + // pair<F, S> value + // + template <typename F, typename S> + inline int value_traits<pair<F, S>>:: + compare (const pair<F, S>& l, const pair<F, S>& r) + { + int i (value_traits<F>::compare (l.first, r.first)); + + if (i == 0) + i = value_traits<S>::compare (l.second, r.second); + + return i; + } + // vector<T> value // template <typename T> diff --git a/libbuild2/variable.txx b/libbuild2/variable.txx index 8fa1d7c..9c9c867 100644 --- a/libbuild2/variable.txx +++ b/libbuild2/variable.txx @@ -241,9 +241,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&) + { + diag_record dr (fail); + + dr << "invalid " << value_traits<S>::value_type.name + << " second have of pair '" << *r << "'"; + + if (var != nullptr) + dr << " in variable " << var->name; + + dr << endf; + } + } + catch (const invalid_argument&) + { + diag_record dr (fail); + + dr << "invalid " << value_traits<F>::value_type.name + << " first have of pair '" << l << "'"; + + if (var != nullptr) + dr << " in variable " << var->name; + + dr << 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&) + { + diag_record dr (fail); + + dr << "invalid " << value_traits<S>::value_type.name + << " second have of pair '" << *r << "'"; + + if (var != nullptr) + dr << " in variable " << var->name; + + dr << endf; + } + } + catch (const invalid_argument&) + { + diag_record dr (fail); + + dr << "invalid " << value_traits<F>::value_type.name + << " first have of pair '" << l << "'"; + if (var != nullptr) + dr << " in variable " << var->name; + + dr << 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&) + { + diag_record dr (fail); + + dr << "invalid " << value_traits<S>::value_type.name + << " second have of pair '" << l << "'"; + + if (var != nullptr) + dr << " in variable " << var->name; + + dr << endf; + } + } + catch (const invalid_argument&) + { + diag_record dr (fail); + + dr << "invalid " << value_traits<F>::value_type.name + << " first have of pair '" << l << "'"; + + if (var != nullptr) + dr << " in variable " << var->name; + + dr << 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) @@ -396,21 +615,28 @@ 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 T> - value_traits<vector<T>>::value_type_ex:: - value_type_ex (value_type&& v) - : value_type (move (v)) + struct vector_value_type: value_type { - type_name = value_traits<T>::type_name; - type_name += 's'; - name = type_name.c_str (); - } + string type_name; + + vector_value_type (value_type&& v) + : value_type (move (v)) + { + 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. @@ -444,63 +670,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); - - dr << value_traits<vector<pair<K, V>>>::value_type.name - << " key-value pair expected instead of '" << l << "'"; + p.push_back (value_traits<pair<K, V>>::convert ( + move (l), r, + value_traits<vector<pair<K, V>>>::value_type.name, + "element", + var)); - 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; - } } } @@ -522,11 +699,7 @@ namespace build2 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; } @@ -543,9 +716,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,23 +729,66 @@ 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)) + { + 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 (); + } + }; + + // 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)) + { + type_name = value_traits<K>::type_name; + type_name += "_optional_"; + type_name += value_traits<V>::type_name; + type_name += "_pair_vector"; + 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)) + { + type_name = "optional_"; + 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 (); + } + }; 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. @@ -610,63 +824,15 @@ 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); + pair<K, V> v (value_traits<pair<K, V>>::convert ( + move (l), r, + value_traits<map<K, V>>::value_type.name, + "element", + var)); - dr << value_traits<map<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<map<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 (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; - } + p.emplace (move (v.first), move (v.second)); } } @@ -692,11 +858,7 @@ namespace build2 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; } @@ -715,9 +877,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,23 +890,66 @@ 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<std::map<K, V>>::value_type_ex:: - value_type_ex (value_type&& v) - : value_type (move (v)) + struct map_value_type: value_type { - type_name = value_traits<K>::type_name; - type_name += '_'; - type_name += value_traits<V>::type_name; - type_name += "_map"; - name = type_name.c_str (); - } + string type_name; + + map_value_type (value_type&& v) + : value_type (move (v)) + { + type_name = value_traits<K>::type_name; + type_name += '_'; + type_name += value_traits<V>::type_name; + type_name += "_map"; + 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 map_value_type<K, optional<V>>: value_type + { + string type_name; + + map_value_type (value_type&& v) + : value_type (move (v)) + { + type_name = value_traits<K>::type_name; + type_name += "_optional_"; + type_name += value_traits<V>::type_name; + type_name += "_map"; + name = type_name.c_str (); + } + }; + + 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)) + { + type_name = "optional_"; + type_name += value_traits<K>::type_name; + type_name += '_'; + type_name += value_traits<V>::type_name; + type_name += "_map"; + name = type_name.c_str (); + } + }; template <typename K, typename V> const std::map<K, V> value_traits<std::map<K, V>>::empty_instance; template <typename K, typename V> - const typename value_traits<std::map<K, V>>::value_type_ex + const map_value_type<K, V> value_traits<std::map<K, V>>::value_type = build2::value_type // VC14 wants = { nullptr, // Patched above. |