// (C) Copyright Tobias Schwinger // (C) Copyright 2016 (Modified Work) Barrett Adair // // Use modification and distribution are subject to the boost Software License, // Version 1.0. (See http://www.boost.org/LICENSE_1_0.txt). //[ interface_header #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace intrfc { namespace ct = callable_traits; //this is our placeholder parent class for member pointers struct secret {}; // used to forward a parameter without copying it template using forward_t = std::conditional_t< sizeof(void*) < sizeof(T) , std::add_lvalue_reference_t< std::add_const_t >, T>; // The member struct erases the object reference type that is supplied by // function_type, which aliases an INVOKE-aware function type when // a pmf is passed to it. We replace the first argument (which is // a reference to an object of the member ptr's parent class, as required // by INVOKE) with void*. template::value> struct member; // this is our type erasure "glue". member<...>::wrapper::wrap is a static member function which // casts a void* back to its original object type, and invokes the appropriate member. This first // definition handles member functions. template struct member { // qualified_parent_class_of yields a reference type which is qualified // according to the member function type. using context = std::remove_reference_t>; template struct member_wrapper { static inline decltype(auto) wrap(void* c, ::intrfc::forward_t... args) { return (reinterpret_cast(c)->*PmfValue)(args...); }; }; // removing the member pointer so that expand_args below doesn't include // the INVOKE-required object argument using abominable_function_type = ::intrfc::ct::remove_member_pointer_t; // expand_args is used to expand the argument types into member_wrapper using wrapper = ::intrfc::ct::expand_args_t< abominable_function_type, member_wrapper>; }; // This specialization handles member data. template struct member { using no_ref = std::remove_reference_t; static constexpr const bool is_const = std::is_const::value; using context = std::conditional_t; struct wrapper { static inline T& wrap(void* c) { return reinterpret_cast(c)->*PmdValue; }; }; }; } // BOOST_PP_NIL is not defined - the code below is simply a large comment #ifdef BOOST_PP_NIL DEFINE_INTERFACE(interface_x, ((print_member_data, void() const)) ((member_data, int)) ); // The above macro invocation would expand to the following code (sans comments and formatting, of course): // most of our implementation logic is dumped into this // class to reduce the chance of naming conflicts. We could // use a namespace instead, except that namespaces can't be // opened inside class definition. Anywhere you see "interface_x", // "print_member_data", and "member_data", remember that these // names originate from the macro parameters. struct interface_x_detail { // This will be our root base class, containing our vtable and void* // necessary for type erasure. struct interface_root { struct vtable { template struct member_info0 { // this comes from the from the macro parameters using member_type = void() const; // this definition is only used for member data template ::value> struct member_info { using result_type = std::add_lvalue_reference_t; using ptr_type = Member U::*; // we will use these later to correctly re-construct our interface function using qualifiers = decltype(::intrfc::ct::get_qualifier_flags()); // Our type erasure scheme will use a function to dereference // pointers to member data using type_erased_ptr = result_type(*)(void *); }; // this specialization is only used for member functions template struct member_info { using ptr_type = member_type U::*; using result_type = ::intrfc::ct::result_of_t; // The first argument of this function type is a qualified U // reference, because function_type is defined in terms of INVOKE using function_type = ::intrfc::ct::function_type_t; // we will use these later to correctly re-construct our forwarding // interface function using qualifiers = decltype(::intrfc::ct::get_member_qualifier_flags()); // overwriting the first argument with void*, "erasing" the // qualified U reference. We then make it a function pointer. using type_erased_ptr = ::intrfc::ct::replace_args_t<0, function_type, void*> *; }; // these aliases simply make later code easier to follow using info = member_info; using ptr_type = typename info::ptr_type; using result_type = typename info::result_type; using type_erased_ptr = typename info::type_erased_ptr; using qualifiers = typename info::qualifiers; }; // this alias saves us some typing later using pmf0 = typename member_info0<>::ptr_type; // This is our vtable function pointer entry, which will be initialized // at compile-time. typename member_info0<>::type_erased_ptr func0; // repeating for additional members supplied by the macro template struct member_info1 { // this comes from the from the macro parameters using member_type = int; template ()> struct member_info { using result_type = std::add_lvalue_reference_t; using ptr_type = Member U::*; using qualifiers = decltype(::intrfc::ct::get_qualifier_flags()); using type_erased_ptr = result_type(*)(void *); }; template struct member_info { using ptr_type = member_type U::*; using result_type = ::intrfc::ct::result_of_t; using function_type = ::intrfc::ct::function_type_t; using qualifiers = decltype(::intrfc::ct::get_member_qualifier_flags()); using type_erased_ptr = ::intrfc::ct::replace_args_t<0, function_type, void *> *; }; using info = member_info; using ptr_type = typename info::ptr_type; using result_type = typename info::result_type; using type_erased_ptr = typename info::type_erased_ptr; using qualifiers = typename info::qualifiers; }; using pmf1 = typename member_info1<>::ptr_type; typename member_info1<>::type_erased_ptr func1; }; //our two data members const vtable *ptr_vtable; void *obj_ptr; // conversion constructor that creates an interface from an arbitrary class template inline interface_root(T &that) : ptr_vtable(&vtable_holder::val_vtable), obj_ptr(std::addressof(that)) { } // This template is instantiated for every class from which this interface // is constructed (see constructor above). This instantiation causes the // compiler to emit an initialized vtable for this type (via aggregate // assignment later in the code). template struct vtable_holder { static const vtable val_vtable; }; }; // this definition will not be used, but is necessary for eager template instantiation template struct base { using type = Ignored; }; // We use get_next_base to chain together our base classes, starting with interface_root. // The rest of the classes each contain an interface member. template using get_next_base = std::conditional_t::type>; // base_impl0 defines the first member supplied by the macro, specializing on // the qualifiers that exist on the supplied member. The appropriate "apply" // specialization will serve as a base class for our interface. template struct base_impl0 { template < ::intrfc::ct::flags QualifierFlags, typename Base = get_next_base<0>> struct apply; // for unqualified member functions/data template struct apply< ::intrfc::ct::default_, Base> : Base { using Base::Base; using Base::ptr_vtable; using Base::obj_ptr; inline decltype(auto) print_member_data(::intrfc::forward_t... args) { return ptr_vtable->func0(obj_ptr, args...); } }; // for const-qualified member functions/data template struct apply<::intrfc::ct::const_, Base> : Base { using Base::Base; using Base::ptr_vtable; using Base::obj_ptr; inline decltype(auto) print_member_data(::intrfc::forward_t... args) const { return ptr_vtable->func0(obj_ptr, args...); } }; // for volatile-qualified member functions/data template struct apply<::intrfc::ct::volatile_, Base> : Base { using Base::Base; using Base::ptr_vtable; using Base::obj_ptr; inline decltype(auto) print_member_data(::intrfc::forward_t... args) volatile { return ptr_vtable->func0(obj_ptr, args...); } }; // for const-volatile-qualified member functions/data template struct apply<(::intrfc::ct::const_ | ::intrfc::ct::volatile_), Base> : Base { using Base::Base; using Base::ptr_vtable; using Base::obj_ptr; inline decltype(auto) print_member_data(::intrfc::forward_t... args) const volatile { return ptr_vtable->func0(obj_ptr, args...); } }; }; // this specialization helps link the bases together template struct base<0, Ignored> { using function_type = ::intrfc::ct::function_type_t< typename interface_root::vtable::pmf0>; using impl = ::intrfc::ct::expand_args_t< ::intrfc::ct::pop_front_args_t, base_impl0>; using qualifiers = typename interface_root::vtable::member_info0<>::qualifiers; using type = impl::template apply; }; // repeat for additional interface members template struct base_impl1 { template <::intrfc::ct::flags QualifierFlags, typename Base = get_next_base<1>> struct apply; template struct apply<::intrfc::ct::default_, Base> : Base { using Base::Base; using Base::ptr_vtable; using Base::obj_ptr; inline decltype(auto) member_data(::intrfc::forward_t... args) { return ptr_vtable->func1(obj_ptr, args...); } }; template struct apply<::intrfc::ct::const_, Base> : Base { using Base::Base; using Base::ptr_vtable; using Base::obj_ptr; inline decltype(auto) member_data(::intrfc::forward_t... args) const { return ptr_vtable->func1(obj_ptr, args...); } }; template struct apply<::intrfc::ct::volatile_, Base> : Base { using Base::Base; using Base::ptr_vtable; using Base::obj_ptr; inline decltype(auto) member_data(::intrfc::forward_t... args) volatile { return ptr_vtable->func1(obj_ptr, args...); } }; template struct apply<(::intrfc::ct::const_ | ::intrfc::ct::volatile_), Base> : Base { using Base::Base; using Base::ptr_vtable; using Base::obj_ptr; inline decltype(auto) member_data(::intrfc::forward_t... args) const volatile { return ptr_vtable->func1(obj_ptr, args...); } }; }; template struct base<1, Ignored> { using function_type = ::intrfc::ct::function_type_t< typename interface_root::vtable::pmf1>; using impl = ::intrfc::ct::expand_args_t< ::intrfc::ct::pop_front_args_t, base_impl1>; using qualifiers = typename interface_root::vtable::member_info1<>::qualifiers; using type = impl::template apply; }; }; // this initializes the vtable at compile time for every class used to construct an // interface_x object. Member functions and member data are both handled. template interface_x_detail::interface_root::vtable const interface_x_detail::interface_root::vtable_holder::val_vtable = { &::intrfc::member< typename vtable::member_info0::ptr_type, &T::print_member_data >::wrapper::wrap, &::intrfc::member< typename vtable::member_info1::ptr_type, &T::member_data >::wrapper::wrap }; // We inherit the base for the last member, which inherits all // the bases before it. This strategy keeps our runtime memory layout // as small as possible, but unfortunately increases compile times. struct interface_x : interface_x_detail::base<2 - 1>::type { using detail = interface_x_detail; using base = typename detail::base<2 - 1>::type; template>::value, int> = 0> inline interface_x(T &that) : base(that) {} inline interface_x(const interface_x &) = default; // These using declarations assist with IDE code-completion using detail::base<0>::type::print_member_data; using detail::base<1>::type::member_data; }; #endif // the interface definition on the client's side #define DEFINE_INTERFACE(name,def) \ struct BOOST_PP_CAT(name, _detail) { \ \ struct interface_root { \ \ struct vtable { \ \ CALLABLE_TRAITS_INTERFACE__MEMBERS(def, VTABLE) \ }; \ \ const vtable * ptr_vtable; \ void* obj_ptr; \ \ template \ inline interface_root(T& that) \ : ptr_vtable(&vtable_holder::val_vtable), \ obj_ptr(std::addressof(that)) {} \ \ template \ struct vtable_holder { \ static const vtable val_vtable; \ }; \ }; \ \ template \ struct base{ \ using type = Ignored; \ }; \ \ template \ using get_next_base = std::conditional_t< \ I == 0, interface_root, typename base::type>; \ \ CALLABLE_TRAITS_INTERFACE__MEMBERS(def, BASES) \ }; \ \ template \ BOOST_PP_CAT(name, _detail)::interface_root::vtable const \ BOOST_PP_CAT(name, _detail)::interface_root::vtable_holder \ ::val_vtable = \ { CALLABLE_TRAITS_INTERFACE__MEMBERS(def, INIT_VTABLE) }; \ \ struct name : BOOST_PP_CAT(name, _detail) \ ::base::type { \ \ using detail = BOOST_PP_CAT(name, _detail); \ using base = \ typename detail::base::type; \ \ template>::value, int> = 0> \ inline name(T& that) : base(that) {} \ \ inline name(const name &) = default; \ \ CALLABLE_TRAITS_INTERFACE__MEMBERS(def, USING_DECLARATIONS) \ } \ /**/ // preprocessing code details // iterate all of the interface's members and invoke a macro, prefixed // with CALLABLE_TRAITS_INTERFACE__ #define CALLABLE_TRAITS_INTERFACE__MEMBERS(seq,macro) \ BOOST_PP_REPEAT(BOOST_PP_SEQ_SIZE(seq), \ CALLABLE_TRAITS_INTERFACE__ ## macro, seq) \ /**/ // generate the vtable initilizer code #define CALLABLE_TRAITS_INTERFACE__INIT_VTABLE(z,i,seq) \ CALLABLE_TRAITS_INTERFACE__INIT_VTABLE_I(i, \ BOOST_PP_TUPLE_ELEM(2,0,BOOST_PP_SEQ_ELEM(i,seq))) \ /**/ #define CALLABLE_TRAITS_INTERFACE__INIT_VTABLE_I(i,mem) \ BOOST_PP_COMMA_IF(i) \ &::intrfc::member< \ typename vtable::BOOST_PP_CAT(member_info, i)::ptr_type,\ &T::mem \ >::wrapper::wrap \ /**/ //generate the vtable #define CALLABLE_TRAITS_INTERFACE__VTABLE(z,i,seq) \ CALLABLE_TRAITS_INTERFACE__VTABLE_I(z,i, \ BOOST_PP_TUPLE_ELEM(2,1,BOOST_PP_SEQ_ELEM(i,seq))) \ /**/ #define CALLABLE_TRAITS_INTERFACE__VTABLE_I(z,i,signature) \ template \ struct BOOST_PP_CAT(member_info, i) { \ \ using member_type = signature; \ \ template()> \ struct member_info { \ \ using result_type = std::add_lvalue_reference_t; \ using ptr_type = Member U::*; \ \ using qualifiers = \ decltype( ::intrfc::ct::get_qualifier_flags()); \ \ using type_erased_ptr = result_type(*)(void*); \ }; \ \ template \ struct member_info { \ \ using ptr_type = member_type U::*; \ using result_type = ::intrfc::ct::result_of_t; \ using function_type = ::intrfc::ct::function_type_t; \ \ using qualifiers = decltype( \ ::intrfc::ct::get_member_qualifier_flags()); \ \ using type_erased_ptr = \ ::intrfc::ct::replace_args_t<0, function_type, void*> *; \ }; \ \ using info = member_info; \ using ptr_type = typename info::ptr_type; \ using result_type = typename info::result_type; \ using type_erased_ptr = typename info::type_erased_ptr; \ using qualifiers = typename info::qualifiers; \ }; \ \ using BOOST_PP_CAT(pmf, i) = \ typename BOOST_PP_CAT(member_info, i)<>::ptr_type; \ \ typename BOOST_PP_CAT(member_info, i)<> \ ::type_erased_ptr BOOST_PP_CAT(func, i); \ /**/ // generate the bases, each of which will contain a public-facing // interface function #define CALLABLE_TRAITS_INTERFACE__BASES(z,i,seq) \ CALLABLE_TRAITS_INTERFACE__BASES_I(i, \ BOOST_PP_TUPLE_ELEM(2,0,BOOST_PP_SEQ_ELEM(i,seq))) \ /**/ #define CALLABLE_TRAITS_INTERFACE__BASES_I(i, mem) \ template \ struct BOOST_PP_CAT(base_impl, i) { \ \ template<::intrfc::ct::flags QualifierFlags, \ typename Base = get_next_base> \ struct apply; \ \ CALLABLE_TRAITS_INTERFACE__BASES_APPLY(i, mem, \ ::intrfc::ct::default_, BOOST_PP_EMPTY()) \ \ CALLABLE_TRAITS_INTERFACE__BASES_APPLY(i, mem, \ ::intrfc::ct::const_, const) \ \ CALLABLE_TRAITS_INTERFACE__BASES_APPLY(i, mem, \ ::intrfc::ct::volatile_, volatile) \ \ CALLABLE_TRAITS_INTERFACE__BASES_APPLY(i, mem, \ (::intrfc::ct::const_ | ::intrfc::ct::volatile_), const volatile)\ }; \ \ template \ struct base { \ \ using function_type = ::intrfc::ct::function_type_t< \ typename interface_root::vtable::BOOST_PP_CAT(pmf, i)>; \ \ using impl = ::intrfc::ct::expand_args_t< \ ::intrfc::ct::pop_front_args_t, \ BOOST_PP_CAT(base_impl, i)>; \ \ using qualifiers = \ typename interface_root::vtable::BOOST_PP_CAT(member_info, i)<>\ ::qualifiers; \ \ using type = impl::template apply; \ }; \ /**/ // generate specializations based on qualifiers on member functions/data. // lvalue and rvalue member functions are ignored. #define CALLABLE_TRAITS_INTERFACE__BASES_APPLY(i, mem, flags, qualifiers) \ template \ struct apply : Base { \ \ using Base::Base; \ using Base::ptr_vtable; \ using Base::obj_ptr; \ \ inline decltype(auto) \ mem(::intrfc::forward_t... args) qualifiers { \ return ptr_vtable->BOOST_PP_CAT(func, i)(obj_ptr, args...);\ } \ }; \ /**/ #define CALLABLE_TRAITS_INTERFACE__USING_DECLARATIONS(z,i,seq) \ using detail::base::type:: \ BOOST_PP_TUPLE_ELEM(2,0,BOOST_PP_SEQ_ELEM(i,seq)); \ /**/ //]