// file : libbuild2/variable.ixx -*- C++ -*- // copyright : Copyright (c) 2014-2019 Code Synthesis Ltd // license : MIT; see accompanying LICENSE file #include // is_same #include namespace build2 { // value // inline bool value:: empty () const { assert (!null); return type == nullptr ? as ().empty () : type->empty == nullptr ? false : type->empty (*this); } inline value:: value (names ns) : type (nullptr), null (false), extra (0) { new (&data_) names (move (ns)); } inline value:: value (optional ns) : type (nullptr), null (!ns), extra (0) { if (!null) new (&data_) names (move (*ns)); } template inline value:: value (T v) : type (&value_traits::value_type), null (true), extra (0) { value_traits::assign (*this, move (v)); null = false; } template inline value:: value (optional v) : type (&value_traits::value_type), null (true), extra (0) { if (v) { value_traits::assign (*this, move (*v)); null = false; } } inline value& value:: operator= (reference_wrapper v) { return *this = v.get (); } inline value& value:: operator= (reference_wrapper v) { return *this = v.get (); } template inline value& value:: operator= (T v) { assert (type == &value_traits::value_type || type == nullptr); // Prepare the receiving value. // if (type == nullptr) { *this = nullptr; type = &value_traits::value_type; } value_traits::assign (*this, move (v)); null = false; return *this; } template inline value& value:: operator+= (T v) { assert (type == &value_traits::value_type || (type == nullptr && null)); // Prepare the receiving value. // if (type == nullptr) type = &value_traits::value_type; value_traits::append (*this, move (v)); null = false; return *this; } inline void value:: assign (name&& n, const variable* var) { names ns; ns.push_back (move (n)); assign (move (ns), var); } inline bool operator!= (const value& x, const value& y) { return !(x == y); } inline bool operator<= (const value& x, const value& y) { return !(x > y); } inline bool operator>= (const value& x, const value& y) { return !(x < y); } template <> inline const names& cast (const value& v) { assert (v && v.type == nullptr); return v.as (); } template <> inline names& cast (value& v) { assert (v && v.type == nullptr); return v.as (); } template inline const T& cast (const value& v) { assert (v); // Find base if any. // // Note that here we use the value type address as type identity. // const value_type* b (v.type); for (; b != nullptr && b != &value_traits::value_type; b = b->base_type) ; assert (b != nullptr); return *static_cast (v.type->cast == nullptr ? static_cast (&v.data_) : v.type->cast (v, b)); } template inline T& cast (value& v) { // Forward to const T&. // return const_cast (cast (static_cast (v))); } template inline T&& cast (value&& v) { return move (cast (v)); // Forward to T&. } template inline const T& cast (const lookup& l) { return cast (*l); } template inline T* cast_null (value& v) { return v ? &cast (v) : nullptr; } template inline const T* cast_null (const value& v) { return v ? &cast (v) : nullptr; } template inline const T* cast_null (const lookup& l) { return l ? &cast (*l) : nullptr; } template inline const T& cast_empty (const value& v) { return v ? cast (v) : value_traits::empty_instance; } template inline const T& cast_empty (const lookup& l) { return l ? cast (l) : value_traits::empty_instance; } template inline T cast_default (const value& v, const T& d) { return v ? cast (v) : d; } template inline T cast_default (const lookup& l, const T& d) { return l ? cast (l) : d; } template inline T cast_false (const value& v) { return v && cast (v); } template inline T cast_false (const lookup& l) { return l && cast (l); } template inline T cast_true (const value& v) { return !v || cast (v); } template inline T cast_true (const lookup& l) { return !l || cast (l); } template inline void typify (value& v, const variable* var) { const value_type& t (value_traits::value_type); if (v.type != &t) typify (v, t, var); } LIBBUILD2_SYMEXPORT void typify (value&, const value_type&, const variable*, memory_order); inline void typify (value& v, const value_type& t, const variable* var) { typify (v, t, var, memory_order_relaxed); } inline vector_view reverse (const value& v, names& storage) { assert (v && storage.empty () && (v.type == nullptr || v.type->reverse != nullptr)); return v.type == nullptr ? v.as () : v.type->reverse (v, storage); } inline vector_view reverse (value& v, names& storage) { names_view cv (reverse (static_cast (v), storage)); return vector_view (const_cast (cv.data ()), cv.size ()); } // value_traits // template inline T convert (name&& n) { return value_traits::convert (move (n), nullptr); } template inline T convert (name&& l, name&& r) { return value_traits::convert (move (l), &r); } // This one will be SFINAE'd out unless T is a container. // template inline auto convert (names&& ns) -> decltype (value_traits::convert (move (ns))) { return value_traits::convert (move (ns)); } // bool value // inline void value_traits:: assign (value& v, bool x) { if (v) v.as () = x; else new (&v.data_) bool (x); } inline void value_traits:: append (value& v, bool x) { // Logical OR. // if (v) v.as () = v.as () || x; else new (&v.data_) bool (x); } inline int value_traits:: compare (bool l, bool r) { return l < r ? -1 : (l > r ? 1 : 0); } // uint64_t value // inline void value_traits:: assign (value& v, uint64_t x) { if (v) v.as () = x; else new (&v.data_) uint64_t (x); } inline void value_traits:: append (value& v, uint64_t x) { // ADD. // if (v) v.as () += x; else new (&v.data_) uint64_t (x); } inline int value_traits:: compare (uint64_t l, uint64_t r) { return l < r ? -1 : (l > r ? 1 : 0); } // string value // inline void value_traits:: assign (value& v, string&& x) { if (v) v.as () = move (x); else new (&v.data_) string (move (x)); } inline void value_traits:: append (value& v, string&& x) { if (v) { string& s (v.as ()); if (s.empty ()) s.swap (x); else s += x; } else new (&v.data_) string (move (x)); } inline void value_traits:: prepend (value& v, string&& x) { if (v) { string& s (v.as ()); if (!s.empty ()) x += s; s.swap (x); } else new (&v.data_) string (move (x)); } inline int value_traits:: compare (const string& l, const string& r) { return l.compare (r); } // path value // inline void value_traits:: assign (value& v, path&& x) { if (v) v.as () = move (x); else new (&v.data_) path (move (x)); } inline void value_traits:: append (value& v, path&& x) { if (v) { path& p (v.as ()); if (p.empty ()) p.swap (x); else p /= x; } else new (&v.data_) path (move (x)); } inline void value_traits:: prepend (value& v, path&& x) { if (v) { path& p (v.as ()); if (!p.empty ()) x /= p; p.swap (x); } else new (&v.data_) path (move (x)); } inline int value_traits:: compare (const path& l, const path& r) { return l.compare (r); } // dir_path value // inline void value_traits:: assign (value& v, dir_path&& x) { if (v) v.as () = move (x); else new (&v.data_) dir_path (move (x)); } inline void value_traits:: append (value& v, dir_path&& x) { if (v) { dir_path& p (v.as ()); if (p.empty ()) p.swap (x); else p /= x; } else new (&v.data_) dir_path (move (x)); } inline void value_traits:: prepend (value& v, dir_path&& x) { if (v) { dir_path& p (v.as ()); if (!p.empty ()) x /= p; p.swap (x); } else new (&v.data_) dir_path (move (x)); } inline int value_traits:: compare (const dir_path& l, const dir_path& r) { return l.compare (r); } // abs_dir_path value // inline void value_traits:: assign (value& v, abs_dir_path&& x) { if (v) v.as () = move (x); else new (&v.data_) abs_dir_path (move (x)); } inline void value_traits:: append (value& v, abs_dir_path&& x) { if (v) { abs_dir_path& p (v.as ()); if (p.empty ()) p.swap (x); else p /= x; } else new (&v.data_) abs_dir_path (move (x)); } inline int value_traits:: compare (const abs_dir_path& l, const abs_dir_path& r) { return l.compare (static_cast (r)); } // name value // inline void value_traits:: assign (value& v, name&& x) { if (v) v.as () = move (x); else new (&v.data_) name (move (x)); } // name_pair value // inline void value_traits:: assign (value& v, name_pair&& x) { if (v) v.as () = move (x); else new (&v.data_) name_pair (move (x)); } inline int value_traits:: compare (const name_pair& x, const name_pair& y) { int r (x.first.compare (y.first)); if (r == 0) r = x.second.compare (y.second); return r; } // process_path value // inline void value_traits:: assign (value& v, process_path&& x) { // Convert the value to its "self-sufficient" form. // if (x.recall.empty ()) x.recall = path (x.initial); x.initial = x.recall.string ().c_str (); if (v) v.as () = move (x); else new (&v.data_) process_path (move (x)); } inline int value_traits:: compare (const process_path& x, const process_path& y) { int r (x.recall.compare (y.recall)); if (r == 0) r = x.effect.compare (y.effect); return r; } // target_triplet value // inline void value_traits:: assign (value& v, target_triplet&& x) { if (v) v.as () = move (x); else new (&v.data_) target_triplet (move (x)); } // project_name value // inline void value_traits:: assign (value& v, project_name&& x) { if (v) v.as () = move (x); else new (&v.data_) project_name (move (x)); } inline name value_traits:: reverse (const project_name& x) { // Make work for the special unnamed subproject representation (see // find_subprojects() in file.cxx for details). // const string& s (x.string ()); return name (s.empty () || path::traits_type::is_separator (s.back ()) ? empty_string : s); } // vector value // template inline void value_traits>:: assign (value& v, vector&& x) { if (v) v.as> () = move (x); else new (&v.data_) vector (move (x)); } template inline void value_traits>:: append (value& v, vector&& x) { if (v) { vector& p (v.as> ()); if (p.empty ()) p.swap (x); else p.insert (p.end (), make_move_iterator (x.begin ()), make_move_iterator (x.end ())); } else new (&v.data_) vector (move (x)); } template inline void value_traits>:: prepend (value& v, vector&& x) { if (v) { vector& p (v.as> ()); if (!p.empty ()) x.insert (x.end (), make_move_iterator (p.begin ()), make_move_iterator (p.end ())); p.swap (x); } else new (&v.data_) vector (move (x)); } // map value // template inline void value_traits>:: assign (value& v, map&& x) { if (v) v.as> () = move (x); else new (&v.data_) map (move (x)); } template inline void value_traits>:: append (value& v, map&& x) { if (v) { map& m (v.as> ()); if (m.empty ()) m.swap (x); else // Note that this will only move values. Keys (being const) are still // copied. // m.insert (make_move_iterator (x.begin ()), make_move_iterator (x.end ())); } else new (&v.data_) map (move (x)); } // variable_pool // inline const variable& variable_pool:: operator[] (const string& n) const { const variable* r (find (n)); assert (r != nullptr); return *r; } inline const variable* variable_pool:: find (const string& n) const { auto i (map_.find (&n)); return i != map_.end () ? &i->second : nullptr; } // variable_map // inline void variable_map:: typify (const value_data& v, const variable& var) const { // We assume typification is not modification so no version increment. // if (ctx->phase == run_phase::load) { if (v.type != var.type) build2::typify (const_cast (v), *var.type, &var); } else { if (v.type.load (memory_order_acquire) != var.type) build2::typify_atomic (const_cast (v), *var.type, &var); } } // variable_map::iterator_adapter // template inline typename I::reference variable_map::iterator_adapter:: operator* () const { auto& r (I::operator* ()); const variable& var (r.first); const value_data& val (r.second); // Check if this is the first access after being assigned a type. // if (var.type != nullptr) m_->typify (val, var); return r; } template inline typename I::pointer variable_map::iterator_adapter:: operator-> () const { auto p (I::operator-> ()); const variable& var (p->first); const value_data& val (p->second); // Check if this is the first access after being assigned a type. // if (var.type != nullptr) m_->typify (val, var); return p; } }