diff options
Diffstat (limited to 'build2/function.hxx')
-rw-r--r-- | build2/function.hxx | 232 |
1 files changed, 209 insertions, 23 deletions
diff --git a/build2/function.hxx b/build2/function.hxx index 0e592b1..96bf401 100644 --- a/build2/function.hxx +++ b/build2/function.hxx @@ -5,9 +5,9 @@ #ifndef BUILD2_FUNCTION_HXX #define BUILD2_FUNCTION_HXX +#include <map> #include <utility> // index_sequence #include <type_traits> // aligned_storage -#include <unordered_map> #include <build2/types.hxx> #include <build2/utility.hxx> @@ -45,6 +45,9 @@ namespace build2 // expected to issue diagnostics and throw failed. Note that the arguments // are conceptually "moved" and can be reused by the implementation. // + // A function can also optionally receive the current scope by having the + // first argument of the const scope& type. + // // Normally functions come in families that share a common qualification // (e.g., string. or path.). The function_family class is a "registrar" // that simplifies handling of function families. For example: @@ -64,7 +67,9 @@ namespace build2 // struct function_overload; - using function_impl = value (vector_view<value>, const function_overload&); + using function_impl = value (const scope&, + vector_view<value>, + const function_overload&); struct function_overload { @@ -134,7 +139,7 @@ namespace build2 class function_map { public: - using map_type = std::unordered_multimap<string, function_overload>; + using map_type = std::multimap<string, function_overload>; using iterator = map_type::iterator; using const_iterator = map_type::const_iterator; @@ -145,9 +150,12 @@ namespace build2 erase (iterator i) {map_.erase (i);} value - call (const string& name, vector_view<value> args, const location& l) const + call (const scope& base, + const string& name, + vector_view<value> args, + const location& l) const { - return call (name, args, l, true).first; + return call (base, name, args, l, true).first; } // As above but do not fail if no match was found (but still do if the @@ -156,11 +164,12 @@ namespace build2 // functions. // pair<value, bool> - try_call (const string& name, + try_call (const scope& base, + const string& name, vector_view<value> args, const location& l) const { - return call (name, args, l, false); + return call (base, name, args, l, false); } iterator @@ -175,9 +184,20 @@ namespace build2 const_iterator end () const {return map_.end ();} + // Return true if the function with this name is already defined. If the + // name ends with '.', then instead check if any function with this prefix + // (which we call a family) is already defined. + // + bool + defined (const string&) const; + private: pair<value, bool> - call (const string&, vector_view<value>, const location&, bool fail) const; + call (const scope&, + const string&, + vector_view<value>, + const location&, + bool fail) const; map_type map_; }; @@ -195,7 +215,7 @@ namespace build2 // exceptions), you would normally call the default implementation. // static value - default_thunk (vector_view<value>, const function_overload&); + default_thunk (const scope&, vector_view<value>, const function_overload&); // A function family uses a common qualification (though you can pass // empty string to supress it). For an unqualified name (doesn't not @@ -212,6 +232,13 @@ namespace build2 entry operator[] (string name) const; + static bool + defined (string qual) + { + qual += '.'; + return functions.defined (qual); + } + private: const string qual_; function_impl* thunk_; @@ -392,12 +419,12 @@ namespace build2 // struct data { - value (*const thunk) (vector_view<value>, const void*); + value (*const thunk) (const scope&, vector_view<value>, const void*); R (*const impl) (A...); }; static value - thunk (vector_view<value> args, const void* d) + thunk (const scope&, vector_view<value> args, const void* d) { return thunk (move (args), static_cast<const data*> (d)->impl, @@ -417,6 +444,39 @@ namespace build2 } }; + // Specialization for functions that expect the current scope as a first + // argument. + // + template <typename R, typename... A> + struct function_cast<R, const scope&, A...> + { + struct data + { + value (*const thunk) (const scope&, vector_view<value>, const void*); + R (*const impl) (const scope&, A...); + }; + + static value + thunk (const scope& base, vector_view<value> args, const void* d) + { + return thunk (base, move (args), + static_cast<const data*> (d)->impl, + std::index_sequence_for<A...> ()); + } + + template <size_t... i> + static value + thunk (const scope& base, vector_view<value> args, + R (*impl) (const scope&, A...), + std::index_sequence<i...>) + { + return value ( + impl (base, + function_arg<A>::cast ( + i < args.size () ? &args[i] : nullptr)...)); + } + }; + // Specialization for void return type. In this case we return NULL value. // template <typename... A> @@ -424,12 +484,12 @@ namespace build2 { struct data { - value (*const thunk) (vector_view<value>, const void*); + value (*const thunk) (const scope&, vector_view<value>, const void*); void (*const impl) (A...); }; static value - thunk (vector_view<value> args, const void* d) + thunk (const scope&, vector_view<value> args, const void* d) { thunk (move (args), static_cast<const data*> (d)->impl, @@ -447,6 +507,35 @@ namespace build2 } }; + template <typename... A> + struct function_cast<void, const scope&, A...> + { + struct data + { + value (*const thunk) (const scope&, vector_view<value>, const void*); + void (*const impl) (const scope&, A...); + }; + + static value + thunk (const scope& base, vector_view<value> args, const void* d) + { + thunk (base, move (args), + static_cast<const data*> (d)->impl, + std::index_sequence_for<A...> ()); + return value (nullptr); + } + + template <size_t... i> + static void + thunk (const scope& base, vector_view<value> args, + void (*impl) (const scope&, A...), + std::index_sequence<i...>) + { + impl (base, + function_arg<A>::cast (i < args.size () ? &args[i] : nullptr)...); + } + }; + // Customization for coerced lambdas (see below). // #if defined(__GNUC__) && !defined(__clang__) && __GNUC__ < 6 @@ -455,12 +544,12 @@ namespace build2 { struct data { - value (*const thunk) (vector_view<value>, const void*); + value (*const thunk) (const scope&, vector_view<value>, const void*); R (L::*const impl) (A...) const; }; static value - thunk (vector_view<value> args, const void* d) + thunk (const scope&, vector_view<value> args, const void* d) { return thunk (move (args), static_cast<const data*> (d)->impl, @@ -482,17 +571,49 @@ namespace build2 } }; + template <typename L, typename R, typename... A> + struct function_cast_lamb<L, R, const scope&, A...> + { + struct data + { + value (*const thunk) (const scope&, vector_view<value>, const void*); + R (L::*const impl) (const scope&, A...) const; + }; + + static value + thunk (const scope& base, vector_view<value> args, const void* d) + { + return thunk (base, move (args), + static_cast<const data*> (d)->impl, + std::index_sequence_for<A...> ()); + } + + template <size_t... i> + static value + thunk (const scope& base, vector_view<value> args, + R (L::*impl) (const scope&, A...) const, + std::index_sequence<i...>) + { + const L* l (nullptr); // Undefined behavior. + + return value ( + (l->*impl) (base, + function_arg<A>::cast ( + i < args.size () ? &args[i] : nullptr)...)); + } + }; + template <typename L, typename... A> struct function_cast_lamb<L, void, A...> { struct data { - value (*const thunk) (vector_view<value>, const void*); + value (*const thunk) (const scope&, vector_view<value>, const void*); void (L::*const impl) (A...) const; }; static value - thunk (vector_view<value> args, const void* d) + thunk (const scope&, vector_view<value> args, const void* d) { thunk (move (args), static_cast<const data*> (d)->impl, @@ -512,6 +633,37 @@ namespace build2 i < args.size () ? &args[i] : nullptr)...); } }; + + template <typename L, typename... A> + struct function_cast_lamb<L, void, const scope&, A...> + { + struct data + { + value (*const thunk) (const scope&, vector_view<value>, const void*); + void (L::*const impl) (const scope&, A...) const; + }; + + static value + thunk (const scope& base, vector_view<value> args, const void* d) + { + thunk (base, move (args), + static_cast<const data*> (d)->impl, + std::index_sequence_for<A...> ()); + return value (nullptr); + } + + template <size_t... i> + static void + thunk (const scope& base, vector_view<value> args, + void (L::*impl) (const scope&, A...) const, + std::index_sequence<i...>) + { + const L* l (nullptr); + (l->*impl) (base, + function_arg<A>::cast ( + i < args.size () ? &args[i] : nullptr)...); + } + }; #endif // Customization for member functions. @@ -521,12 +673,12 @@ namespace build2 { struct data { - value (*const thunk) (vector_view<value>, const void*); + value (*const thunk) (const scope&, vector_view<value>, const void*); R (T::*const impl) () const; }; static value - thunk (vector_view<value> args, const void* d) + thunk (const scope&, vector_view<value> args, const void* d) { auto mf (static_cast<const data*> (d)->impl); return value ((function_arg<T>::cast (&args[0]).*mf) ()); @@ -538,12 +690,12 @@ namespace build2 { struct data { - value (*const thunk) (vector_view<value>, const void*); + value (*const thunk) (const scope&, vector_view<value>, const void*); void (T::*const impl) () const; }; static value - thunk (vector_view<value> args, const void* d) + thunk (const scope&, vector_view<value> args, const void* d) { auto mf (static_cast<const data*> (d)->impl); (function_arg<T>::cast (args[0]).*mf) (); @@ -558,12 +710,12 @@ namespace build2 { struct data { - value (*const thunk) (vector_view<value>, const void*); + value (*const thunk) (const scope&, vector_view<value>, const void*); R T::*const impl; }; static value - thunk (vector_view<value> args, const void* d) + thunk (const scope&, vector_view<value> args, const void* d) { auto dm (static_cast<const data*> (d)->impl); return value (move (function_arg<T>::cast (&args[0]).*dm)); @@ -593,6 +745,23 @@ namespace build2 typename cast::data {&cast::thunk, impl})); } + template <typename R, typename... A> + void + operator= (R (*impl) (const scope&, A...)) && + { + using args = function_args<A...>; + using cast = function_cast<R, const scope&, A...>; + + insert (move (name), + function_overload ( + nullptr, + args::min, + args::max, + function_overload::types (args::types, args::max), + thunk, + typename cast::data {&cast::thunk, impl})); + } + // Support for assigning a (capture-less) lambda. // // GCC up until version 6 has a bug (#62052) that is triggered by calling @@ -625,6 +794,23 @@ namespace build2 thunk, typename cast::data {&cast::thunk, op})); } + + template <typename L, typename R, typename... A> + void + coerce_lambda (R (L::*op) (const scope&, A...) const) && + { + using args = function_args<A...>; + using cast = function_cast_lamb<L, R, const scope&, A...>; + + insert (move (name), + function_overload ( + nullptr, + args::min, + args::max, + function_overload::types (args::types, args::max), + thunk, + typename cast::data {&cast::thunk, op})); + } #else template <typename L> void |