From 70317569c6dcd9809ed4a8c425777e653ec6ca08 Mon Sep 17 00:00:00 2001 From: Karen Arutyunov Date: Mon, 1 May 2017 18:24:31 +0300 Subject: Add hxx extension for headers --- build2/variable | 1328 ------------------------------------------------------- 1 file changed, 1328 deletions(-) delete mode 100644 build2/variable (limited to 'build2/variable') diff --git a/build2/variable b/build2/variable deleted file mode 100644 index be3b478..0000000 --- a/build2/variable +++ /dev/null @@ -1,1328 +0,0 @@ -// file : build2/variable -*- C++ -*- -// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd -// license : MIT; see accompanying LICENSE file - -#ifndef BUILD2_VARIABLE -#define BUILD2_VARIABLE - -#include -#include -#include // aligned_storage -#include - -#include -#include // map_key - -#include -#include - -#include - -namespace build2 -{ - class value; - struct variable; - struct lookup; - - struct value_type - { - const char* name; // Type name for diagnostics. - const size_t size; // Type size in value::data_ (only used for PODs). - - // Base type, if any. We have very limited support for inheritance: a - // value can be cast to the base type. In particular, a derived/base value - // cannot be assigned to base/derived. If not NULL, then the cast function - // below is expected to return the base pointer if its second argument - // points to the base's value_type. - // - const value_type* base; - - // Destroy the value. If it is NULL, then the type is assumed to be POD - // with a trivial destructor. - // - void (*const dtor) (value&); - - // Copy/move constructor and copy/move assignment for data_. If NULL, then - // assume the stored data is POD. If move is true then the second argument - // can be const_cast and moved from. copy_assign() is only called with - // non-NULL first argument. - // - void (*const copy_ctor) (value&, const value&, bool move); - void (*const copy_assign) (value&, const value&, bool move); - - // While assign cannot be NULL, if append or prepend is NULL, then this - // means this type doesn't support this operation. Variable is optional - // and is provided only for diagnostics. Return true if the resulting - // value is not empty. - // - void (*const assign) (value&, names&&, const variable*); - void (*const append) (value&, names&&, const variable*); - 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. - // - names_view (*const reverse) (const value&, names& storage); - - // 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 - // that this function is used for both const and non-const values. - // - const void* (*const cast) (const value&, const value_type*); - - // If NULL, then the types are compared as PODs using memcmp(). - // - int (*const compare) (const value&, const value&); - - // If NULL, then the value is never empty. - // - bool (*const empty) (const value&); - }; - - enum class variable_visibility - { - target, // Target and target type/pattern-specific. - scope, // This scope (no outer scopes). - project, // This project (no outer projects). - normal // All outer scopes. - - // Note that the search for target type/pattern-specific terminates at - // the project boundary. - }; - - // variable - // - // The two variables are considered the same if they have the same name. - // - // If the variable is overridden on the command line, then override is the - // chain of the special override variables. Their names are derived from the - // main variable name as .{__override,__prefix,__suffix} and they are - // not entered into the var_pool. The override variables only vary in their - // names and visibility. - // - // Note also that we don't propagate the variable type to override variables - // and we keep override values as untyped names. They get "typed" when they - // are applied. - // - // We use the "modify original, override on query" model. Because of that, a - // modified value does not necessarily represent the actual value so care - // must be taken to re-query after (direct) modification. And because of - // that, variables set by the C++ code are by default non-overridable. - // - // Initial processing including entering of global overrides happens in - // reset() before any other variables. Project wide overrides are entered in - // main(). Overriding happens in scope::find_override(). - // - struct variable - { - string name; - const value_type* type; // If NULL, then not (yet) typed. - unique_ptr override; - variable_visibility visibility; - size_t generation; // load_generation of this variable. - }; - - inline bool - operator== (const variable& x, const variable& y) {return x.name == y.name;} - - inline ostream& - operator<< (ostream& os, const variable& v) {return os << v.name;} - - // - // - class value - { - public: - const value_type* type; // NULL means this value is not (yet) typed. - bool null; - - // Extra data that is associated with the value that can be used to store - // flags, etc. It is initialized to 0 and copied (but not assigned) from - // one value to another but is otherwise untouched (not even when the - // value is reset to NULL). - // - // Note: if deciding to use for something make sure it is not overlapping - // with an existing usage. - // - uint16_t extra; - - explicit operator bool () const {return !null;} - bool operator== (nullptr_t) const {return null;} - bool operator!= (nullptr_t) const {return !null;} - - // Check in a type-independent way if the value is empty. The value must - // not be NULL. - // - bool - empty () const; - - // Creation. A default-initialzied value is NULL and can be reset back to - // NULL by assigning nullptr. Values can be copied and copy-assigned. Note - // that for assignment, the values' types should be the same or LHS should - // be untyped. - // - // - public: - ~value () {*this = nullptr;} - - explicit - value (nullptr_t = nullptr): type (nullptr), null (true), extra (0) {} - - explicit - value (const value_type* t): type (t), null (true), extra (0) {} - - explicit - value (names); // Create untyped value. - - template - explicit - value (T); // Create value of value_traits::value_type type. - - // Note: preserves type. - // - value& - operator= (nullptr_t) {if (!null) reset (); return *this;} - - value (value&&); - explicit value (const value&); - value& operator= (value&&); - value& operator= (const value&); - value& operator= (reference_wrapper); - value& operator= (reference_wrapper); - - // Assign/Append/Prepend. - // - public: - // Assign/append 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. - // - template value& operator= (T); - template value& operator+= (T); - - template value& operator+= (T* v) { - return v != nullptr ? *this += *v : *this;} - - value& operator= (const char* v) {return *this = string (v);} - value& operator+= (const char* v) {return *this += string (v);} - - // Assign/append/prepend raw data. Variable is optional and is only used - // for diagnostics. - // - void assign (names&&, const variable*); - void assign (name&&, const variable*); // Shortcut for single name. - void append (names&&, const variable*); - void prepend (names&&, const variable*); - - - // Implementation details, don't use directly except in representation - // type implementations. - // - public: - // Fast, unchecked cast of data_ to T. - // - template T& as () & {return reinterpret_cast (data_);} - template T&& as () && {return move (as ());} - template const T& as () const& { - return reinterpret_cast (data_);} - - public: - // The maximum size we can store directly is sufficient for the most - // commonly used types (string, vector, map) on all the platforms that we - // support (each type should static assert this in its value_traits - // 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::type data_; - - // Make sure we have sufficient storage for untyped values. - // - static_assert (sizeof (names) <= size_, "insufficient space"); - - private: - void - reset (); - }; - - // This is what we call a "value pack"; it can be created by the eval - // context and passed as arguments to functions. Usually we will have just - // one value. - // - using values = small_vector; - - // The values should be of the same type (or both be untyped) except NULL - // values can also be untyped. NULL values compare equal and a NULL value - // is always less than a non-NULL. - // - bool operator== (const value&, const value&); - bool operator!= (const value&, const value&); - bool operator< (const value&, const value&); - bool operator<= (const value&, const value&); - bool operator> (const value&, const value&); - bool operator>= (const value&, const value&); - - // Value cast. The first three expect the value to be not NULL. The cast - // from lookup expects the value to aslo be defined. - // - // Note that a cast to names expects the value to be untyped while a cast - // to vector -- typed. - // - // Why are these non-members? The cast is easier on the eyes and is also - // consistent with the cast operators. The other two are for symmetry. - // - template T& cast (value&); - template T&& cast (value&&); - template const T& cast (const value&); - template const T& cast (const lookup&); - - // As above but returns NULL if the value is NULL (or not defined, in - // case of lookup). - // - template T* cast_null (value&); - template const T* cast_null (const value&); - template const T* cast_null (const lookup&); - - // As above but returns false 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 T cast_false (const value& v); - template T cast_false (const lookup& l); - - // Assign value type to the value. In the second version the variable is - // optional and is only used for diagnostics. - // - template - void typify (value&, const variable&); - void typify (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. - // - void untypify (value&); - - // Reverse the value back to names. The value should not be NULL and storage - // should be empty. - // - vector_view - reverse (const value&, names& storage); - - vector_view - reverse (value&, names& storage); - - // lookup - // - // A variable can be undefined, NULL, or contain a (potentially empty) - // value. - // - class variable_map; - - struct lookup - { - using value_type = build2::value; - - const value_type* value; // NULL if undefined. - const variable_map* vars; // value is variable_map::value_data if not NULL. - - bool - defined () const {return value != nullptr;} - - // Note: returns true if defined and not NULL. - // - explicit operator bool () const {return defined () && !value->null;} - - const value_type& operator* () const {return *value;} - const value_type* operator-> () const {return value;} - - // Return true if this value belongs to the specified scope or target. - // Note that it can also be a target type/pattern-specific value (in - // which case it won't belong to either). - // - template - bool - belongs (const T& x) const {return vars == &x.vars;} - - lookup (): value (nullptr), vars (nullptr) {} - - template - lookup (const value_type& v, const T& x): lookup (&v, &x.vars) {} - - lookup (const value_type& v, const variable_map& vm) - : value (&v), vars (&vm) {} - - lookup (const value_type* v, const variable_map* vm) - : value (v), vars (v != nullptr ? vm : nullptr) {} - }; - - // Two lookups are equal if they point to the same variable. - // - inline bool - operator== (const lookup& x, const lookup& y) - { - bool r (x.value == y.value); - assert (!r || x.vars == y.vars); - return r; - } - - inline bool - operator!= (const lookup& x, const lookup& y) {return !(x == y);} - - - // Representation types. - // - // Potential optimizations: - // - // - Split value::operator=/+=() into const T and T&&, also overload - // value_traits functions that they call. - // - // - Specialization for vector (if used and becomes critical). - // - template - struct value_traits_specialization; // enable_if'able specialization support. - - template - struct value_traits: value_traits_specialization {}; - // { - // static_assert (sizeof (T) <= value::size_, "insufficient space"); - // - // // Convert name to T. If rhs is not NULL, then it is the second half - // // of a pair. Only needs to be provided by simple types. Throw - // // invalid_argument (with a message) if the name is not a valid - // // representation of value (in which case the name should remain - // // unchanged for diagnostics). - // // - // static T convert (name&&, name* rhs); - // - // // Assign/append/prepend T to value which is already of type T but can - // // be NULL. - // // - // static void assign (value&, T&&); - // static void append (value&, T&&); - // static void prepend (value&, T&&); - // - // // Reverse a value back to name. Only needs to be provided by simple - // // types. - // // - // static name reverse (const T&); - // - // // Compare two values. Only needs to be provided by simple types. - // // - // static int compare (const T&, const T&); - // - // // Return true if the value is empty. - // // - // static bool empty (const T&); - // - // // True if can be constructed from empty names as T(). - // // - // static const bool empty_value = true; - // - // // For simple types (those that can be used as elements of containers), - // // type_name must be constexpr in order to sidestep the static init - // // order issue (in fact, that's the only reason we have it both here - // // and in value_type.name -- value_type cannot be constexpr because - // // of pointers to function template instantiations). - // // - // static const char* const type_name; - // static const build2::value_type value_type; - // }; - - // 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 - // a pair. - // - template T convert (name&&); - template T convert (name&&, name&&); - - // As above but can also be called for container types. Note that in this - // case (container) if invalid_argument is thrown, the names are not - // guaranteed to be unchanged. - // - //template T convert (names&&); (declaration causes ambiguity) - - // Convert value to T. If value is already of type T, then simply cast it. - // Otherwise call convert(names) above. - // - template T convert (value&&); - - // Default implementations of the dtor/copy_ctor/copy_assing callbacks for - // types that are stored directly in value::data_ and the provide all the - // necessary functions (copy/move ctor and assignment operator). - // - template - static void - default_dtor (value&); - - template - static void - default_copy_ctor (value&, const value&, bool); - - template - static void - default_copy_assign (value&, const value&, bool); - - // Default implementations of the empty callback that calls - // value_traits::empty(). - // - template - static bool - default_empty (const value&); - - // Default implementations of the assign/append/prepend callbacks for simple - // types. They call value_traits::convert() and then pass the result to - // value_traits::assign()/append()/prepend(). As a result, it may not be - // the most efficient way to do it. - // - template - static void - simple_assign (value&, names&&, const variable*); - - template - static void - simple_append (value&, names&&, const variable*); - - template - static void - simple_prepend (value&, names&&, const variable*); - - // Default implementations of the reverse callback for simple types that - // calls value_traits::reverse() and adds the result to the vector. As a - // result, it may not be the most efficient way to do it. - // - template - static names_view - simple_reverse (const value&, names&); - - // Default implementations of the compare callback for simple types that - // calls value_traits::compare(). - // - template - static int - simple_compare (const value&, const value&); - - // bool - // - template <> - struct value_traits - { - static_assert (sizeof (bool) <= value::size_, "insufficient space"); - - static bool convert (name&&, name*); - static void assign (value&, bool); - static void append (value&, bool); // OR. - static name reverse (bool x) {return name (x ? "true" : "false");} - static int compare (bool, bool); - static bool empty (bool) {return false;} - - static const bool empty_value = false; - static const char* const type_name; - static const build2::value_type value_type; - }; - - template <> - struct value_traits - { - static_assert (sizeof (uint64_t) <= value::size_, "insufficient space"); - - static uint64_t convert (name&&, name*); - static void assign (value&, uint64_t); - static void append (value&, uint64_t); // ADD. - static name reverse (uint64_t x) {return name (to_string (x));} - static int compare (uint64_t, uint64_t); - static bool empty (bool) {return false;} - - static const bool empty_value = false; - static const char* const type_name; - static const build2::value_type value_type; - }; - - // Treat unsigned integral types as uint64. Note that bool is handled - // differently at an earlier stage. - // - template - struct value_traits_specialization::value && - std::is_unsigned::value>::type>: - value_traits {}; - - // string - // - template <> - struct value_traits - { - static_assert (sizeof (string) <= value::size_, "insufficient space"); - - static string convert (name&&, name*); - static void assign (value&, string&&); - static void append (value&, string&&); - static void prepend (value&, string&&); - static name reverse (const string& x) {return name (x);} - static int compare (const string&, const string&); - static bool empty (const string& x) {return x.empty ();} - - static const bool empty_value = true; - static const char* const type_name; - static const build2::value_type value_type; - }; - - // Treat const char* as string. - // - template <> - struct value_traits: value_traits {}; - - // path - // - template <> - struct value_traits - { - static_assert (sizeof (path) <= value::size_, "insufficient space"); - - static path convert (name&&, name*); - static void assign (value&, path&&); - static void append (value&, path&&); // operator/ - static void prepend (value&, path&&); // operator/ - static name reverse (const path& x) { - return x.to_directory () - ? name (path_cast (x)) - : name (x.string ()); - } - static int compare (const path&, const path&); - static bool empty (const path& x) {return x.empty ();} - - static const bool empty_value = true; - static const char* const type_name; - static const build2::value_type value_type; - }; - - // dir_path - // - template <> - struct value_traits - { - static_assert (sizeof (dir_path) <= value::size_, "insufficient space"); - - static dir_path convert (name&&, name*); - static void assign (value&, dir_path&&); - static void append (value&, dir_path&&); // operator/ - static void prepend (value&, dir_path&&); // operator/ - static name reverse (const dir_path& x) {return name (x);} - static int compare (const dir_path&, const dir_path&); - static bool empty (const dir_path& x) {return x.empty ();} - - static const bool empty_value = true; - static const char* const type_name; - static const build2::value_type value_type; - }; - - // abs_dir_path - // - template <> - struct value_traits - { - static_assert (sizeof (abs_dir_path) <= value::size_, - "insufficient space"); - - static abs_dir_path convert (name&&, name*); - static void assign (value&, abs_dir_path&&); - static void append (value&, abs_dir_path&&); // operator/ - static name reverse (const abs_dir_path& x) {return name (x);} - static int compare (const abs_dir_path&, const abs_dir_path&); - static bool empty (const abs_dir_path& x) {return x.empty ();} - - static const bool empty_value = true; - static const char* const type_name; - static const build2::value_type value_type; - }; - - // name - // - template <> - struct value_traits - { - static_assert (sizeof (name) <= value::size_, "insufficient space"); - - static name convert (name&&, name*); - static void assign (value&, name&&); - static name reverse (const name& x) {return x;} - static int compare (const name& l, const name& r) {return l.compare (r);} - static bool empty (const name& x) {return x.empty ();} - - static const bool empty_value = true; - static const char* const type_name; - static const build2::value_type value_type; - }; - - // name_pair - // - // An empty first or second half of a pair is treated as unspecified (this - // way it can be usage-specific whether a single value is first or second - // half of a pair). If both are empty then this is an empty value (and not a - // pair of two empties). - // - template <> - struct value_traits - { - static_assert (sizeof (name_pair) <= value::size_, "insufficient space"); - - static name_pair convert (name&&, name*); - static void assign (value&, name_pair&&); - static int compare (const name_pair&, const name_pair&); - static bool empty (const name_pair& x) { - return x.first.empty () && x.second.empty ();} - - static const bool empty_value = true; - static const char* const type_name; - static const build2::value_type value_type; - }; - - // process_path - // - // Note that instances that we store always have non-empty recall and - // initial is its shallow copy. - // - template <> - struct value_traits - { - static_assert (sizeof (process_path) <= value::size_, - "insufficient space"); - - // This one is represented as a @-pair of names. As a result it cannot - // be stored in a container. - // - static process_path convert (name&&, name*); - static void assign (value&, process_path&&); - static int compare (const process_path&, const process_path&); - static bool empty (const process_path& x) {return x.empty ();} - - static const bool empty_value = true; - static const char* const type_name; - static const build2::value_type value_type; - }; - - - // target_triplet - // - template <> - struct value_traits - { - static_assert (sizeof (target_triplet) <= value::size_, - "insufficient space"); - - static target_triplet convert (name&&, name*); - static void assign (value&, target_triplet&&); - static name reverse (const target_triplet& x) {return name (x.string ());} - static int compare (const target_triplet& x, const target_triplet& y) { - return x.compare (y);} - static bool empty (const target_triplet& x) {return x.empty ();} - - static const bool empty_value = true; - static const char* const type_name; - static const build2::value_type value_type; - }; - - // vector - // - template - struct value_traits> - { - static_assert (sizeof (vector) <= value::size_, "insufficient space"); - - static vector convert (names&&); - static void assign (value&, vector&&); - static void append (value&, vector&&); - static void prepend (value&, vector&&); - static bool empty (const vector& x) {return x.empty ();} - - // 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; - }; - - // map - // - template - struct value_traits> - { - template using map = std::map; - - static_assert (sizeof (map) <= value::size_, "insufficient space"); - - static void assign (value&, map&&); - static void append (value&, map&&); - static void prepend (value& v, map&& x) { - return append (v, move (x));} - static bool empty (const map& x) {return x.empty ();} - - // 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; - }; - - // Project-wide (as opposed to global) variable overrides. Returned by - // context.cxx:reset(). - // - struct variable_override - { - const variable& var; // Original variable. - const variable& ovr; // Override variable. - value val; - }; - - using variable_overrides = vector; - - // Variable pool. - // - // The global version is protected by the model mutex. - // - class variable_pool - { - public: - // Find existing or insert new. - // - const variable& - operator[] (const string& name) const; - - // Return NULL if there is no variable with this name. - // - const variable* - find (const string& name) const; - - // Find existing or insert new (untyped, non-overridable, normal - // visibility; but may be overridden by a pattern). - // - const variable& - insert (string name) - { - return insert (move (name), nullptr, nullptr, nullptr); - } - - // Insert or override (type/visibility). Note that by default the - // variable is not overridable. - // - const variable& - insert (string name, variable_visibility v) - { - return insert (move (name), nullptr, &v, nullptr); - } - - const variable& - insert (string name, bool overridable) - { - return insert (move (name), nullptr, nullptr, &overridable); - } - - const variable& - insert (string name, bool overridable, variable_visibility v) - { - return insert (move (name), nullptr, &v, &overridable); - } - - template - const variable& - insert (string name) - { - return insert (move (name), &value_traits::value_type); - } - - template - const variable& - insert (string name, variable_visibility v) - { - return insert (move (name), &value_traits::value_type, &v); - } - - template - const variable& - insert (string name, bool overridable) - { - return insert ( - move (name), &value_traits::value_type, nullptr, &overridable); - } - - template - const variable& - insert (string name, bool overridable, variable_visibility v) - { - return insert ( - move (name), &value_traits::value_type, &v, &overridable); - } - - // Insert a variable pattern. Any variable that matches this pattern - // will have the specified type, visibility, and overridability. If - // match is true, then individual insertions of the matching variable - // must match the specified type/visibility/overridability. Otherwise, - // individual insertions can provide alternative values and the pattern - // values are a fallback (if you specify false you better be very clear - // about what you are trying to achieve). - // - // The pattern must be in the form [.](*|**)[.] where - // '*' matches single component stems (i.e., 'foo' but not 'foo.bar') - // and '**' matches single and multi-component stems. Note that only - // multi-component variables are considered for pattern matching (so - // just '*' won't match anything). - // - // The patterns are matched in the more-specific-first order where the - // pattern is considered more specific if it has a greater sum of its - // prefix and suffix lengths. If the prefix and suffix are equal, then the - // '*' pattern is considered more specific than '**'. If neither is more - // specific, then they are matched in the reverse order of insertion. - // - // If retro is true then a newly inserted pattern is also applied - // retrospectively to all the existing variables that match but only - // if no more specific pattern already exists (which is then assumed - // to have been applied). So if you use this functionality, watch out - // for the insertion order (you probably want more specific first). - // - public: - void - insert_pattern (const string& pattern, - optional type, - optional overridable, - optional, - bool retro = false, - bool match = true); - - template - void - insert_pattern (const string& p, - optional overridable, - optional v, - bool retro = false, - bool match = true) - { - insert_pattern ( - p, &value_traits::value_type, overridable, v, retro, match); - } - - public: - void - clear () {map_.clear ();} - - variable_pool (): variable_pool (false) {} - - // RW access. - // - variable_pool& - rw () const - { - assert (phase == run_phase::load); - return const_cast (*this); - } - - variable_pool& - rw (scope&) const {return const_cast (*this);} - - private: - static variable_pool instance; - - const variable& - insert (string name, - const value_type*, - const variable_visibility* = nullptr, - const bool* overridable = nullptr); - - void - update (variable&, - const value_type*, - const variable_visibility* = nullptr, - const bool* = nullptr) const; - - // Entities that can access bypassing the lock proof. - // - friend class parser; - friend class scope; - friend variable_overrides reset (const strings&); - - public: - static const variable_pool& cinstance; // For var_pool initialization. - - // Variable map. - // - private: - using key = butl::map_key; - using map = std::unordered_map; - - pair - insert (variable&& var) - { - // Keeping a pointer to the key while moving things during insertion is - // tricky. We could use a C-string instead of C++ for a key but that - // gets hairy very quickly (there is no std::hash for C-strings). So - // let's rely on small object-optimized std::string for now. - // - string n (var.name); - auto r (map_.insert (map::value_type (&n, move (var)))); - - if (r.second) - r.first->first.p = &r.first->second.name; - - return r; - } - - map map_; - - // Patterns. - // - public: - struct pattern - { - string prefix; - string suffix; - bool multi; // Match multi-component stems. - bool match; // Must match individual variable insersions. - - optional type; - optional visibility; - optional overridable; - - friend bool - operator< (const pattern& x, const pattern& y) - { - if (x.prefix.size () + x.suffix.size () < - y.prefix.size () + y.suffix.size ()) - return true; - - if (x.prefix == y.prefix && x.suffix == y.suffix) - return x.multi && !y.multi; - - return false; - } - }; - - private: - std::multiset patterns_; - - // Global pool flag. - // - private: - explicit - variable_pool (bool global): global_ (global) {} - - bool global_; - }; - - extern const variable_pool& var_pool; -} - -// variable_map -// -namespace butl -{ - template <> - struct compare_prefix>: - compare_prefix - { - typedef compare_prefix base; - - explicit - compare_prefix (char d): base (d) {} - - bool - operator() (const build2::variable& x, const build2::variable& y) const - { - return base::operator() (x.name, y.name); - } - - bool - prefix (const build2::variable& p, const build2::variable& k) const - { - return base::prefix (p.name, k.name); - } - }; -} - -namespace build2 -{ - class variable_map - { - public: - struct value_data: value - { - using value::value; - using value::operator=; - - size_t version = 0; // Incremented on each modification (variable_cache). - size_t generation; // load_generation of this value (global state only). - }; - - using map_type = butl::prefix_map, - value_data, - '.'>; - using size_type = map_type::size_type; - - template - class iterator_adapter: public I - { - public: - iterator_adapter () = default; - iterator_adapter (const I& i, const variable_map& m): I (i), m_ (&m) {} - - // Automatically type a newly typed value on access. - // - typename I::reference operator* () const; - typename I::pointer operator-> () const; - - // Untyped access. - // - uint16_t extra () const {return I::operator* ().second.extra;} - typename I::reference untyped () const {return I::operator* ();} - - private: - const variable_map* m_; - }; - - using const_iterator = iterator_adapter; - - // Lookup. Note that variable overrides will not be applied, even if - // set in this map. - // - lookup - operator[] (const variable& var) const - { - return lookup (find (var), this); - } - - lookup - operator[] (const variable* var) const // For cached variables. - { - assert (var != nullptr); - return operator[] (*var); - } - - lookup - operator[] (const string& name) const - { - const variable* var (var_pool.find (name)); - return var != nullptr ? operator[] (*var) : lookup (); - } - - // If typed is false, leave the value untyped even if the variable is. - // - const value_data* - find (const variable&, bool typed = true) const; - - value_data* - find_to_modify (const variable&, bool typed = true); - - // Convert a lookup pointing to a value belonging to this variable map - // to its non-const version. Note that this is only safe on the original - // values (see find_original()). - // - value& - modify (const lookup& l) - { - assert (l.vars == this); - value& r (const_cast (*l.value)); - static_cast (r).version++; - return r; - } - - // Return a value suitable for assignment. See scope for details. - // - value& - assign (const variable& var) {return insert (var).first;} - - // Note that the variable is expected to have already been registered. - // - value& - assign (const string& name) {return insert (var_pool[name]).first;} - - // As above but also return an indication of whether the new value (which - // will be NULL) was actually inserted. Similar to find(), if typed is - // false, leave the value untyped even if the variable is. - // - pair, bool> - insert (const variable&, bool typed = true); - - pair - find_namespace (const variable& ns) const - { - auto r (m_.find_prefix (ns)); - return make_pair (const_iterator (r.first, *this), - const_iterator (r.second, *this)); - } - - const_iterator - begin () const {return const_iterator (m_.begin (), *this);} - - const_iterator - end () const {return const_iterator (m_.end (), *this);} - - bool - empty () const {return m_.empty ();} - - size_type - size () const {return m_.size ();} - - public: - // Global should be true if this map is part of the global (model) state - // (e.g., scopes, etc). - // - explicit - variable_map (bool global = false): global_ (global) {} - - private: - friend class variable_type_map; - - void - typify (value_data&, const variable&) const; - - private: - bool global_; - map_type m_; - }; - - // Value caching. Used for overrides as well as target type/pattern-specific - // append/prepend. - // - // In many places we assume that we can store a reference to the returned - // variable value (e.g., install::lookup_install()). As a result, in these - // cases where we calculate the value dynamically, we have to cache it - // (note, however, that if the value becomes stale, there is no guarantee - // the references remain valid). - // - // Note that since the cache can be modified on any lookup (including during - // the execute phase), it is protected by its own mutex shard (allocated in - // main()). - // - extern size_t variable_cache_mutex_shard_size; - extern unique_ptr variable_cache_mutex_shard; - - template - class variable_cache - { - public: - // If the returned unique lock is locked, then the value has been - // invalidated. If the variable type does not match the value type, - // then typify the cached value. - // - pair - insert (K, const lookup& stem, size_t version, const variable&); - - private: - struct entry_type - { - // Note: we use value_data instead of value since the result is often - // returned as lookup. We also maintain the version in case one cached - // value (e.g., override) is based on another (e.g., target - // type/pattern-specific prepend/append). - // - variable_map::value_data value; - - size_t version = 0; // Version on which this value is based. - - // Location of the stem as well as the version on which this cache - // value is based. Used to track the location and value of the stem - // for cache invalidation. NULL/0 means there is no stem. - // - const variable_map* stem_vars = nullptr; - size_t stem_version = 0; - - // For GCC 4.9. - // - entry_type () = default; - entry_type (variable_map::value_data val, - size_t ver, - const variable_map* svars, - size_t sver) - : value (move (val)), - version (ver), - stem_vars (svars), - stem_version (sver) {} - }; - - using map_type = std::map; - - map_type m_; - }; - - // Target type/pattern-specific variables. - // - class variable_pattern_map - { - public: - using map_type = std::map; - using const_iterator = map_type::const_iterator; - using const_reverse_iterator = map_type::const_reverse_iterator; - - explicit - variable_pattern_map (bool global): global_ (global) {} - - variable_map& - operator[] (const string& v) - { - return map_.emplace (v, variable_map (global_)).first->second; - } - - const_iterator begin () const {return map_.begin ();} - const_iterator end () const {return map_.end ();} - const_reverse_iterator rbegin () const {return map_.rbegin ();} - const_reverse_iterator rend () const {return map_.rend ();} - bool empty () const {return map_.empty ();} - - private: - bool global_; - map_type map_; - }; - - class variable_type_map - { - public: - using map_type = std::map, - variable_pattern_map>; - using const_iterator = map_type::const_iterator; - - explicit - variable_type_map (bool global): global_ (global) {} - - variable_pattern_map& - operator[] (const target_type& t) - { - return map_.emplace (t, variable_pattern_map (global_)).first->second; - } - - const_iterator begin () const {return map_.begin ();} - const_iterator end () const {return map_.end ();} - bool empty () const {return map_.empty ();} - - lookup - find (const target_type&, const string& tname, const variable&) const; - - // Prepend/append value cache. - // - // The key is the combination of the "original value identity" (as a - // pointer to the value in one of the variable_pattern_map's) and the - // "target identity" (as target type and target name). Note that while at - // first it may seem like we don't need the target identity, we actually - // do since the stem may itself be target-type/pattern-specific. See - // scope::find_original() for details. - // - mutable - variable_cache> - cache; - - private: - bool global_; - map_type map_; - }; -} - -#include -#include - -#endif // BUILD2_VARIABLE -- cgit v1.1