#pragma once #include #include #include #include #include #include #include #include #include #include //#include namespace fc { class binary_api_connection; namespace detail { template class callback_functor { public: typedef typename std::function::result_type result_type; callback_functor( std::weak_ptr< fc::binary_api_connection > con, uint64_t id ) :_callback_id(id),_binary_api_connection(con){} template result_type operator()( Args... args )const; private: uint64_t _callback_id; std::weak_ptr< fc::binary_api_connection > _binary_api_connection; }; template std::function bind_first_arg( const std::function& f, Arg0 a0 ) { return [=]( Args... args ) { return f( a0, args... ); }; } template R call_generic( const std::function& f, variants::const_iterator a0, variants::const_iterator e ) { return f(); } template R call_generic( const std::function& f, variants::const_iterator a0, variants::const_iterator e ) { FC_ASSERT( a0 != e ); return call_generic( bind_first_arg( f, a0->as< typename std::decay::type >() ), a0+1, e ); } template std::function to_generic( const std::function& f ) { return [=]( const variants& args ) { return variant( call_generic( f, args.begin(), args.end() ) ); }; } template std::function to_generic( const std::function& f ) { return [=]( const variants& args ) { call_generic( f, args.begin(), args.end() ); return variant(); }; } /** * If api is returned from a remote method, the API is eagerly bound to api of * the correct type in api_visitor::from_variant(). This binding [1] needs a reference * to the binary_api_connection, which is made available to from_variant() as a parameter. * * However, in the case of a remote method which returns api_base which can subsequently * be cast by the caller with as, we need to keep track of the connection because * the binding is done later (when the client code actually calls as). * * [1] The binding actually happens in get_remote_api(). */ class any_api : public api_base { public: any_api( api_id_type api_id, const std::shared_ptr& con ) : _api_id(api_id), _binary_api_connection(con) {} virtual uint64_t get_handle()const override { return _api_id; } virtual api_id_type register_api( binary_api_connection& conn )const override { FC_ASSERT( false ); return api_id_type(); } api_id_type _api_id; std::weak_ptr _binary_api_connection; }; } // namespace detail class generic_api { public: template generic_api( const Api& a, const std::shared_ptr& c ); generic_api( const generic_api& cpy ) = delete; vector call( const string& name, const vector& args ) { auto itr = _by_name.find(name); FC_ASSERT( itr != _by_name.end(), "no method with name '${name}'", ("name",name)("api",_by_name) ); return call( itr->second, args ); } vector call( uint32_t method_id, const vector& args ) { FC_ASSERT( method_id < _methods.size() ); return _methods[method_id](args); } std::weak_ptr< fc::binary_api_connection > get_connection() { return _binary_api_connection; } std::vector get_method_names()const { std::vector result; result.reserve( _by_name.size() ); for( auto& m : _by_name ) result.push_back(m.first); return result; } private: friend struct api_visitor; template std::function bind_first_arg( const std::function& f, Arg0 a0 )const { return [=]( Args... args ) { return f( a0, args... ); }; } template R call_generic( const std::function& f, datastream& ds )const { return f(); } template R call_generic( const std::function,Args...)>& f, datastream& ds ) { uint64_t callback_id = 0; fc::raw::unpack( ds, callback_id ); detail::callback_functor arg0( get_connection(), callback_id ); return call_generic( this->bind_first_arg,Args...>( f, std::function(arg0) ), ds ); } template R call_generic( const std::function&,Args...)>& f, fc::datastream& ds ) { uint64_t callback_id = 0; fc::raw::unpack( ds, callback_id ); detail::callback_functor arg0( get_connection(), callback_id ); return call_generic( this->bind_first_arg&,Args...>( f, arg0 ), ds ); } template R call_generic( const std::function& f, fc::datastream& ds ) { std::decay::type a0; fc::raw::unpack( ds, a0 ); return call_generic( this->bind_first_arg( f, a0 ), ds ); } struct api_visitor { api_visitor( generic_api& a, const std::weak_ptr& s ):api(a),_api_con(s){ } template std::function to_generic( const std::function(Args...)>& f )const; template std::function to_generic( const std::function>(Args...)>& f )const; template std::function to_generic( const std::function& f )const; template std::function to_generic( const std::function& f )const; template std::function to_generic( const std::function& f )const; template void operator()( const char* name, std::function& memb )const { api._methods.emplace_back( to_generic( memb ) ); api._by_name[name] = api._methods.size() - 1; } generic_api& api; const std::weak_ptr& _api_con; }; std::weak_ptr _binary_api_connection; fc::any _api; std::map< std::string, uint32_t > _by_name; std::vector< std::function(const vector&)> > _methods; }; // class generic_api class binary_api_connection : public std::enable_shared_from_this { public: typedef std::vector params_type; typedef std::vector result_type; binary_api_connection(){} virtual ~binary_api_connection(){}; template api get_remote_api( api_id_type api_id = 0 ) { api result; result->visit( api_visitor( api_id, this->shared_from_this() ) ); return result; } /** makes calls to the remote server */ virtual result_type send_call( api_id_type api_id, string method_name, params_type args = params_type() ) = 0; virtual result_type send_callback( uint64_t callback_id, params_type args = params_type() ) = 0; virtual void send_notice( uint64_t callback_id, params_type args = params_type() ) = 0; result_type receive_call( api_id_type api_id, const string& method_name, const params_type& args = params_type() )const { FC_ASSERT( _local_apis.size() > api_id ); return _local_apis[api_id]->call( method_name, args ); } result_type receive_callback( uint64_t callback_id, const params_type& args = params_type() )const { FC_ASSERT( _local_callbacks.size() > callback_id ); return _local_callbacks[callback_id]( args ); } void receive_notice( uint64_t callback_id, const params_type& args = params_type() )const { FC_ASSERT( _local_callbacks.size() > callback_id ); _local_callbacks[callback_id]( args ); } template api_id_type register_api( const Interface& a ) { auto handle = a.get_handle(); auto itr = _handle_to_id.find(handle); if( itr != _handle_to_id.end() ) return itr->second; _local_apis.push_back( std::unique_ptr( new generic_api(a, shared_from_this() ) ) ); _handle_to_id[handle] = _local_apis.size() - 1; return _local_apis.size() - 1; } template uint64_t register_callback( const std::function& cb ) { _local_callbacks.push_back( detail::to_generic( cb ) ); return _local_callbacks.size() - 1; } std::vector get_method_names( api_id_type local_api_id = 0 )const { return _local_apis[local_api_id]->get_method_names(); } fc::signal closed; private: std::vector< std::unique_ptr > _local_apis; std::map< uint64_t, api_id_type > _handle_to_id; std::vector< std::function > _local_callbacks; struct api_visitor { uint32_t _api_id; std::shared_ptr _connection; api_visitor( uint32_t api_id, std::shared_ptr con ) :_api_id(api_id),_connection(std::move(con)) { } api_visitor() = delete; template static Result from_vector( const vector& v, Result*, const std::shared_ptr& ) { return fc::raw::unpack( v ); } template static fc::api from_vector( const vector& v, fc::api* /*used for template deduction*/, const std::shared_ptr& con ) { return con->get_remote_api( fc::raw::unpack( v ) ); } static fc::api_ptr from_vector( const vector& v, fc::api_ptr* /* used for template deduction */, const std::shared_ptr& con ) { return fc::api_ptr( new detail::any_api( fc::raw::unpack(v), con ) ); } template static result_type convert_callbacks( const std::shared_ptr&, const T& v ) { return fc::raw::pack(v); } template static result_type convert_callbacks( const std::shared_ptr& con, const std::function& v ) { return con->register_callback( v ); } template void operator()( const char* name, std::function& memb )const { auto con = _connection; auto api_id = _api_id; memb = [con,api_id,name]( Args... args ) { auto var_result = con->send_call( api_id, name, { convert_callbacks(con,args)...} ); return from_vector( var_result, (Result*)nullptr, con ); }; } template void operator()( const char* name, std::function& memb )const { auto con = _connection; auto api_id = _api_id; memb = [con,api_id,name]( Args... args ) { con->send_call( api_id, name, { convert_callbacks(con,args)...} ); }; } }; }; class local_binary_api_connection : public binary_api_connection { public: /** makes calls to the remote server */ virtual result_type send_call( api_id_type api_id, string method_name, params_type args = params_type() ) override { FC_ASSERT( _remote_connection ); return _remote_connection->receive_call( api_id, method_name, std::move(args) ); } virtual result_type send_callback( uint64_t callback_id, params_type args = params_type() ) override { FC_ASSERT( _remote_connection ); return _remote_connection->receive_callback( callback_id, args ); } virtual void send_notice( uint64_t callback_id, params_type args = params_type() ) override { FC_ASSERT( _remote_connection ); _remote_connection->receive_notice( callback_id, args ); } void set_remote_connection( const std::shared_ptr& rc ) { FC_ASSERT( !_remote_connection ); FC_ASSERT( rc != this->shared_from_this() ); _remote_connection = rc; } const std::shared_ptr& remote_connection()const { return _remote_connection; } std::shared_ptr _remote_connection; }; template generic_api::generic_api( const Api& a, const std::shared_ptr& c ) :_binary_api_connection(c),_api(a) { boost::any_cast(a)->visit( api_visitor( *this, c ) ); } template std::function generic_api::api_visitor::to_generic( const std::function(Args...)>& f )const { auto api_con = _api_con; auto gapi = &api; return [=]( const params_type& args ) { auto con = api_con.lock(); FC_ASSERT( con, "not connected" ); fc::raw::datastream ds( args.data(), args.size() ); auto api_result = gapi->call_generic( f, args ); return con->register_api( api_result ); }; } template std::function generic_api::api_visitor::to_generic( const std::function>(Args...)>& f )const { auto api_con = _api_con; auto gapi = &api; return [=]( const params_type& args )-> fc::variant { auto con = api_con.lock(); FC_ASSERT( con, "not connected" ); fc::raw::datastream ds( args.data(), args.size() ); auto api_result = gapi->call_generic( f, ds ); if( api_result ) return con->register_api( *api_result ); return result_type(); }; } template std::function generic_api::api_visitor::to_generic( const std::function& f )const { auto api_con = _api_con; auto gapi = &api; return [=]( const variants& args ) -> fc::variant { auto con = api_con.lock(); FC_ASSERT( con, "not connected" ); fc::raw::datastream ds( args.data(), args.size() ); auto api_result = gapi->call_generic( f, ds ); if( !api_result ) return result_type(); return api_result->register_api( *con ); }; } template std::function generic_api::api_visitor::to_generic( const std::function& f )const { generic_api* gapi = &api; return [f,gapi]( const params_type& args ) { fc::raw::datastream ds( args.data(), args.size() ); return fc::raw::pack(gapi->call_generic( f, ds )); }; } template std::function generic_api::api_visitor::to_generic( const std::function& f )const { generic_api* gapi = &api; return [f,gapi]( const params_type& args ) { fc::raw::datastream ds( args.data(), args.size() ); gapi->call_generic( f, ds ); return result_type(); }; } /** * It is slightly unclean tight coupling to have this method in the api class. * It breaks encapsulation by requiring an api class method to have a pointer * to an binary_api_connection. The reason this is necessary is we have a goal of being * able to call register_api() on an api through its base class api_base. But * register_api() must know the template parameters! * * The only reasonable way to achieve the goal is to implement register_api() * as a method in api (which obviously knows the template parameter T), * then make the implementation accessible through the base class (by making * it a pure virtual method in the base class which is overridden by the subclass's * implementation). */ template< typename Interface, typename Transform > api_id_type api< Interface, Transform >::register_api( binary_api_connection& conn )const { return conn.register_api( *this ); } template< typename T > api api_base::as() { // TODO: this method should probably be const (if it is not too hard) api* maybe_requested_type = dynamic_cast< api* >(this); if( maybe_requested_type != nullptr ) return *maybe_requested_type; detail::any_api* maybe_any = dynamic_cast< detail::any_api* >(this); FC_ASSERT( maybe_any != nullptr ); std::shared_ptr< binary_api_connection > api_conn = maybe_any->_binary_api_connection.lock(); FC_ASSERT( api_conn ); return api_conn->get_remote_api( maybe_any->_api_id ); } namespace detail { template template typename callback_functor::result_type callback_functor::operator()( Args... args )const { std::shared_ptr< fc::binary_api_connection > locked = _binary_api_connection.lock(); // TODO: make new exception type for this instead of recycling eof_exception if( !locked ) throw fc::eof_exception(); /// TODO------------->>> pack args... locked->send_callback( _callback_id, fc::raw::pack( args... ) ).template as< result_type >(); } template class callback_functor { public: typedef void result_type; callback_functor( std::weak_ptr< fc::binary_api_connection > con, uint64_t id ) :_callback_id(id),_binary_api_connection(con){} void operator()( Args... args )const { std::shared_ptr< fc::binary_api_connection > locked = _binary_api_connection.lock(); // TODO: make new exception type for this instead of recycling eof_exception if( !locked ) throw fc::eof_exception(); locked->send_notice( _callback_id, fc::variants{ args... } ); } private: uint64_t _callback_id; std::weak_ptr< fc::binary_api_connection > _binary_api_connection; }; } // namespace detail } // fc