refactor api_client and api_server into api_connection and add JSON impl
This commit is contained in:
parent
fa352e14bd
commit
171eb212cd
5 changed files with 290 additions and 249 deletions
|
|
@ -68,22 +68,14 @@ namespace fc {
|
|||
_vtable->template visit_other( vtable_copy_visitor<source_vtable_type>(pointed_at) );
|
||||
}
|
||||
|
||||
template<typename T >
|
||||
api( const std::shared_ptr< api<T> >& p )
|
||||
:_vtable( std::make_shared<vtable_type>() )
|
||||
{
|
||||
_data = std::make_shared<fc::any>(p);
|
||||
auto& ptr = *boost::any_cast< decltype(p) >(*_data);
|
||||
auto& pointed_at = *ptr;
|
||||
typedef typename std::remove_reference<decltype(pointed_at)>::type source_vtable_type;
|
||||
_vtable->template visit_other( vtable_copy_visitor<source_vtable_type>(pointed_at) );
|
||||
}
|
||||
|
||||
api( const api& cpy )
|
||||
:_vtable(cpy._vtable),_data(cpy._data)
|
||||
{
|
||||
}
|
||||
|
||||
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); }
|
||||
|
||||
vtable_type& operator*()const { wdump((uint64_t(this))); assert(_vtable); FC_ASSERT( _vtable ); return *_vtable; }
|
||||
vtable_type* operator->()const {
|
||||
assert(_vtable);
|
||||
|
|
|
|||
|
|
@ -1,56 +0,0 @@
|
|||
#pragma once
|
||||
#include <fc/rpc/api_server.hpp>
|
||||
|
||||
namespace fc {
|
||||
|
||||
template<typename Interface, typename Adaptor = identity_member>
|
||||
class api_client : public api<Interface,Adaptor>
|
||||
{
|
||||
public:
|
||||
api_client( fc::api<api_server> server, uint64_t api_id = 0 )
|
||||
{
|
||||
(*this)->visit( api_visitor(this, api_id, std::make_shared<api<api_server>>(server)) );
|
||||
}
|
||||
|
||||
private:
|
||||
struct api_visitor
|
||||
{
|
||||
api_client* _client = nullptr;
|
||||
uint32_t _api_id;
|
||||
std::shared_ptr<api<api_server>> _server;
|
||||
|
||||
api_visitor( api_client* c, uint32_t api_id, std::shared_ptr<api<api_server>> serv )
|
||||
:_client(c),_api_id(api_id),_server(std::move(serv))
|
||||
{ wdump((int64_t(c))); }
|
||||
|
||||
api_visitor() = delete;
|
||||
|
||||
template<typename Result>
|
||||
static Result from_variant( const variant& v, Result&&, const std::shared_ptr<api<api_server>>& )
|
||||
{
|
||||
return v.as<Result>();
|
||||
}
|
||||
|
||||
template<typename ResultInterface, typename ResultAdaptor>
|
||||
static fc::api<ResultInterface,ResultAdaptor> from_variant( const variant& v, fc::api<ResultInterface,ResultAdaptor>&&,
|
||||
const std::shared_ptr<api<api_server>>& serv
|
||||
)
|
||||
{
|
||||
return api_client<ResultInterface,ResultAdaptor>( serv, v.as_uint64() );
|
||||
}
|
||||
|
||||
|
||||
template<typename Result, typename... Args>
|
||||
void operator()( const char* name, std::function<Result(Args...)>& memb )const
|
||||
{
|
||||
auto serv = _server;
|
||||
auto api_id = _api_id;
|
||||
memb = [serv,api_id,name]( Args... args ) {
|
||||
vtable<api_server,identity_member> test;
|
||||
auto var_result = (*serv)->call( api_id, name, {args...} );
|
||||
return from_variant( var_result, Result(), serv );
|
||||
};
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
261
include/fc/rpc/api_connection.hpp
Normal file
261
include/fc/rpc/api_connection.hpp
Normal file
|
|
@ -0,0 +1,261 @@
|
|||
#pragma once
|
||||
#include <fc/variant.hpp>
|
||||
#include <fc/optional.hpp>
|
||||
#include <fc/api.hpp>
|
||||
#include <fc/any.hpp>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
#include <fc/rpc/json_connection.hpp>
|
||||
|
||||
namespace fc {
|
||||
class api_connection;
|
||||
|
||||
typedef uint32_t api_id_type;
|
||||
|
||||
namespace detail {
|
||||
class generic_api
|
||||
{
|
||||
public:
|
||||
template<typename Api>
|
||||
generic_api( const Api& a, const std::shared_ptr<fc::api_connection>& c );
|
||||
|
||||
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, const std::shared_ptr<fc::api_connection>& s ):api(a),_api_con(s){ }
|
||||
|
||||
template<typename R, typename Arg0, typename ... Args>
|
||||
std::function<R(Args...)> bind_first_arg( const std::function<R(Arg0,Args...)>& f, Arg0 a0 )const
|
||||
{
|
||||
return [=]( Args... args ) { return f( a0, args... ); };
|
||||
}
|
||||
template<typename R>
|
||||
R call_generic( const std::function<R()>& f, variants::const_iterator a0, variants::const_iterator e )const
|
||||
{
|
||||
return f();
|
||||
}
|
||||
|
||||
/*
|
||||
template<typename R, typename Arg0>
|
||||
R call_generic( const std::function<R(Arg0)>& f, variants::const_iterator a0, variants::const_iterator e )const
|
||||
{
|
||||
FC_ASSERT( a0 != e );
|
||||
return f(a0->as<Arg0>());
|
||||
}
|
||||
*/
|
||||
|
||||
template<typename R, typename Arg0, typename ... Args>
|
||||
R call_generic( const std::function<R(Arg0,Args...)>& f, variants::const_iterator a0, variants::const_iterator e )const
|
||||
{
|
||||
FC_ASSERT( a0 != e );
|
||||
return call_generic<R,Args...>( bind_first_arg( f, a0->as<Arg0>() ), a0+1, e );
|
||||
}
|
||||
|
||||
template<typename Interface, typename Adaptor, typename ... Args>
|
||||
std::function<variant(const fc::variants&)> to_generic( const std::function<api<Interface,Adaptor>(Args...)>& f )const;
|
||||
|
||||
template<typename Interface, typename Adaptor, typename ... Args>
|
||||
std::function<variant(const fc::variants&)> to_generic( const std::function<fc::optional<api<Interface,Adaptor>>(Args...)>& f )const;
|
||||
|
||||
template<typename R, typename ... Args>
|
||||
std::function<variant(const fc::variants&)> to_generic( const std::function<R(Args...)>& f )const
|
||||
{
|
||||
return [=]( const variants& args ) {
|
||||
return call_generic( f, args.begin(), args.end() );
|
||||
};
|
||||
}
|
||||
|
||||
template<typename Result, typename... Args>
|
||||
void operator()( const char* name, std::function<Result(Args...)>& memb )const {
|
||||
api._methods.emplace_back( to_generic( memb ) );
|
||||
api._by_name[name] = api._methods.size() - 1;
|
||||
}
|
||||
|
||||
generic_api& api;
|
||||
const std::shared_ptr<fc::api_connection>& _api_con;
|
||||
};
|
||||
|
||||
|
||||
std::shared_ptr<fc::api_connection> _api_connection;
|
||||
fc::any _api;
|
||||
std::map< std::string, uint32_t > _by_name;
|
||||
std::vector< std::function<variant(const variants&)> > _methods;
|
||||
}; // class generic_api
|
||||
} // namespace detail
|
||||
|
||||
|
||||
|
||||
class api_connection : public std::enable_shared_from_this<fc::api_connection>
|
||||
{
|
||||
public:
|
||||
api_connection(){}
|
||||
virtual ~api_connection(){};
|
||||
|
||||
|
||||
template<typename T>
|
||||
api<T> get_remote_api( api_id_type api_id = 0 )
|
||||
{
|
||||
api<T> result;
|
||||
result->visit( api_visitor( api_id, this->shared_from_this() ) );
|
||||
return result;
|
||||
}
|
||||
|
||||
/** makes calls to the remote server */
|
||||
virtual variant send_call( api_id_type api_id, const string& method_name, const variants& args = variants() )const
|
||||
{
|
||||
FC_ASSERT( _remote_connection );
|
||||
return _remote_connection->receive_call( api_id, method_name, args );
|
||||
}
|
||||
|
||||
variant receive_call( api_id_type api_id, const string& method_name, const variants& args = variants() )const
|
||||
{
|
||||
wdump( (api_id)(method_name)(args) );
|
||||
FC_ASSERT( _local_apis.size() > api_id );
|
||||
return _local_apis[api_id]->call( method_name, args );
|
||||
}
|
||||
|
||||
template<typename Interface>
|
||||
api_id_type register_api( const Interface& a )
|
||||
{
|
||||
_local_apis.push_back( std::unique_ptr<detail::generic_api>( new detail::generic_api(a, shared_from_this() ) ) );
|
||||
return _local_apis.size() - 1;
|
||||
}
|
||||
|
||||
void set_remote_connection( const std::shared_ptr<fc::api_connection>& rc )
|
||||
{
|
||||
FC_ASSERT( !_remote_connection );
|
||||
FC_ASSERT( rc != this->shared_from_this() );
|
||||
_remote_connection = rc;
|
||||
if( _remote_connection && _remote_connection->remote_connection() != this->shared_from_this() )
|
||||
_remote_connection->set_remote_connection( this->shared_from_this() );
|
||||
}
|
||||
const std::shared_ptr<fc::api_connection>& remote_connection()const { return _remote_connection; }
|
||||
|
||||
private:
|
||||
std::vector< std::unique_ptr<detail::generic_api> > _local_apis;
|
||||
std::shared_ptr<fc::api_connection> _remote_connection;
|
||||
|
||||
|
||||
struct api_visitor
|
||||
{
|
||||
uint32_t _api_id;
|
||||
std::shared_ptr<fc::api_connection> _connection;
|
||||
|
||||
api_visitor( uint32_t api_id, std::shared_ptr<fc::api_connection> con )
|
||||
:_api_id(api_id),_connection(std::move(con))
|
||||
{
|
||||
}
|
||||
|
||||
api_visitor() = delete;
|
||||
|
||||
template<typename Result>
|
||||
static Result from_variant( const variant& v, Result*, const std::shared_ptr<fc::api_connection>& )
|
||||
{
|
||||
return v.as<Result>();
|
||||
}
|
||||
|
||||
template<typename ResultInterface>
|
||||
static fc::api<ResultInterface> from_variant( const variant& v,
|
||||
fc::api<ResultInterface>* /*used for template deduction*/,
|
||||
const std::shared_ptr<fc::api_connection>& con
|
||||
)
|
||||
{
|
||||
return con->get_remote_api<ResultInterface>( v.as_uint64() );
|
||||
}
|
||||
|
||||
|
||||
template<typename Result, typename... Args>
|
||||
void operator()( const char* name, std::function<Result(Args...)>& 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, {args...} );
|
||||
return from_variant( var_result, (Result*)nullptr, con );
|
||||
};
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
template<typename Api>
|
||||
detail::generic_api::generic_api( const Api& a, const std::shared_ptr<fc::api_connection>& c )
|
||||
:_api_connection(c),_api(a)
|
||||
{
|
||||
boost::any_cast<const Api&>(a)->visit( api_visitor( *this, _api_connection ) );
|
||||
}
|
||||
|
||||
template<typename Interface, typename Adaptor, typename ... Args>
|
||||
std::function<variant(const fc::variants&)> detail::generic_api::api_visitor::to_generic(
|
||||
const std::function<fc::api<Interface,Adaptor>(Args...)>& f )const
|
||||
{
|
||||
auto api_con = _api_con;
|
||||
return [=]( const variants& args ) {
|
||||
auto api_result = call_generic( f, args.begin(), args.end() );
|
||||
return api_con->register_api( api_result );
|
||||
};
|
||||
}
|
||||
template<typename Interface, typename Adaptor, typename ... Args>
|
||||
std::function<variant(const fc::variants&)> detail::generic_api::api_visitor::to_generic(
|
||||
const std::function<fc::optional<fc::api<Interface,Adaptor>>(Args...)>& f )const
|
||||
{
|
||||
auto api_con = _api_con;
|
||||
return [=]( const variants& args )-> fc::variant {
|
||||
auto api_result = call_generic( f, args.begin(), args.end() );
|
||||
if( api_result )
|
||||
return api_con->register_api( *api_result );
|
||||
return variant();
|
||||
};
|
||||
}
|
||||
|
||||
class json_api_connection : public api_connection
|
||||
{
|
||||
public:
|
||||
json_api_connection(){};
|
||||
void set_json_connection( const std::shared_ptr<rpc::json_connection>& json_con )
|
||||
{
|
||||
json_con->add_method( "call", [this]( const variants& args ) -> variant {
|
||||
FC_ASSERT( args.size() == 3 && args[2].is_array() );
|
||||
return this->receive_call( args[0].as_uint64(),
|
||||
args[1].as_string(),
|
||||
args[2].get_array() );
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
json_api_connection( const std::shared_ptr<rpc::json_connection>& json_con )
|
||||
{
|
||||
set_json_connection( json_con );
|
||||
}
|
||||
|
||||
virtual variant send_call( api_id_type api_id,
|
||||
const string& method_name,
|
||||
const variants& args = variants() )const override
|
||||
{
|
||||
return _json_con->async_call( "call", {api_id, method_name, args} ).wait();
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<rpc::json_connection> _json_con;
|
||||
|
||||
};
|
||||
|
||||
} // fc
|
||||
|
|
@ -1,179 +0,0 @@
|
|||
#pragma once
|
||||
#include <fc/variant.hpp>
|
||||
#include <fc/optional.hpp>
|
||||
#include <fc/api.hpp>
|
||||
#include <fc/any.hpp>
|
||||
#include <vector>
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
|
||||
namespace fc {
|
||||
class api_server;
|
||||
namespace impl {
|
||||
using std::vector;
|
||||
|
||||
class generic_api
|
||||
{
|
||||
public:
|
||||
template<typename Api>
|
||||
generic_api( const Api& a, api_server& s )
|
||||
:_api_server(s),_api(a)
|
||||
{
|
||||
boost::any_cast<const Api&>(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<typename R, typename Arg0, typename ... Args>
|
||||
std::function<R(Args...)> bind_first_arg( const std::function<R(Arg0,Args...)>& f, Arg0 a0 )const
|
||||
{
|
||||
return [=]( Args... args ) { return f( a0, args... ); };
|
||||
}
|
||||
template<typename R>
|
||||
R call_generic( const std::function<R()>& f, variants::const_iterator a0, variants::const_iterator e )const
|
||||
{
|
||||
return f();
|
||||
}
|
||||
|
||||
template<typename R, typename Arg0>
|
||||
R call_generic( const std::function<R(Arg0)>& f, variants::const_iterator a0, variants::const_iterator e )const
|
||||
{
|
||||
FC_ASSERT( a0 != e );
|
||||
return f(a0->as<Arg0>());
|
||||
}
|
||||
template<typename R, typename Arg0, typename Arg1>
|
||||
R call_generic( const std::function<R(Arg0,Arg1)>& f, variants::const_iterator a0, variants::const_iterator e )const
|
||||
{
|
||||
FC_ASSERT( a0 != e && (a0+1) != e);
|
||||
return f(a0->as<Arg0>(), (a0+1)->as<Arg1>());
|
||||
}
|
||||
|
||||
template<typename R, typename Arg0, typename ... Args>
|
||||
R call_generic( const std::function<R(Arg0,Args...)>& f, variants::const_iterator a0, variants::const_iterator e )const
|
||||
{
|
||||
FC_ASSERT( a0 != e );
|
||||
return call_generic<R,Args...>( bind_first_arg( f, a0->as<Arg0>() ), a0+1, e );
|
||||
}
|
||||
|
||||
template<typename Interface, typename Adaptor, typename ... Args>
|
||||
std::function<variant(const fc::variants&)> to_generic( const std::function<api<Interface,Adaptor>(Args...)>& f )const;
|
||||
template<typename Interface, typename Adaptor, typename ... Args>
|
||||
std::function<variant(const fc::variants&)> to_generic( const std::function<fc::optional<api<Interface,Adaptor>>(Args...)>& f )const;
|
||||
|
||||
template<typename R, typename ... Args>
|
||||
std::function<variant(const fc::variants&)> to_generic( const std::function<R(Args...)>& f )const
|
||||
{
|
||||
return [=]( const variants& args ) {
|
||||
return call_generic( f, args.begin(), args.end() );
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
template<typename Result, typename... Args>
|
||||
void operator()( const char* name, std::function<Result(Args...)>& 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<variant(const variants&)> > _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<typename Interface>
|
||||
api_id_type register_api( const Interface& a )
|
||||
{
|
||||
_apis.push_back( std::unique_ptr<impl::generic_api>( 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<impl::generic_api> > _apis;
|
||||
|
||||
};
|
||||
|
||||
template<typename Interface, typename Adaptor, typename ... Args>
|
||||
std::function<variant(const fc::variants&)> impl::generic_api::api_visitor::to_generic(
|
||||
const std::function<fc::api<Interface,Adaptor>(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<typename Interface, typename Adaptor, typename ... Args>
|
||||
std::function<variant(const fc::variants&)> impl::generic_api::api_visitor::to_generic(
|
||||
const std::function<fc::optional<fc::api<Interface,Adaptor>>(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) )
|
||||
|
|
@ -1,7 +1,6 @@
|
|||
#include <fc/api.hpp>
|
||||
#include <fc/log/logger.hpp>
|
||||
#include <fc/rpc/api_server.hpp>
|
||||
#include <fc/rpc/api_client.hpp>
|
||||
#include <fc/rpc/api_connection.hpp>
|
||||
|
||||
class calculator
|
||||
{
|
||||
|
|
@ -76,7 +75,6 @@ int main( int argc, char** argv )
|
|||
{
|
||||
elog( "${e}", ("e",e.to_detail_string() ) );
|
||||
}
|
||||
*/
|
||||
|
||||
ilog( "------------------ NESTED TEST --------------" );
|
||||
try {
|
||||
|
|
@ -100,6 +98,31 @@ int main( int argc, char** argv )
|
|||
fc::api<nested_api> remote_api = apic;
|
||||
|
||||
|
||||
auto remote_calc = remote_api->get_calc();
|
||||
int r = remote_calc->add( 4, 5 );
|
||||
idump( (r) );
|
||||
|
||||
} catch ( const fc::exception& e )
|
||||
{
|
||||
elog( "${e}", ("e",e.to_detail_string() ) );
|
||||
}
|
||||
*/
|
||||
|
||||
ilog( "------------------ NESTED TEST --------------" );
|
||||
try {
|
||||
nested_api napi_impl;
|
||||
napi_impl.calc = api_calc;
|
||||
fc::api<nested_api> napi(&napi_impl);
|
||||
|
||||
|
||||
auto client_side = std::make_shared<api_connection>();
|
||||
auto server_side = std::make_shared<api_connection>();
|
||||
server_side->set_remote_connection( client_side );
|
||||
|
||||
server_side->register_api( napi );
|
||||
|
||||
fc::api<nested_api> remote_api = client_side->get_remote_api<nested_api>();
|
||||
|
||||
auto remote_calc = remote_api->get_calc();
|
||||
int r = remote_calc->add( 4, 5 );
|
||||
idump( (r) );
|
||||
|
|
|
|||
Loading…
Reference in a new issue