From 6e91cb7cdb0c4f000a79d20d8578890d56bcdc84 Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Fri, 22 Jan 2021 08:39:10 +0200 Subject: Add support for optional pair halves in variable values --- libbuild2/variable.cxx | 12 ++ libbuild2/variable.hxx | 122 +++++++++--- libbuild2/variable.ixx | 25 +++ libbuild2/variable.txx | 513 ++++++++++++++++++++++++++++++++++--------------- 4 files changed, 486 insertions(+), 186 deletions(-) (limited to 'libbuild2') 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>>; template struct LIBBUILD2_DEFEXPORT + value_traits>>>; + + template struct LIBBUILD2_DEFEXPORT + value_traits, string>>>; + + template struct LIBBUILD2_DEFEXPORT value_traits>; template struct LIBBUILD2_DEFEXPORT + value_traits>>; + + template struct LIBBUILD2_DEFEXPORT + value_traits, string>>; + + template struct LIBBUILD2_DEFEXPORT value_traits>; } 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 + struct value_traits: value_traits {}; + // 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 { @@ -1004,9 +1010,67 @@ namespace build2 static const build2::value_type value_type; }; + // optional + // + // This is an incomplete implementation meant to provide enough support only + // to be usable as elements of containers. + // + template + struct value_traits> + { + static int compare (const optional&, const optional&); + }; + + // pair + // + // Either F or S can be optional 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 + struct pair_value_traits + { + static pair + convert (name&&, name*, const char*, const char*, const variable*); + + static void + reverse (const F&, const S&, names&); + }; + + template + struct pair_value_traits> + { + static pair> + convert (name&&, name*, const char*, const char*, const variable*); + + static void + reverse (const F&, const optional&, names&); + }; + + template + struct pair_value_traits, S> + { + static pair, S> + convert (name&&, name*, const char*, const char*, const variable*); + + static void + reverse (const optional&, const S&, names&); + }; + + template + struct value_traits>: pair_value_traits + { + static int compare (const pair&, const pair&); + }; + // vector // template + struct vector_value_type; + + template struct value_traits> { static_assert (sizeof (vector) <= value::size_, "insufficient space"); @@ -1018,20 +1082,17 @@ namespace build2 static bool empty (const vector& x) {return x.empty ();} static const vector 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 value_type; }; // vector> // + // Either K or V can be optional making the corresponding half of the + // pair optional. + // + template + struct pair_vector_value_type; + template struct value_traits>> { @@ -1045,20 +1106,16 @@ namespace build2 static bool empty (const vector>& x) {return x.empty ();} static const vector> 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 value_type; }; // map // + // Either K or V can be optional making the key or value optional. + // + template + struct map_value_type; + template struct value_traits> { @@ -1073,16 +1130,7 @@ namespace build2 static bool empty (const map& x) {return x.empty ();} static const map 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 value_type; }; // Explicitly pre-instantiate and export value_traits templates for @@ -1102,10 +1150,22 @@ namespace build2 value_traits>>; extern template struct LIBBUILD2_DECEXPORT + value_traits>>>; + + extern template struct LIBBUILD2_DECEXPORT + value_traits, string>>>; + + extern template struct LIBBUILD2_DECEXPORT value_traits>; extern template struct LIBBUILD2_DECEXPORT - value_traits>; + value_traits>>; + + extern template struct LIBBUILD2_DECEXPORT + value_traits, string>>; + + extern template struct LIBBUILD2_DECEXPORT + value_traits>; // 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 + // + template + inline int value_traits>:: + compare (const optional& l, const optional& r) + { + return l + ? (r ? value_traits::compare (*l, *r) : 1) + : (r ? -1 : 0); + } + + // pair value + // + template + inline int value_traits>:: + compare (const pair& l, const pair& r) + { + int i (value_traits::compare (l.first, r.first)); + + if (i == 0) + i = value_traits::compare (l.second, r.second); + + return i; + } + // vector value // template 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::compare (l.as (), r.as ()); } - // vector value + // pair value // + template + pair pair_value_traits:: + 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::convert (move (l), nullptr)); + + try + { + S s (value_traits::convert (move (*r), nullptr)); + + return pair (move (f), move (s)); + } + catch (const invalid_argument&) + { + diag_record dr (fail); + + dr << "invalid " << value_traits::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::value_type.name + << " first have of pair '" << l << "'"; + + if (var != nullptr) + dr << " in variable " << var->name; + + dr << endf; + } + } + + template + pair> pair_value_traits>:: + 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::convert (move (l), nullptr)); + + try + { + optional s; + + if (l.pair) + s = value_traits::convert (move (*r), nullptr); + + return pair> (move (f), move (s)); + } + catch (const invalid_argument&) + { + diag_record dr (fail); + + dr << "invalid " << value_traits::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::value_type.name + << " first have of pair '" << l << "'"; + if (var != nullptr) + dr << " in variable " << var->name; + + dr << endf; + } + } + + template + pair, S> pair_value_traits, 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; + + if (l.pair) + { + f = value_traits::convert (move (l), nullptr); + l = move (*r); // Shift. + } + + try + { + S s (value_traits::convert (move (l), nullptr)); + + return pair, S> (move (f), move (s)); + } + catch (const invalid_argument&) + { + diag_record dr (fail); + + dr << "invalid " << value_traits::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::value_type.name + << " first have of pair '" << l << "'"; + + if (var != nullptr) + dr << " in variable " << var->name; + + dr << endf; + } + } + + template + void pair_value_traits:: + reverse (const F& f, const S& s, names& ns) + { + ns.push_back (value_traits::reverse (f)); + ns.back ().pair = '@'; + ns.push_back (value_traits::reverse (s)); + } + + template + void pair_value_traits>:: + reverse (const F& f, const optional& s, names& ns) + { + ns.push_back (value_traits::reverse (f)); + if (s) + { + ns.back ().pair = '@'; + ns.push_back (value_traits::reverse (*s)); + } + } + + template + void pair_value_traits, S>:: + reverse (const optional& f, const S& s, names& ns) + { + if (f) + { + ns.push_back (value_traits::reverse (*f)); + ns.back ().pair = '@'; + } + ns.push_back (value_traits::reverse (s)); + } + + // vector value + // template vector value_traits>:: 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 - value_traits>::value_type_ex:: - value_type_ex (value_type&& v) - : value_type (move (v)) + struct vector_value_type: value_type { - type_name = value_traits::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::type_name; + type_name += 's'; + name = type_name.c_str (); + } + }; template const vector value_traits>::empty_instance; template - const typename value_traits>::value_type_ex + const vector_value_type value_traits>::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>>::value_type.name - << " key-value pair expected instead of '" << l << "'"; + p.push_back (value_traits>::convert ( + move (l), r, + value_traits>>::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>>::value_type.name - << " key-value '" << l << "'" << l.pair << "'" << r << "'"; - - if (var != nullptr) - dr << " in variable " << var->name; - } - - try - { - K k (value_traits::convert (move (l), nullptr)); - - try - { - V v (value_traits::convert (move (r), nullptr)); - - p.emplace_back (move (k), move (v)); - } - catch (const invalid_argument&) - { - diag_record dr (fail); - - dr << "invalid " << value_traits::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::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::reverse (p.first)); - s.back ().pair = '@'; - s.push_back (value_traits::reverse (p.second)); - } + value_traits>::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::compare (li->first, ri->first)) != 0 || - (r = value_traits::compare (li->second, ri->second)) != 0) + if (int r = value_traits>::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 - value_traits>>::value_type_ex:: - value_type_ex (value_type&& v) - : value_type (move (v)) + struct pair_vector_value_type: value_type { - type_name = value_traits::type_name; - type_name += '_'; - type_name += value_traits::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::type_name; + type_name += '_'; + type_name += value_traits::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 + struct pair_vector_value_type>: value_type + { + string type_name; + + pair_vector_value_type (value_type&& v) + : value_type (move (v)) + { + type_name = value_traits::type_name; + type_name += "_optional_"; + type_name += value_traits::type_name; + type_name += "_pair_vector"; + name = type_name.c_str (); + } + }; + + template + struct pair_vector_value_type, V>: value_type + { + string type_name; + + pair_vector_value_type (value_type&& v) + : value_type (move (v)) + { + type_name = "optional_"; + type_name += value_traits::type_name; + type_name += '_'; + type_name += value_traits::type_name; + type_name += "_pair_vector"; + name = type_name.c_str (); + } + }; template const vector> value_traits>>::empty_instance; template - const typename value_traits>>::value_type_ex + const pair_vector_value_type value_traits>>::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 v (value_traits>::convert ( + move (l), r, + value_traits>::value_type.name, + "element", + var)); - dr << value_traits>::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>::value_type.name << " key-value " - << "'" << l << "'" << l.pair << "'" << r << "'"; - - if (var != nullptr) - dr << " in variable " << var->name; - } - - try - { - K k (value_traits::convert (move (l), nullptr)); - - try - { - V v (value_traits::convert (move (r), nullptr)); - - p.emplace (move (k), move (v)); - } - catch (const invalid_argument&) - { - diag_record dr (fail); - - dr << "invalid " << value_traits::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::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::reverse (p.first)); - s.back ().pair = '@'; - s.push_back (value_traits::reverse (p.second)); - } + value_traits>::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::compare (li->first, ri->first)) != 0 || - (r = value_traits::compare (li->second, ri->second)) != 0) + if (int r = value_traits>::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 - value_traits>::value_type_ex:: - value_type_ex (value_type&& v) - : value_type (move (v)) + struct map_value_type: value_type { - type_name = value_traits::type_name; - type_name += '_'; - type_name += value_traits::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::type_name; + type_name += '_'; + type_name += value_traits::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 + struct map_value_type>: value_type + { + string type_name; + + map_value_type (value_type&& v) + : value_type (move (v)) + { + type_name = value_traits::type_name; + type_name += "_optional_"; + type_name += value_traits::type_name; + type_name += "_map"; + name = type_name.c_str (); + } + }; + + template + struct map_value_type, V>: value_type + { + string type_name; + + map_value_type (value_type&& v) + : value_type (move (v)) + { + type_name = "optional_"; + type_name += value_traits::type_name; + type_name += '_'; + type_name += value_traits::type_name; + type_name += "_map"; + name = type_name.c_str (); + } + }; template const std::map value_traits>::empty_instance; template - const typename value_traits>::value_type_ex + const map_value_type value_traits>::value_type = build2::value_type // VC14 wants = { nullptr, // Patched above. -- cgit v1.1