refactor api_client and api_server into api_connection and add JSON impl

This commit is contained in:
Daniel Larimer 2015-03-11 10:49:30 -04:00
parent fa352e14bd
commit 171eb212cd
5 changed files with 290 additions and 249 deletions

View file

@ -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);

View file

@ -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 );
};
}
};
};
}

View 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

View file

@ -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) )

View file

@ -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) );