#pragma once #include #include #include #include #include #include #include namespace fc { class api_server; namespace impl { using std::vector; class generic_api { public: template generic_api( const Api& a, api_server& s ) :_api_server(s),_api(a) { boost::any_cast(a)->visit( api_visitor( *this, _api_server ) ); } generic_api( const generic_api& cpy ) = delete; variant call( const string& name, const variants& args ) { auto itr = _by_name.find(name); FC_ASSERT( itr != _by_name.end() ); return call( itr->second, args ); } variant call( uint32_t method_id, const variants& args ) { FC_ASSERT( method_id < _methods.size() ); return _methods[method_id](args); } private: friend struct api_visitor; struct api_visitor { api_visitor( generic_api& a, api_server& s ):api(a),server(s){ } ~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, variants::const_iterator a0, variants::const_iterator e )const { return f(); } template R call_generic( const std::function& f, variants::const_iterator a0, variants::const_iterator e )const { FC_ASSERT( a0 != e ); return f(a0->as()); } template R call_generic( const std::function& f, variants::const_iterator a0, variants::const_iterator e )const { FC_ASSERT( a0 != e && (a0+1) != e); return f(a0->as(), (a0+1)->as()); } template R call_generic( const std::function& f, variants::const_iterator a0, variants::const_iterator e )const { FC_ASSERT( a0 != e ); return call_generic( bind_first_arg( f, a0->as() ), a0+1, e ); } 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 { return [=]( const variants& args ) { return call_generic( f, args.begin(), args.end() ); }; } 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; api_server& server; api_server* server_ptr = nullptr; }; api_server& _api_server; fc::any _api; std::map< std::string, uint32_t > _by_name; vector< std::function > _methods; }; } // namespace impl /** * @brief exposes Object Oriented API via a flat RPC call interface * * The wire API is simply: * * variant call( obj_id, method_num|name, [params...] ) * * The default obj_id is 0. */ class api_server { public: typedef uint32_t api_id_type; api_server() { } api_server( const api_server& cpy ) = delete; template api_id_type register_api( const Interface& a ) { _apis.push_back( std::unique_ptr( new impl::generic_api(a, *this) ) ); return _apis.size() - 1; } variant call( api_id_type api_id, const string& method_name, const variants& args = variants() )const { wdump( (api_id)(method_name)(args) ); FC_ASSERT( _apis.size() > api_id ); return _apis[api_id]->call( method_name, args ); } private: std::vector< std::unique_ptr > _apis; }; template std::function impl::generic_api::api_visitor::to_generic( const std::function(Args...)>& f )const { auto tmp = *this; return [=]( const variants& args ) { auto api_result = tmp.call_generic( f, args.begin(), args.end() ); return tmp.server.register_api( api_result ); }; } template std::function impl::generic_api::api_visitor::to_generic( const std::function>(Args...)>& f )const { auto tmp = *this; return [=]( const variants& args )-> fc::variant { auto api_result = tmp.call_generic( f, args.begin(), args.end() ); if( api_result ) return tmp.server.register_api( *api_result ); return variant(); }; } } // namesapce fc FC_API( fc::api_server, (call) )