diff options
Diffstat (limited to 'libbuild2/variable.hxx')
-rw-r--r-- | libbuild2/variable.hxx | 208 |
1 files changed, 180 insertions, 28 deletions
diff --git a/libbuild2/variable.hxx b/libbuild2/variable.hxx index 400aaf1..aed3350 100644 --- a/libbuild2/variable.hxx +++ b/libbuild2/variable.hxx @@ -4,7 +4,8 @@ #ifndef LIBBUILD2_VARIABLE_HXX #define LIBBUILD2_VARIABLE_HXX -#include <type_traits> // aligned_storage +#include <cstddef> // max_align_t +#include <type_traits> // is_* #include <unordered_map> #include <libbutl/prefix-map.hxx> @@ -14,6 +15,8 @@ #include <libbuild2/forward.hxx> #include <libbuild2/utility.hxx> +#include <libbuild2/json.hxx> + #include <libbuild2/context.hxx> #include <libbuild2/target-type.hxx> #include <libbuild2/diagnostics.hxx> @@ -48,7 +51,11 @@ namespace build2 template <typename T> const value_type* is_a () const; - // Element type, if this is a vector. + // True if the type is a container. + // + bool container; + + // Element type, if this is a container and the element type is named. // const value_type* element_type; @@ -75,9 +82,11 @@ namespace build2 void (*const prepend) (value&, names&&, const variable*); // Reverse the value back to a vector of names. Storage can be used by the - // implementation if necessary. Cannot be NULL. + // implementation if necessary. If reduce is true, then for an empty + // simple value return an empty list rather than a list of one empty name. + // Note that the value cannot be NULL. // - names_view (*const reverse) (const value&, names& storage); + names_view (*const reverse) (const value&, names& storage, bool reduce); // Cast value::data_ storage to value type so that the result can be // static_cast to const T*. If it is NULL, then cast data_ directly. Note @@ -91,7 +100,33 @@ namespace build2 // If NULL, then the value is never empty. // + // Note that this is "semantically empty", not necessarily + // "representationally empty". For example, an empty JSON array is + // semantically empty but its representation (`[]`) is not. + // bool (*const empty) (const value&); + + // Custom subscript function. If NULL, then the generic implementation is + // used. + // + // Note that val can be NULL. If val_data points to val, then it can be + // moved from. The sloc and bloc arguments are the subscript and brace + // locations, respectively. + // + // Note: should normally be consistent with iterate. + // + value (*/*const*/ subscript) (const value& val, + value* val_data, + value&& subscript, + const location& sloc, + const location& bloc); + + // Custom iteration function. It should invoked the specified function for + // each element in order. If NULL, then the generic implementation is + // used. The passed value is never NULL. + // + void (*const iterate) (const value&, + const function<void (value&&, bool first)>&); }; // The order of the enumerators is arranged so that their integral values @@ -108,8 +143,9 @@ namespace build2 target, // Target and target type/pattern-specific. prereq // Prerequisite-specific. - // Note: remember to update the visibility attribute parsing if adding - // any new values here. + // Note: remember to update the visibility attribute parsing if adding any + // new values here. As well as the $builtin.visibility() function + // documentation. }; // VC14 reports ambiguity but seems to work if we don't provide any. @@ -315,6 +351,10 @@ namespace build2 // Check in a type-independent way if the value is empty. The value must // not be NULL. // + // Note that this is "semantically empty", not necessarily + // "representationally empty". For example, an empty JSON array is + // semantically empty but its representation (`[]`) is not. + // bool empty () const; @@ -352,9 +392,13 @@ namespace build2 value& operator= (nullptr_t) {if (!null) reset (); return *this;} - value (value&&); + // Note that we have the noexcept specification even though copy_ctor() + // could potentially throw (for example, for std::map). + // + value (value&&) noexcept; + explicit value (const value&); - value& operator= (value&&); + value& operator= (value&&); // Note: can throw for untyped RHS. value& operator= (const value&); value& operator= (reference_wrapper<value>); value& operator= (reference_wrapper<const value>); @@ -363,8 +407,8 @@ namespace build2 // public: // Assign/append/prepend a typed value. For assign, LHS should be either - // of the same type or untyped. For append, LHS should be either of the - // same type or untyped and NULL. + // of the same type or untyped. For append/prepend, LHS should be either + // of the same type or untyped and NULL. // template <typename T> value& operator= (T); template <typename T> value& operator+= (T); @@ -413,8 +457,8 @@ namespace build2 // specialization below). Types that don't fit will have to be handled // with an extra dynamic allocation. // - static constexpr size_t size_ = sizeof (name_pair); - std::aligned_storage<size_>::type data_; + static constexpr size_t size_ = sizeof (name_pair); + alignas (std::max_align_t) unsigned char data_[size_]; // Make sure we have sufficient storage for untyped values. // @@ -454,37 +498,37 @@ namespace build2 template <typename T> T& cast (value&); template <typename T> T&& cast (value&&); template <typename T> const T& cast (const value&); - template <typename T> const T& cast (const lookup&); + template <typename T> const T& cast (lookup); // As above but returns NULL if the value is NULL (or not defined, in // case of lookup). // template <typename T> T* cast_null (value&); template <typename T> const T* cast_null (const value&); - template <typename T> const T* cast_null (const lookup&); + template <typename T> const T* cast_null (lookup); // As above but returns empty value if the value is NULL (or not defined, in // case of lookup). // template <typename T> const T& cast_empty (const value&); - template <typename T> const T& cast_empty (const lookup&); + template <typename T> const T& cast_empty (lookup); // As above but returns the specified default if the value is NULL (or not // defined, in case of lookup). Note that the return is by value, not by // reference. // template <typename T> T cast_default (const value&, const T&); - template <typename T> T cast_default (const lookup&, const T&); + template <typename T> T cast_default (lookup, const T&); // As above but returns false/true if the value is NULL (or not defined, // in case of lookup). Note that the template argument is only for // documentation and should be bool (or semantically compatible). // template <typename T> T cast_false (const value&); - template <typename T> T cast_false (const lookup&); + template <typename T> T cast_false (lookup); template <typename T> T cast_true (const value&); - template <typename T> T cast_true (const lookup&); + template <typename T> T cast_true (lookup); // Assign value type to the value. The variable is optional and is only used // for diagnostics. @@ -497,18 +541,20 @@ namespace build2 typify_atomic (context&, value&, const value_type&, const variable*); // Remove value type from the value reversing it to names. This is similar - // to reverse() below except that it modifies the value itself. + // to reverse() below except that it modifies the value itself. Note that + // the reduce semantics applies to empty but not null. // - LIBBUILD2_SYMEXPORT void untypify (value&); + LIBBUILD2_SYMEXPORT void untypify (value&, bool reduce); // Reverse the value back to names. The value should not be NULL and storage - // should be empty. + // should be empty. If reduce is true, then for an empty simple value return + // an empty list rather than a list of one empty name. // vector_view<const name> - reverse (const value&, names& storage); + reverse (const value&, names& storage, bool reduce); vector_view<name> - reverse (value&, names& storage); + reverse (value&, names& storage, bool reduce); // Variable lookup result, AKA, binding of a variable to a value. // @@ -653,7 +699,7 @@ namespace build2 // case (container) if invalid_argument is thrown, the names are not // guaranteed to be unchanged. // - //template <typename T> T convert (names&&); (declaration causes ambiguity) + template <typename T> T convert (names&&); // Convert value to T. If value is already of type T, then simply cast it. // Otherwise call convert(names) above. If value is NULL, then throw @@ -931,7 +977,7 @@ namespace build2 // pair of two empties). // // @@ Maybe we should redo this with optional<> to signify which half can - // be missing? + // be missing? See also dump_value(json). // template <> struct LIBBUILD2_SYMEXPORT value_traits<name_pair> @@ -1139,12 +1185,35 @@ namespace build2 static const pair_vector_value_type<K, V> value_type; }; + // set<T> + // + template <typename T> + struct set_value_type; + + template <typename T> + struct value_traits<set<T>> + { + static_assert (sizeof (set<T>) <= value::size_, "insufficient space"); + + static set<T> convert (names&&); + static void assign (value&, set<T>&&); + static void append (value&, set<T>&&); + static void prepend (value&, set<T>&&); + static bool empty (const set<T>& x) {return x.empty ();} + + static const set<T> empty_instance; + static const set_value_type<T> value_type; + }; + // map<K, V> // // Either K or V can be optional<T> making the key or value optional. // - // Note that append/+= is non-overriding (like insert()) while prepend/=+ - // is (like insert_or_assign()). + // Note that append/+= is overriding (like insert_or_assign()) while + // prepend/=+ is not (like insert()). In a sense, whatever appears last + // (from left to right) is kept, which is consistent with what we expect to + // happen when specifying the same key repeatedly in a representation (e.g., + // a@0 a@1). // template <typename K, typename V> struct map_value_type; @@ -1165,6 +1234,79 @@ namespace build2 static const map_value_type<K, V> value_type; }; + // json + // + // Note that we do not expose json_member as a value type instead + // representing it as an object with one member. While we could expose + // member (and reverse it as a pair since there is no valid JSON + // representation for a standalone member), this doesn't seem to buy us much + // but will cause complications (for example, in supporting append/prepend). + // On the other hand, representing a member as an object only requires a bit + // of what looks like harmless looseness in a few contexts (such as the + // $json.member_*() functions). + // + // Note that similar to map, JSON object append/+= is overriding while + // prepend/=+ is not. In a sense, whatever appears last (from left to right) + // is kept, which is consistent with what we expect to happen when + // specifying the same name repeatedly (provided it's not considered + // invalid) in a representation (e.g., {"a":1,"a":2}). + // + template <> + struct LIBBUILD2_SYMEXPORT value_traits<json_value> + { + static_assert (sizeof (json_value) <= value::size_, "insufficient space"); + + static json_value convert (names&&); + static void assign (value&, json_value&&); + static void append (value&, json_value&&); + static void prepend (value&, json_value&&); + static bool empty (const json_value&); // null or empty array/object + + // These are provided to make it possible to use json_value as a container + // element. + // + static json_value convert (name&&, name*); + static name reverse (const json_value&); + static int compare (const json_value& x, const json_value& y) { + return x.compare (y);} + + static const json_value empty_instance; // null + static const char* const type_name; + static const build2::value_type value_type; + }; + + template <> + struct LIBBUILD2_SYMEXPORT value_traits<json_array> + { + static_assert (sizeof (json_array) <= value::size_, "insufficient space"); + + static json_array convert (names&&); + static void assign (value&, json_array&&); + static void append (value&, json_value&&); // Note: value, not array. + static void prepend (value&, json_value&&); + static bool empty (const json_array& v) {return v.array.empty ();} + + static const json_array empty_instance; // empty array + static const char* const type_name; + static const build2::value_type value_type; + }; + + template <> + struct LIBBUILD2_SYMEXPORT value_traits<json_object> + { + static_assert (sizeof (json_object) <= value::size_, "insufficient space"); + + static json_object convert (names&&); + static void assign (value&, json_object&&); + static void append (value&, json_value&&); // Note: value, not object. + static void prepend (value&, json_value&&); + static bool empty (const json_object& v) {return v.object.empty ();} + + static const json_object empty_instance; // empty object + static const char* const type_name; + static const build2::value_type value_type; + }; + // Canned command line to be re-lexed (used in {Build,Test}scripts). // // Note that because the executable can be specific as a target or as @@ -1200,6 +1342,8 @@ namespace build2 // value type objects for the same traits type (and we use their addressed // as identity; see cast(const value&) for an example). // + // NOTE: REMEMBER TO UPDATE dump_value(json) IF CHANGING ANYTHING HERE! + // extern template struct LIBBUILD2_DECEXPORT value_traits<strings>; extern template struct LIBBUILD2_DECEXPORT value_traits<vector<name>>; extern template struct LIBBUILD2_DECEXPORT value_traits<paths>; @@ -1219,10 +1363,16 @@ namespace build2 extern template struct LIBBUILD2_DECEXPORT value_traits<vector<pair<string, optional<bool>>>>; + extern template struct LIBBUILD2_DECEXPORT value_traits<set<string>>; + extern template struct LIBBUILD2_DECEXPORT value_traits<set<json_value>>; + extern template struct LIBBUILD2_DECEXPORT value_traits<map<string, string>>; extern template struct LIBBUILD2_DECEXPORT + value_traits<map<json_value, json_value>>; + + extern template struct LIBBUILD2_DECEXPORT value_traits<map<string, optional<string>>>; extern template struct LIBBUILD2_DECEXPORT @@ -1851,7 +2001,7 @@ namespace build2 variable_map (const variable_map&, const prerequisite&, bool shared = false); variable_map& - operator= (variable_map&& v) {m_ = move (v.m_); return *this;} + operator= (variable_map&& v) noexcept {m_ = move (v.m_); return *this;} variable_map& operator= (const variable_map& v) {m_ = v.m_; return *this;} @@ -1862,6 +2012,8 @@ namespace build2 variable_map (context& c, bool shared) : shared_ (shared), owner_ (owner::context), ctx (&c) {} + // Note: std::map's move constructor can throw. + // variable_map (variable_map&& v) : shared_ (v.shared_), owner_ (v.owner_), ctx (v.ctx), m_ (move (v.m_)) { |