// 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 #include // unique_ptr #include // nullptr_t #include // move() #include #include // hash #include #include #include #include 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 variable_cref; // value // struct value; typedef std::unique_ptr 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 (&v)); return lv != nullptr && static_cast (*this) == *lv; } }; typedef std::unique_ptr 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 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 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 () const {return **p;} template <> inline const value& value_proxy:: as () const {return **p;} template <> inline list_value& value_proxy:: as () const { list_value* lv (dynamic_cast (p->get ())); assert (lv != nullptr); return *lv; } template <> inline const list_value& value_proxy:: as () const {return as ();} template <> const std::string& value_proxy:: as () const; template <> const dir_path& value_proxy:: as () const; } namespace std { template <> struct hash: hash { size_t operator() (const build::variable& v) const noexcept { return hash::operator() (v.name); } }; } namespace butl { template <> struct compare_prefix: compare_prefix { typedef compare_prefix 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 { // @@ 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; 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 (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 find_namespace (const std::string& ns) { return find_prefix (variable_pool.find (ns)); } std::pair find_namespace (const std::string& ns) const { return find_prefix (variable_pool.find (ns)); } }; } #include #endif // BUILD_VARIABLE