nested API calls fully abstracted

This commit is contained in:
Daniel Larimer 2015-03-10 18:52:27 -04:00
parent cf849b8b54
commit fa352e14bd
4 changed files with 376 additions and 76 deletions

View file

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

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

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

View file

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