nested API calls fully abstracted
This commit is contained in:
parent
cf849b8b54
commit
fa352e14bd
4 changed files with 376 additions and 76 deletions
|
|
@ -1,5 +1,6 @@
|
|||
#pragma once
|
||||
#include <fc/thread/future.hpp>
|
||||
#include <fc/any.hpp>
|
||||
#include <functional>
|
||||
#include <boost/config.hpp>
|
||||
#include <boost/preprocessor/repeat.hpp>
|
||||
|
|
@ -9,63 +10,91 @@
|
|||
#include <boost/preprocessor/facilities/empty.hpp>
|
||||
|
||||
namespace fc {
|
||||
struct identity_member {
|
||||
template<typename R, typename C, typename P, typename... Args>
|
||||
static std::function<R(Args...)> functor( P&& p, R (C::*mem_func)(Args...) ) {
|
||||
return std::function<R(Args...)>([=](Args... args){ return (p->*mem_func)(args...); });
|
||||
}
|
||||
template<typename R, typename C, typename P, typename... Args>
|
||||
static std::function<R(Args...)> functor( P&& p, R (C::*mem_func)(Args...)const ) {
|
||||
return std::function<R(Args...)>([=](Args... args){ return (p->*mem_func)(args...); });
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template< typename Interface, typename Transform >
|
||||
struct vtable : public std::enable_shared_from_this<vtable<Interface,Transform>>
|
||||
{
|
||||
private:
|
||||
vtable();
|
||||
};
|
||||
|
||||
namespace detail {
|
||||
struct identity_member {
|
||||
template<typename R, typename C, typename P, typename... Args>
|
||||
static std::function<R(Args...)> functor( P&& p, R (C::*mem_func)(Args...) ) {
|
||||
return std::function<R(Args...)>([=](Args... args){ return (p->*mem_func)(args...); });
|
||||
}
|
||||
template<typename R, typename C, typename P, typename... Args>
|
||||
static std::function<R(Args...)> functor( P&& p, R (C::*mem_func)(Args...)const ) {
|
||||
return std::function<R(Args...)>([=](Args... args){ return (p->*mem_func)(args...); });
|
||||
}
|
||||
};
|
||||
|
||||
template< typename Interface, typename Transform = detail::identity_member >
|
||||
struct vtable{};
|
||||
|
||||
template<typename ThisPtr>
|
||||
struct vtable_visitor {
|
||||
template<typename U>
|
||||
vtable_visitor( U&& u ):_this( fc::forward<U>(u) ){}
|
||||
|
||||
template<typename Function, typename MemberPtr>
|
||||
void operator()( const char* name, Function& memb, MemberPtr m )const {
|
||||
memb = identity_member::functor( _this, m );
|
||||
}
|
||||
ThisPtr _this;
|
||||
};
|
||||
} // namespace detail
|
||||
template<typename OtherType>
|
||||
struct vtable_copy_visitor {
|
||||
typedef OtherType other_type;
|
||||
|
||||
template<typename Interface, typename Transform = detail::identity_member >
|
||||
vtable_copy_visitor( OtherType& s):_source( s ){}
|
||||
|
||||
template<typename R, typename MemberPtr, typename... Args>
|
||||
void operator()( const char* name, std::function<R(Args...)>& memb, MemberPtr m )const {
|
||||
OtherType* src = &_source;
|
||||
memb = [src,m]( Args... args ){
|
||||
wdump( (uint64_t(src) ) );
|
||||
return (src->*m)(args...);
|
||||
};
|
||||
}
|
||||
OtherType& _source;
|
||||
};
|
||||
|
||||
template<typename Interface, typename Transform = identity_member >
|
||||
class api {
|
||||
public:
|
||||
typedef detail::vtable<Interface,Transform> vtable_type;
|
||||
typedef vtable<Interface,Transform> vtable_type;
|
||||
|
||||
api(){}
|
||||
api()
|
||||
:_vtable( std::make_shared<vtable_type>() )
|
||||
{}
|
||||
|
||||
template<typename InterfaceType>
|
||||
api( InterfaceType* p )
|
||||
:_vtable( new vtable_type() ) {
|
||||
_vtable->template visit_other<InterfaceType>( detail::vtable_visitor<InterfaceType*>(p) );
|
||||
/** T is anything with pointer semantics */
|
||||
template<typename T >
|
||||
api( const T& p )
|
||||
:_vtable( std::make_shared<vtable_type>() )
|
||||
{
|
||||
_data = std::make_shared<fc::any>(p);
|
||||
T& ptr = boost::any_cast<T&>(*_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) );
|
||||
}
|
||||
|
||||
template<typename InterfaceType>
|
||||
api( const fc::shared_ptr<InterfaceType>& p )
|
||||
:_vtable( new vtable_type() ),_self(p){
|
||||
_vtable->template visit_other<InterfaceType>( detail::vtable_visitor<InterfaceType*>(p.get()) );
|
||||
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) );
|
||||
}
|
||||
|
||||
//vtable_type& operator*() { return *_vtable; }
|
||||
vtable_type& operator*()const { return *_vtable; }
|
||||
api( const api& cpy )
|
||||
:_vtable(cpy._vtable),_data(cpy._data)
|
||||
{
|
||||
}
|
||||
|
||||
//vtable_type* operator->() { return _vtable.get(); }
|
||||
vtable_type* operator->()const { return _vtable.get(); }
|
||||
vtable_type& operator*()const { wdump((uint64_t(this))); assert(_vtable); FC_ASSERT( _vtable ); return *_vtable; }
|
||||
vtable_type* operator->()const {
|
||||
assert(_vtable);
|
||||
FC_ASSERT( _vtable );
|
||||
return _vtable.get();
|
||||
}
|
||||
void test();
|
||||
|
||||
protected:
|
||||
fc::shared_ptr< vtable_type > _vtable;
|
||||
fc::shared_ptr< fc::retainable > _self;
|
||||
std::shared_ptr<vtable_type> _vtable;
|
||||
std::shared_ptr<fc::any> _data;
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -77,17 +106,17 @@ namespace fc {
|
|||
#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 ) \
|
||||
v( BOOST_PP_STRINGIZE(elem), elem, &T::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 { namespace detail { \
|
||||
namespace fc { \
|
||||
template<typename Transform> \
|
||||
struct vtable<CLASS,Transform> : public fc::retainable { \
|
||||
vtable(){} \
|
||||
BOOST_PP_SEQ_FOR_EACH( FC_API_VTABLE_DEFINE_MEMBER, CLASS, METHODS ) \
|
||||
template<typename T, typename Visitor> \
|
||||
struct vtable<CLASS,Transform> : public std::enable_shared_from_this<vtable<CLASS,Transform>> { \
|
||||
BOOST_PP_SEQ_FOR_EACH( FC_API_VTABLE_DEFINE_MEMBER, CLASS, METHODS ) \
|
||||
template<typename Visitor> \
|
||||
void visit_other( Visitor&& v ){ \
|
||||
BOOST_PP_SEQ_FOR_EACH( FC_API_VTABLE_DEFINE_VISIT_OTHER, CLASS, METHODS ) \
|
||||
} \
|
||||
|
|
@ -96,6 +125,6 @@ namespace fc { namespace detail { \
|
|||
BOOST_PP_SEQ_FOR_EACH( FC_API_VTABLE_DEFINE_VISIT, CLASS, METHODS ) \
|
||||
} \
|
||||
}; \
|
||||
} }
|
||||
}
|
||||
//#undef FC_API_VTABLE_DEFINE_MEMBER
|
||||
//#undef FC_API_VTABLE_DEFINE_VISIT
|
||||
|
|
|
|||
56
include/fc/rpc/api_client.hpp
Normal file
56
include/fc/rpc/api_client.hpp
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
#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 );
|
||||
};
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
179
include/fc/rpc/api_server.hpp
Normal file
179
include/fc/rpc/api_server.hpp
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
#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,6 +1,7 @@
|
|||
#include <fc/api.hpp>
|
||||
#include <fc/log/logger.hpp>
|
||||
//#include <fc/rpc/api_server.hpp>
|
||||
#include <fc/rpc/api_server.hpp>
|
||||
#include <fc/rpc/api_client.hpp>
|
||||
|
||||
class calculator
|
||||
{
|
||||
|
|
@ -11,6 +12,19 @@ class calculator
|
|||
|
||||
FC_API( calculator, (add)(sub) )
|
||||
|
||||
|
||||
class nested_api
|
||||
{
|
||||
public:
|
||||
fc::api<calculator> get_calc()const
|
||||
{
|
||||
FC_ASSERT( calc );
|
||||
return *calc;
|
||||
}
|
||||
fc::optional<fc::api<calculator>> calc;
|
||||
};
|
||||
FC_API( nested_api, (get_calc) );
|
||||
|
||||
using namespace fc;
|
||||
|
||||
class some_calculator
|
||||
|
|
@ -26,37 +40,16 @@ class variant_calculator
|
|||
double sub( fc::variant a, fc::variant b ) { return a.as_double()-b.as_double(); }
|
||||
};
|
||||
|
||||
template<typename R, typename Arg0, typename ... Args>
|
||||
//std::function<R(Args...)> bind_first_arg( const std::function<R(Arg0,Args...)>& f, Arg0 ao )
|
||||
std::function<R(Args...)> bind_first_arg( const std::function<R(Arg0,Args...)>& f, Arg0 ao )
|
||||
{
|
||||
return [=]( Args... args ) { return f( ao, args... ); };
|
||||
}
|
||||
|
||||
template<typename R, typename Arg0>
|
||||
R call_generic( const std::function<R(Arg0)>& f, variants::const_iterator a0, variants::const_iterator 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 )
|
||||
{
|
||||
return call_generic<R,Args...>( bind_first_arg( f, a0->as<Arg0>() ), a0+1, e );
|
||||
}
|
||||
|
||||
template<typename R, typename ... Args>
|
||||
std::function<variant(const fc::variants&)> to_generic( const std::function<R(Args...)>& f )
|
||||
{
|
||||
return [=]( const variants& args ) { return call_generic( f, args.begin(), args.end() ); };
|
||||
}
|
||||
|
||||
int main( int argc, char** argv )
|
||||
{
|
||||
some_calculator calc;
|
||||
variant_calculator vcalc;
|
||||
|
||||
fc::api<calculator> api_calc( &calc );
|
||||
fc::api<calculator> api_vcalc( &vcalc );
|
||||
fc::api<calculator> api_nested_calc( api_calc );
|
||||
|
||||
wdump( (api_calc->add(5,4)) );
|
||||
wdump( (api_calc->sub(5,4)) );
|
||||
wdump( (api_vcalc->add(5,4)) );
|
||||
|
|
@ -64,14 +57,57 @@ int main( int argc, char** argv )
|
|||
wdump( (api_nested_calc->sub(5,4)) );
|
||||
wdump( (api_nested_calc->sub(5,4)) );
|
||||
|
||||
/*
|
||||
variants v = { 4, 5 };
|
||||
auto g = to_generic( api_calc->add );
|
||||
auto r = call_generic( api_calc->add, v.begin(), v.end() );
|
||||
wdump((r));
|
||||
wdump( (g(v)) );
|
||||
*/
|
||||
|
||||
/*
|
||||
try {
|
||||
fc::api_server server;
|
||||
auto api_id = server.register_api( api_calc );
|
||||
wdump( (api_id) );
|
||||
auto result = server.call( api_id, "add", {4, 5} );
|
||||
wdump( (result) );
|
||||
} 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);
|
||||
|
||||
fc::api_server server;
|
||||
auto api_id = server.register_api( napi );
|
||||
wdump( (api_id) );
|
||||
auto result = server.call( api_id, "get_calc" );
|
||||
wdump( (result) );
|
||||
result = server.call( result.as_uint64(), "add", {4,5} );
|
||||
wdump( (result) );
|
||||
|
||||
|
||||
// fc::api_server server;
|
||||
// server.register_api( api_calc );
|
||||
fc::api<api_server> serv( &server );
|
||||
|
||||
fc::api_client<nested_api> apic( serv );
|
||||
|
||||
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() ) );
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue