#pragma once #include #include #include #include #include #include #include "self_reflection_tag.h" #include "reflection_traits_info.h" #include "lib_begin.h" /// the cgv namespace namespace cgv { /// in this namespace reflection of types is implemented namespace reflect { /** abstract interface to call a method of a given instance. For simplicity all parameters are passed as const pointers. In case the parameter should also be written by the method, a const_cast needs to be used. */ struct method_interface { virtual void call_void(void* instance, const std::vector& param_value_traits, const std::vector& param_value_ptrs, const std::vector& param_type_names, const abst_reflection_traits* result_traits = 0, void* result_value_ptr = 0, const std::string& result_type = "") = 0; }; template struct method_parameter_traits_helper { static void fill_vector(std::vector& param_value_traits) { param_value_traits.push_back(reflection_traits_info::type>::traits_type().clone()); if (i+1 < n) method_parameter_traits_helper::fill_vector(param_value_traits); } }; template struct method_parameter_traits_helper { static void fill_vector(std::vector& param_value_traits) { } }; /// forward declaration of method_interface_impl which is implemented in , include the generated header template struct method_interface_impl; /** the self reflection handler is passed to the virtual self_reflect() method of cgv::base::base. It is used to process type information by describing member variables and function (methods) to the handler with its templated methods reflect_member() and reflect_method() or the non templated versions with the suffix _void. */ class CGV_API reflection_handler { #ifndef REFLECT_TRAITS_WITH_DECLTYPE template bool reflect_base_impl(B& base_ref, const RB&); template bool reflect_const_array_impl(const std::string& member_name, T (&member_ref)[n], const RT&); template bool reflect_vector_impl(const std::string& member_name, std::vector& member_ref, const RT&); #endif public: template friend bool reflect_enum(reflection_handler& rh, const std::string& name, T& instance, const std::string& declarations); template friend bool reflect_string(reflection_handler& rh, const std::string& name, T& instance); friend struct detail; /**@ basic types with helper functions */ //@{ /// different values for group traversal strategy enum GroupTraversal { GT_TERMINATE = -3, GT_SKIP = -2, GT_COMPLETE = -1 }; /// return the group traversals as a string static std::string group_traversal_name(GroupTraversal gt); /// different support group types enum GroupKind { GK_NO_GROUP, GK_BASE_CLASS, GK_STRUCTURE, GK_VECTOR, GK_ARRAY, GK_POINTER }; /// return the group kind as a string static const char* group_kind_name(GroupKind gk); /// check whether a group kind is of array or vector kind static bool is_array_kind(GroupKind gk); //@} protected: /**@name internal helper functions*/ //@{ /// type independent part of the reflect_group method that starts the group traversal GroupTraversal process_structural_group_begin(GroupKind gk, const std::string& member_name, GroupTraversal gt); /// updates the nesting info at the end of group and always returns true bool group_end(GroupKind gk); /// implementation of reflection with internal or external self_reflect function template bool self_reflect_member(const std::string& member_name, T& member_ref, const RT&, const D&, bool hard_cast) { RT rt; switch (process_structural_group_begin(GK_STRUCTURE, member_name, GroupTraversal(reflect_group_begin(GK_STRUCTURE, member_name, &member_ref, &rt)))) { case GT_TERMINATE : return false; case GT_COMPLETE : if (hard_cast) return static_cast(member_ref).D::self_reflect(*this) && group_end(GK_STRUCTURE); else { D& d = static_cast(member_ref); return d.self_reflect(*this) && group_end(GK_STRUCTURE); } default: return true; } } /// type independent functionality of array reflection int reflect_array_begin(GroupKind group_kind, const std::string& group_name, void* group_ptr, abst_reflection_traits* rt, unsigned grp_size); //@} /**@name management of nesting information*/ //@{ /// for each nesting during traversal a nesting info is pushed back to the end of the nesting info stack struct nesting_info { /// group kind of nesting level GroupKind group_kind; /// pointer to type name for base class groups and member name otherwise const std::string* name; /// current element index for vector and array groups unsigned idx; /// nesting_info(GroupKind _group_kind = GK_NO_GROUP, const std::string* _name = 0, unsigned _idx = 0) : group_kind(_group_kind), name(_name), idx(_idx) {}; }; /// stack of nesting_info used during the reflection process std::vector nesting_info_stack; //@} /**@name interface to be implemented by derived reflection handlers*/ //@{ //! abstract interface to start reflection of a group of members. /*! The return value gives information about further traversal of the group: - GT_TERMINATE ... terminate traversal completely - GT_SKIP ... skip traversal of group - GT_COMPLETE ... traverse group completely - index i >= 0 ... traverse only the i-th member of the group. Depending on the group kind, the remaining parameters are given as follows: - GK_BASE_CLASS ... group_name and grp_size are undefined, group_ptr is pointer to instance of base class, rt reflects base class - GK_STRUCTURE ... group_name is instance name, group_ptr is pointer to instance, rt reflects type of structure, grp_size is undefined, - GK_VECTOR ... group_name is vector name, group_ptr is pointer to vector data structure, rt reflects element type, grp_size is size of vector - GK_ARRAY ... group_name is array name, group_ptr is pointer to first array element, rt reflects element type, grp_size is size of array. - GK_POINTER ... group_name is pointer name, group_ptr is pointer to first array element, rt reflects type to which pointer poits, grp_size is undefined. */ virtual int reflect_group_begin(GroupKind group_kind, const std::string& group_name, void* group_ptr, abst_reflection_traits* rt, unsigned grp_size = -1); //! abstract interface to terminate reflection of a group of members /*! This function should only be called if group is traversed completely. */ virtual void reflect_group_end(GroupKind group_kind); /** abstract interface to reflect a member variable, where the member type is specified as a string. Returns whether to continue the reflection. */ virtual bool reflect_member_void(const std::string& member_name, void* member_ptr, abst_reflection_traits* rt) = 0; /** abstract interface to reflect a method, where return and parameter types are specified as strings. Returns whether to continue the reflection. */ virtual bool reflect_method_void(const std::string& method_name, method_interface* mi_ptr, abst_reflection_traits* return_traits, const std::vector& param_value_traits) = 0; //@} public: /// declare virtual destructor virtual ~reflection_handler(); /**@name interface used by reflect and self_reflect functions*/ //@{ /// give information on whether reflection_handler creates object (defaults to false) virtual bool is_creative() const; /** call this to reflect a member by member name and reference to the member. The member type is deduced from the reference via templates. The method uses reflect_member_impl to dispath types with implementation of a self_reflect method and types without. For polymorphic objects with a polymorphic self_reflect() method the parameter \c hard_cast steers whether the concrete implementation T::self_reflect() is used or the overloaded function member_ref.self_reflect(). This is important for self reflection of base classes in polymorphic objects where hard_cast is set to true. In most other cases one can use the default argument false. */ template bool reflect_member(const std::string& member_name, T& member_ref, bool hard_cast = false); /** call this to reflect a method by method name and reference to the member. The method type is deduced from the reference via templates. This only works, if you additionally include the header , where the template code is located. This header is not included automatically, because of its length. To give names to the parameters, append them to the name enclosed in parenthesis, i.e. "sin(x)" or "dot_prod(p,q)". */ template bool reflect_method(const std::string& method_name, M m); /// reflect a base class with its members template bool reflect_base(B& base_ref); /// reflect a member of constant size array type template bool reflect_member(const std::string& member_name, T (&member_ref)[n]); /// reflect a member of vector type template bool reflect_member(const std::string& member_name, std::vector& member_ref); //! reflect a member of pointer type. /*! This is only a minimal implementation that allows pointers to memory allocated with new. No reference counting or pointers into memory ranges allocated differently are supported. This will be later on. */ template bool reflect_member(const std::string& member_name, T*& member_ref); /// reflect a dynamic array member of vector type template bool reflect_array(const std::string& member_name, T*& member_ref, S& size); }; struct detail { #ifndef REFLECT_TRAITS_WITH_DECLTYPE template struct reflect_member_impl { template struct reflect_impl { static bool reflect(reflection_handler* rh, const std::string& member_name, T& member_ref, const RT&, bool hard_cast) { RT rt; return rh->reflect_member_void(member_name, &member_ref, &rt); } }; template struct reflect_impl { static bool reflect(reflection_handler* rh, const std::string& member_name, T& member_ref, const RT&, bool hard_cast) { RT rt; return rh->self_reflect_member(member_name, member_ref, rt, static_cast(member_ref), hard_cast); } }; template static bool reflect_RT(reflection_handler* rh, const std::string& member_name, T& member_ref, const RT& rt, bool hard_cast) { return reflect_impl::reflect(rh, member_name, member_ref, rt, hard_cast); } static bool reflect(reflection_handler* rh, const std::string& member_name, T& member_ref, bool hard_cast) { return reflect_RT(rh, member_name, member_ref, get_reflection_traits(member_ref), hard_cast); } }; template struct reflect_member_impl { static bool reflect(reflection_handler* rh, const std::string& member_name, T& member_ref, bool hard_cast) { typename reflection_traits_info::traits_type rt; return rh->reflect_member_void(member_name, &member_ref, &rt); } }; #else template struct reflect_member_impl { static bool reflect(reflection_handler* rh, const std::string& member_name, T& member_ref, bool hard_cast) { reflection_traits_info::traits_type rt; return rh->reflect_member_void(member_name, &member_ref, &rt); } }; template struct reflect_member_impl { static bool reflect(reflection_handler* rh, const std::string& member_name, T& member_ref, bool hard_cast) { reflection_traits_info::traits_type rt; return rh->self_reflect_member(member_name, member_ref, rt, static_cast::traits_type::external_self_reflect_type&>(member_ref), hard_cast); } }; #endif template struct reflect_member_impl { static bool reflect(reflection_handler* rh, const std::string& member_name, T& member_ref, bool hard_cast) { typename reflection_traits_info::traits_type rt; return rh->self_reflect_member(member_name, member_ref, rt, member_ref, hard_cast); } }; template struct reflect_method_impl { static bool reflect(reflection_handler* rh, const std::string& method_name, M m) { static std::vector param_value_traits; static method_interface_impl mi(m); if (param_value_traits.size() != cgv::type::traits::method_pointer::nr_arguments) method_parameter_traits_helper<0,cgv::type::traits::method_pointer::nr_arguments,M>::fill_vector(param_value_traits); typename reflection_traits_info::traits_type rt; return rh->reflect_method_void(method_name, &mi, &rt, param_value_traits); } }; template struct reflect_method_impl { static bool reflect(reflection_handler* rh, const std::string& method_name, M m) { static std::vector param_value_traits; static method_interface_impl mi(m); if (param_value_traits.size() != cgv::type::traits::method_pointer::nr_arguments) method_parameter_traits_helper<0,cgv::type::traits::method_pointer::nr_arguments,M>::fill_vector(param_value_traits); return rh->reflect_method_void(method_name, &mi, 0, param_value_traits); } }; #ifndef REFLECT_TRAITS_WITH_DECLTYPE template struct reflect_base_dispatch { static bool reflect(reflection_handler* rh, B& base_ref) { return rh->reflect_base_impl(base_ref, typename reflection_traits_info::traits_type()); } }; template struct reflect_base_dispatch { static bool reflect(reflection_handler* rh, B& base_ref) { return rh->reflect_base_impl(base_ref, get_reflection_traits(base_ref)); } }; template struct reflect_const_array_dispatch { static bool reflect(reflection_handler* rh, const std::string& member_name, T (&member_ref)[n]) { return rh->reflect_const_array_impl(member_name, member_ref, typename reflection_traits_info::traits_type()); } }; template struct reflect_const_array_dispatch { static bool reflect(reflection_handler* rh, const std::string& member_name, T (&member_ref)[n]) { return rh->reflect_const_array_impl(member_name, member_ref, get_reflection_traits(T())); } }; template struct reflect_vector_dispatch { static bool reflect(reflection_handler* rh, const std::string& member_name, std::vector& member_ref) { return rh->reflect_vector_impl(member_name, member_ref, typename reflection_traits_info::traits_type()); } }; template struct reflect_vector_dispatch { static bool reflect(reflection_handler* rh, const std::string& member_name, std::vector& member_ref) { return rh->reflect_vector_impl(member_name, member_ref, get_reflection_traits(T())); } }; #endif }; template bool reflection_handler::reflect_member(const std::string& member_name, T& member_ref, bool hard_cast) { #ifdef REFLECT_TRAITS_WITH_DECLTYPE return detail::reflect_member_impl::kind>::reflect(this, member_name, member_ref, hard_cast); #else return detail::reflect_member_impl::kind>::reflect(this, member_name, member_ref, hard_cast); #endif } template bool reflection_handler::reflect_method(const std::string& method_name, M m) { return detail::reflect_method_impl::return_type>::reflect(this, method_name, m); } #ifdef REFLECT_TRAITS_WITH_DECLTYPE template bool reflection_handler::reflect_base(B& base_ref) { reflection_traits_info::traits_type rt; #else template bool reflection_handler::reflect_base_impl(B& base_ref, const RB&) { RB rt; #endif switch (process_structural_group_begin(GK_BASE_CLASS, "", GroupTraversal(reflect_group_begin(GK_BASE_CLASS, "", &base_ref, &rt)))) { case GT_TERMINATE : return false; case GT_COMPLETE : return reflect_member("", base_ref, true) && group_end(GK_BASE_CLASS); default: return true; } } #ifndef REFLECT_TRAITS_WITH_DECLTYPE template bool reflection_handler::reflect_base(B& base_ref) { return detail::reflect_base_dispatch::use_get,B>::reflect(this,base_ref); } #endif #ifdef REFLECT_TRAITS_WITH_DECLTYPE template bool reflection_handler::reflect_member(const std::string& member_name, T (&member_ref)[n]) { reflection_traits_info::traits_type rt; #else template bool reflection_handler::reflect_const_array_impl(const std::string& member_name, T (&member_ref)[n], const RT&) { RT rt; #endif int grp_tra = reflect_array_begin(GK_ARRAY, member_name, member_ref, &rt, n); if (grp_tra == GT_TERMINATE || grp_tra == GT_SKIP) return grp_tra == GT_SKIP; bool res = true; if (grp_tra == GT_COMPLETE) { for (nesting_info_stack.back().idx=0; res && nesting_info_stack.back().idx bool reflection_handler::reflect_member(const std::string& member_name, T (&member_ref)[n]) { return detail::reflect_const_array_dispatch::use_get,T,n>::reflect(this,member_name,member_ref); } #endif #ifdef REFLECT_TRAITS_WITH_DECLTYPE template bool reflection_handler::reflect_member(const std::string& member_name, std::vector& member_ref) { reflection_traits_info::traits_type rt; #else template bool reflection_handler::reflect_vector_impl(const std::string& member_name, std::vector& member_ref, const RT&) { RT rt; #endif int grp_tra = reflect_array_begin(GK_VECTOR, member_name, &member_ref, &rt, member_ref.size()); if (grp_tra == GT_TERMINATE || grp_tra == GT_SKIP) return grp_tra == GT_SKIP; bool res = true; if (grp_tra == GT_COMPLETE) { unsigned size = member_ref.size(); res = reflect_member("size", size); if (member_ref.size() != size) member_ref.resize(size); for (nesting_info_stack.back().idx=0; res && nesting_info_stack.back().idx bool reflection_handler::reflect_member(const std::string& member_name, std::vector& member_ref) { return detail::reflect_vector_dispatch::use_get,T>::reflect(this, member_name, member_ref); } #endif //! reflect a member of pointer type. /*! This is only a minimal implementation that allows pointers to memory allocated with new. No reference counting or pointers into memory ranges allocated differently are supported. This will be later on. */ template bool reflection_handler::reflect_member(const std::string& member_name, T*& member_ref) { typename reflection_traits_info::traits_type rt; GroupTraversal gt = reflect_group_begin(GK_POINTER, member_name, &member_ref, &rt); switch (gt) { case GT_TERMINATE : return false; case GT_SKIP : return true; case GT_COMPLETE : break; default: std::cerr << "group traversal " << group_traversal_name(gt) << " not allowed on pointer group " << member_name << "!" << std::endl; return false; } nesting_info_stack.push_back(nesting_info(GK_POINTER, &member_name)); unsigned pointer_type = (member_ref == 0 ? 0 : 1); unsigned pointer_type_tmp = pointer_type; bool res = reflect_member("pointer_type", pointer_type_tmp); if (res) { if (pointer_type_tmp != pointer_type) { if (pointer_type != 0) delete member_ref; if (pointer_type_tmp == 0) member_ref = 0; else member_ref = new T(); } if (pointer_type_tmp != 0) res = reflect_member(*member_ref); } group_end(GK_POINTER); return res; } /// reflect a dynamic array member of vector type template bool reflection_handler::reflect_array(const std::string& member_name, T*& member_ref, S& size) { typename reflection_traits_info::traits_type rt; int grp_tra = reflect_array_begin(GK_ARRAY, member_name, member_ref, &rt, size); if (grp_tra == GT_TERMINATE || grp_tra == GT_SKIP) return grp_tra == GT_SKIP; bool res = true; if (grp_tra == GT_COMPLETE) { unsigned tmp_size = size; res = reflect_member("size", tmp_size); if (size != tmp_size) { T* tmp = member_ref; member_ref = new T[tmp_size]; if (tmp) { for (unsigned i=0; i