diff --git a/include/xo/reflect/Reflect.hpp b/include/xo/reflect/Reflect.hpp index bba62ae1..20e862c9 100644 --- a/include/xo/reflect/Reflect.hpp +++ b/include/xo/reflect/Reflect.hpp @@ -72,16 +72,38 @@ namespace xo { // ----- Retval (*)(A1 .. An) ----- template - class EstablishTdx { + class EstablishTdx { public: /* definition provided after decl for Reflect {} below */ static std::unique_ptr make(); }; - // ----- Retval (*)() ----- + // ----- Retval (*)(A1 .. An) noexcept ----- - template - class EstablishTdx { + template + class EstablishTdx { + public: + /* definition provided after decl for Reflect {} below */ + static std::unique_ptr make(); + }; + + // ----- EstasblishFunctionTdx ------------------------------------- + + template + class EstablishFunctionTdx; + + // ----- Retval (*)(A1 .. An) ----- + + template + class EstablishFunctionTdx { + public: + /* definition provided after decl for Reflect {} below */ + static std::unique_ptr make(); + }; + + template + class EstablishFunctionTdx { + public: /* definition provided after decl for Reflect {} below */ static std::unique_ptr make(); }; @@ -170,6 +192,41 @@ namespace xo { return retval_td; } /*require*/ + /* can optionally use this when reflecting a function pointer. + * Should get the same result as reflect(), + * but will not fallback to AtomicTdx if T is not recognized as a function pointer + */ + template + static TypeDescrW require_function() { + //static_assert(std::is_function_v); + + TypeDescrW retval_td = EstablishTypeDescr::establish(); + + /* mark TypeDescr for T as complete (even though it isn't quite yet), + * so that when we encounter recursive types, reflection terminates. + * For example consider type resulting from code like + * + * typename T; + * using T = std::vector; + * + */ + if (retval_td->mark_complete()) { + /* control here on 2nd+later calls to require(). + * in principle can immediately short-circuit. + */ + } else { + /* control comes here the first time require() runs */ + + auto final_tdx = EstablishFunctionTdx::make(); + + retval_td->assign_tdextra(std::move(final_tdx)); + + /* also need to require for each child */ + } + + return retval_td; + } + /* Use: * T * xyz = ...; * TaggedPtr xyz_tp = Reflect::make_tp(xyz); @@ -256,7 +313,7 @@ namespace xo { return StructTdx::pair(); } /*make*/ - // ----- Retval (*) (A1 .. An) ----- + // ----- Retval(A1 .. An) ----- namespace detail { /** @class AssembleArgv @@ -272,7 +329,7 @@ namespace xo { template <> struct AssembleArgv<> { - static void append_argv(std::vector * p_v) {} + static void append_argv(std::vector * /*p_v*/) {} }; template @@ -284,6 +341,28 @@ namespace xo { }; } /*detail*/ + template + std::unique_ptr + EstablishFunctionTdx::make() { + std::vector argv; + detail::AssembleArgv::append_argv(&argv); + + return FunctionTdx::make_function(Reflect::require(), + std::move(argv), + false /*!is_noexcept*/); + } + + template + std::unique_ptr + EstablishFunctionTdx::make() { + std::vector argv; + detail::AssembleArgv::append_argv(&argv); + + return FunctionTdx::make_function(Reflect::require(), + std::move(argv), + true /*is_noexcept*/); + } + /* declared above before * class Reflect { ... } */ @@ -293,8 +372,25 @@ namespace xo { std::vector argv; detail::AssembleArgv::append_argv(&argv); - return FunctionTdx::make_function(Reflect::require(), std::move(argv)); + return FunctionTdx::make_function(Reflect::require(), + std::move(argv), + false /*!is_noexcept*/); } + + /* declared above before + * class Reflect { ... } + */ + template + std::unique_ptr + EstablishTdx::make() { + std::vector argv; + detail::AssembleArgv::append_argv(&argv); + + return FunctionTdx::make_function(Reflect::require(), + std::move(argv), + true /*is_noexcept*/); + } + } /*namespace reflect*/ } /*namespace xo*/ diff --git a/include/xo/reflect/TypeDescr.hpp b/include/xo/reflect/TypeDescr.hpp index c50a6d8d..54013654 100644 --- a/include/xo/reflect/TypeDescr.hpp +++ b/include/xo/reflect/TypeDescr.hpp @@ -226,6 +226,7 @@ namespace xo { */ TypeDescr fn_retval() const { return this->tdextra_->fn_retval(); } TypeDescr fn_arg(uint32_t i) const { return this->tdextra_->fn_arg(i); } + bool fn_is_noexcept() const { return this->tdextra_->fn_is_noexcept(); } void display(std::ostream & os) const; std::string display_string() const; diff --git a/include/xo/reflect/TypeDescrExtra.hpp b/include/xo/reflect/TypeDescrExtra.hpp index 54db3025..c16257b3 100644 --- a/include/xo/reflect/TypeDescrExtra.hpp +++ b/include/xo/reflect/TypeDescrExtra.hpp @@ -67,9 +67,10 @@ namespace xo { * * @pre @ref TypeDescrExtra::is_function() is true **/ - virtual uint32_t n_fn_arg() const { return 0; } virtual const TypeDescrBase * fn_retval() const { return nullptr; } + virtual uint32_t n_fn_arg() const { return 0; } virtual const TypeDescrBase * fn_arg(uint32_t /*i_arg*/) const { return nullptr; } + virtual bool fn_is_noexcept() const { return false; } }; /*TypeDescrExtra*/ } /*namespace reflect*/ } /*namespace xo*/ diff --git a/include/xo/reflect/function/FunctionTdx.hpp b/include/xo/reflect/function/FunctionTdx.hpp index 1558328d..9c5523a6 100644 --- a/include/xo/reflect/function/FunctionTdx.hpp +++ b/include/xo/reflect/function/FunctionTdx.hpp @@ -15,11 +15,15 @@ namespace xo { public: virtual ~FunctionTdx() = default; - /** create instance. Will be invoked exactly once for each reflected function type **/ + /** create instance. Will be invoked exactly once for each reflected function type + * + * @param retval_td. type description for return value + * @param arg_td_v. type descriptions for arguments, in positional order + * @param is_noexcept. true iff function marked noexcept + **/ static std::unique_ptr make_function(TypeDescr retval_td, - std::vector arg_td_v); - - + std::vector arg_td_v, + bool is_noexcept); // ----- Inherited from TypeDescrExtra ----- @@ -28,12 +32,14 @@ namespace xo { virtual TaggedPtr child_tp(uint32_t i, void * object) const override; const std::string & struct_member_name(uint32_t i) const override; - virtual uint32_t n_fn_arg() const override { return arg_td_v_.size(); } virtual TypeDescr fn_retval() const override { return retval_td_; } + virtual uint32_t n_fn_arg() const override { return arg_td_v_.size(); } virtual TypeDescr fn_arg(uint32_t i) const override { return arg_td_v_[i]; } + virtual bool fn_is_noexcept() const override { return is_noexcept_; } private: FunctionTdx(TypeDescr retval_td, + bool is_noexcept, std::vector arg_td_v); private: @@ -41,6 +47,8 @@ namespace xo { TypeDescr retval_td_; /** function arguments, in positional order **/ std::vector arg_td_v_; + /** true iff function promises never to throw **/ + bool is_noexcept_ = false; }; /*FunctionTdx*/ } /*namespace reflect*/ } /*namespace xo*/ diff --git a/src/reflect/function/FunctionTdx.cpp b/src/reflect/function/FunctionTdx.cpp index 1b715fcd..bfd43673 100644 --- a/src/reflect/function/FunctionTdx.cpp +++ b/src/reflect/function/FunctionTdx.cpp @@ -5,19 +5,28 @@ namespace xo { namespace reflect { - /** create instance. Will be invoked exactly once for each reflected function type **/ + /** create instance. Will be invoked exactly once for each reflected function type **/ std::unique_ptr FunctionTdx::make_function(TypeDescr retval_td, - std::vector arg_td_v) + std::vector arg_td_v, + bool is_noexcept) { - return std::unique_ptr(new FunctionTdx(retval_td, std::move(arg_td_v))); + return std::unique_ptr(new FunctionTdx(retval_td, + is_noexcept, + std::move(arg_td_v))); } FunctionTdx::FunctionTdx(TypeDescr retval_td, + bool is_noexcept, std::vector arg_td_v) : retval_td_{retval_td}, - arg_td_v_{std::move(arg_td_v)} - {} + arg_td_v_{std::move(arg_td_v)}, + is_noexcept_{is_noexcept} + { + if (!retval_td) { + throw std::runtime_error("FunctionTdx::ctor: null return type?"); + } + } TaggedPtr FunctionTdx::child_tp(uint32_t /*i*/, void * /*object*/) const