// file : build/variable -*- C++ -*- // copyright : Copyright (c) 2014-2015 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file #ifndef BUILD_VARIABLE #define BUILD_VARIABLE #include <string> #include <memory> // unique_ptr #include <cstddef> // nullptr_t #include <utility> // move() #include <cassert> #include <functional> // hash #include <typeindex> #include <unordered_set> #include <butl/prefix-map> #include <build/types> namespace build { struct value; struct value_type { std::type_index id; value* (*const factory) (); }; // variable // // The two variables are considered the same if they have the same name. // struct variable { explicit variable (std::string n): name (std::move (n)), type (nullptr) {} std::string name; const value_type* type; // If NULL, then this variable has no fixed type. }; inline bool operator== (const variable& x, const variable& y) {return x.name == y.name;} typedef std::reference_wrapper<const variable> variable_cref; // value // struct value; typedef std::unique_ptr<value> value_ptr; struct value { public: virtual value_ptr clone () const = 0; virtual bool compare (const value&) const = 0; virtual ~value () = default; }; class list_value: public value, public names { public: using names::names; list_value () = default; list_value (names d): names (std::move (d)) {} list_value (std::string d) {emplace_back (std::move (d));} list_value (dir_path d) {emplace_back (std::move (d));} virtual value_ptr clone () const {return value_ptr (new list_value (*this));} virtual bool compare (const value& v) const { const list_value* lv (dynamic_cast<const list_value*> (&v)); return lv != nullptr && static_cast<const names&> (*this) == *lv; } }; typedef std::unique_ptr<list_value> list_value_ptr; // value_proxy // // A variable can be undefined, null, or contain some actual value. // Note that once value_proxy is bound to a value, the only way to // rebind it to a different value is by using explicit rebind(). In // particular, assigning one value proxy to another will assing the // values. // struct variable_map; struct value_proxy { bool defined () const {return p != nullptr;} bool null () const {return *p == nullptr;} explicit operator bool () const {return defined () && !null ();} explicit operator value_ptr& () const {return *p;} // Get interface. See available specializations below. // template <typename T> T as () const; // Assign. // const value_proxy& operator= (value_ptr) const; const value_proxy& operator= (const value_proxy&) const; const value_proxy& operator= (list_value) const; const value_proxy& operator= (std::string) const; const value_proxy& operator= (dir_path) const; const value_proxy& operator= (std::nullptr_t) const; // Append. // const value_proxy& operator+= (const value_proxy&) const; const value_proxy& operator+= (const list_value&) const; const value_proxy& operator+= (std::string) const; // Append simple name to list_value. // Return true if this value belongs to the specified scope or target. // template <typename T> bool belongs (const T& x) const {return map == &x.vars;} // Implementation details. // const variable_map* map; // Variable map to which this value belongs. value_proxy (): map (nullptr), p (nullptr) {} value_proxy (value_ptr* p, const variable_map* m): map (m), p (p) {} void rebind (const value_proxy& x) {map = x.map; p = x.p;} private: value_ptr* p; }; template <> inline value& value_proxy:: as<value&> () const {return **p;} template <> inline const value& value_proxy:: as<const value&> () const {return **p;} template <> inline list_value& value_proxy:: as<list_value&> () const { list_value* lv (dynamic_cast<list_value*> (p->get ())); assert (lv != nullptr); return *lv; } template <> inline const list_value& value_proxy:: as<const list_value&> () const {return as<list_value&> ();} template <> const std::string& value_proxy:: as<const std::string&> () const; template <> const dir_path& value_proxy:: as<const dir_path&> () const; } namespace std { template <> struct hash<build::variable>: hash<string> { size_t operator() (const build::variable& v) const noexcept { return hash<string>::operator() (v.name); } }; } namespace butl { template <> struct compare_prefix<build::variable_cref>: compare_prefix<std::string> { typedef compare_prefix<std::string> base; explicit compare_prefix (char d): base (d) {} bool operator() (const build::variable& x, const build::variable& y) const { return base::operator() (x.name, y.name); } bool prefix (const build::variable& p, const build::variable& k) const { return base::prefix (p.name, k.name); } }; } namespace build { // variable_pool // struct variable_set: std::unordered_set<variable> { // @@ Need to check/set type? // const variable& find (std::string name) {return *emplace (std::move (name)).first;} }; extern variable_set variable_pool; // variable_map // using variable_map_base = butl::prefix_map<variable_cref, value_ptr, '.'>; struct variable_map: variable_map_base { value_proxy operator[] (const variable& var) const { auto i (find (var)); return i != end () // @@ To do this properly we seem to need ro_value_proxy. // ? value_proxy (&const_cast<value_ptr&> (i->second), this) : value_proxy (nullptr, nullptr); } value_proxy operator[] (const std::string& name) const { return operator[] (variable_pool.find (name)); } value_proxy assign (const variable& var) { return value_proxy (&variable_map_base::operator[] (var), this); } value_proxy assign (const std::string& name) { return assign (variable_pool.find (name)); } std::pair<iterator, iterator> find_namespace (const std::string& ns) { return find_prefix (variable_pool.find (ns)); } std::pair<const_iterator, const_iterator> find_namespace (const std::string& ns) const { return find_prefix (variable_pool.find (ns)); } }; } #include <build/variable.ixx> #endif // BUILD_VARIABLE