#pragma once #include #include #include #include // ms visual c++ (as of 2013) doesn't accept the standard syntax for calling a // templated member function (foo->template bar();) #ifdef _MSC_VER # define FC_CALL_MEMBER_TEMPLATE_KEYWORD #else # define FC_CALL_MEMBER_TEMPLATE_KEYWORD template #endif namespace fc { namespace detail { /// This metafunction determines whether its template argument is an instantiation of fc::optional template struct is_optional : public std::false_type {}; template struct is_optional> : public std::true_type {}; /// This metafunction determines whether all of its template arguments are instantiations of fc::optional template struct all_optionals; template<> struct all_optionals<> : public std::true_type {}; template struct all_optionals : public std::false_type {}; template struct all_optionals, Ts...> : public all_optionals {}; /// A wrapper of std::function allowing callers to omit the last several arguments if those arguments are /// fc::optional types. i.e. given a function taking (int, double, bool, fc::optional, fc::optional), /// whereas normally the last two arguments must be provided, this template allows them to be omitted. /// Note that this only applies to trailing optional arguments, i.e. given a callable taking /// (fc::optional, int, fc::optional), only the last argument can be omitted. /// /// A discussion of how exactly this works is available here: /// https://github.com/bitshares/bitshares-fc/pull/126#issuecomment-490566060 template struct optionals_callable : public std::function { using std::function::operator(); template struct short_pack {}; /// This metafunction removes the first several types from a variadic parameter pack of types. /// The first parameter is the number of arguments to remove from the beginning of the pack. /// All subsequent parameters are types in the list to be cut /// The result pack_cutter<...>::type is a short_pack template struct pack_cutter; template struct pack_cutter_impl; template struct pack_cutter_impl<0, void, Types...> { static_assert(all_optionals::value, "All omitted arguments must correspond to optional parameters."); using type = short_pack; }; template struct pack_cutter_impl, T, Types...> : public pack_cutter_impl {}; template struct pack_cutter : public pack_cutter_impl {}; template using pack_cutter_t = typename pack_cutter::type; template R call_function(F&& f, short_pack) { return f(OptionalTypes()...); } /// Overload the function call operator, enabled if the caller provides fewer arguments than there are parameters. /// Pads out the provided arguments with default-constructed optionals, checking that they are indeed optional types template std::enable_if_t operator()(Args... args) { // Partially apply with the arguments provided auto partial_function = [this, &args...](auto&&... rest) { return (*this)(std::forward(args)..., std::move(rest)...); }; // Cut the provided arguments' types out of the Parameters list, and store the rest in a dummy type pack_cutter_t...> dummy; // Pass the partially applied function and the dummy type to another function which can deduce the optional // types and call the function with default instantiations of those types return call_function(std::move(partial_function), dummy); } }; } // This is no longer used and probably no longer can be used without generalizing the infrastructure around it, but I // kept it because it is informative. // struct identity_member { // template // static std::function functor( P&& p, R (C::*mem_func)(Args...) ); // template // static std::function functor( P&& p, R (C::*mem_func)(Args...)const ); // }; /// Used as the Transform template parameter for APIs, this type has two main purposes: first, it reads the argument /// list and return type of a method into template parameters; and second, it uses those types in conjunction with the /// optionals_callable template above to create a function pointer which supports optional arguments. struct identity_member_with_optionals { template static detail::optionals_callable functor( P&& p, R (C::*mem_func)(Args...) ); template static detail::optionals_callable functor( P&& p, R (C::*mem_func)(Args...)const ); }; template< typename Interface, typename Transform > struct vtable : public std::enable_shared_from_this> { private: vtable(); }; template struct vtable_copy_visitor { typedef OtherType other_type; vtable_copy_visitor( OtherType& s):_source( s ){} template void operator()( const char* name, std::function& memb, MemberPtr m )const { OtherType* src = &_source; memb = [src,m]( Args... args ){ return (src->*m)(args...); }; } OtherType& _source; }; template class api; class api_connection; typedef uint32_t api_id_type; class api_base { public: api_base() {} virtual ~api_base() {} virtual uint64_t get_handle()const = 0; virtual api_id_type register_api( api_connection& conn )const = 0; // defined in api_connection.hpp template< typename T > api as(); }; typedef std::shared_ptr< api_base > api_ptr; class api_connection; template class api : public api_base { public: typedef vtable vtable_type; api():_vtable( std::make_shared() ) {} /** T is anything with pointer semantics */ template api( const T& p ) :_vtable( std::make_shared() ) { _data = std::make_shared(p); T& ptr = boost::any_cast(*_data); auto& pointed_at = *ptr; typedef typename std::remove_reference::type source_vtable_type; _vtable->FC_CALL_MEMBER_TEMPLATE_KEYWORD visit_other( vtable_copy_visitor(pointed_at) ); } api( const api& cpy ):_vtable(cpy._vtable),_data(cpy._data) {} virtual ~api() {} friend bool operator == ( const api& a, const api& b ) { return a._data == b._data && a._vtable == b._vtable; } friend bool operator != ( const api& a, const api& b ) { return !(a._data == b._data && a._vtable == b._vtable); } virtual uint64_t get_handle()const override { return uint64_t(_data.get()); } virtual api_id_type register_api( api_connection& conn )const override; // defined in api_connection.hpp vtable_type& operator*()const { FC_ASSERT( _vtable ); return *_vtable; } vtable_type* operator->()const { FC_ASSERT( _vtable ); return _vtable.get(); } protected: std::shared_ptr _vtable; std::shared_ptr _data; }; } // namespace fc #include #include #include #include #include #include #include #define FC_API_VTABLE_DEFINE_MEMBER( r, data, elem ) \ decltype(Transform::functor( (data*)nullptr, &data::elem)) elem; #define FC_API_VTABLE_DEFINE_VISIT_OTHER( r, data, elem ) \ { typedef typename Visitor::other_type OtherType; \ v( BOOST_PP_STRINGIZE(elem), elem, &OtherType::elem ); } #define FC_API_VTABLE_DEFINE_VISIT( r, data, elem ) \ v( BOOST_PP_STRINGIZE(elem), elem ); #define FC_API( CLASS, METHODS ) \ namespace fc { \ template \ struct vtable : public std::enable_shared_from_this> { \ BOOST_PP_SEQ_FOR_EACH( FC_API_VTABLE_DEFINE_MEMBER, CLASS, METHODS ) \ template \ void visit_other( Visitor&& v ){ \ BOOST_PP_SEQ_FOR_EACH( FC_API_VTABLE_DEFINE_VISIT_OTHER, CLASS, METHODS ) \ } \ template \ void visit( Visitor&& v ){ \ BOOST_PP_SEQ_FOR_EACH( FC_API_VTABLE_DEFINE_VISIT, CLASS, METHODS ) \ } \ }; \ }