From fa352e14bdc595f2d0d13e567ec8745b33c89bf5 Mon Sep 17 00:00:00 2001 From: Daniel Larimer Date: Tue, 10 Mar 2015 18:52:27 -0400 Subject: [PATCH] nested API calls fully abstracted --- include/fc/api.hpp | 129 ++++++++++++++---------- include/fc/rpc/api_client.hpp | 56 +++++++++++ include/fc/rpc/api_server.hpp | 179 ++++++++++++++++++++++++++++++++++ tests/api.cpp | 88 ++++++++++++----- 4 files changed, 376 insertions(+), 76 deletions(-) create mode 100644 include/fc/rpc/api_client.hpp create mode 100644 include/fc/rpc/api_server.hpp diff --git a/include/fc/api.hpp b/include/fc/api.hpp index 7c57e95..68c159c 100644 --- a/include/fc/api.hpp +++ b/include/fc/api.hpp @@ -1,5 +1,6 @@ #pragma once #include +#include #include #include #include @@ -9,63 +10,91 @@ #include namespace fc { + struct identity_member { + template + static std::function functor( P&& p, R (C::*mem_func)(Args...) ) { + return std::function([=](Args... args){ return (p->*mem_func)(args...); }); + } + template + static std::function functor( P&& p, R (C::*mem_func)(Args...)const ) { + return std::function([=](Args... args){ return (p->*mem_func)(args...); }); + } + }; + + + template< typename Interface, typename Transform > + struct vtable : public std::enable_shared_from_this> + { + private: + vtable(); + }; - namespace detail { - struct identity_member { - template - static std::function functor( P&& p, R (C::*mem_func)(Args...) ) { - return std::function([=](Args... args){ return (p->*mem_func)(args...); }); - } - template - static std::function functor( P&& p, R (C::*mem_func)(Args...)const ) { - return std::function([=](Args... args){ return (p->*mem_func)(args...); }); - } - }; - template< typename Interface, typename Transform = detail::identity_member > - struct vtable{}; - - template - struct vtable_visitor { - template - vtable_visitor( U&& u ):_this( fc::forward(u) ){} - - template - void operator()( const char* name, Function& memb, MemberPtr m )const { - memb = identity_member::functor( _this, m ); - } - ThisPtr _this; - }; - } // namespace detail + template + struct vtable_copy_visitor { + typedef OtherType other_type; - template + vtable_copy_visitor( OtherType& s):_source( s ){} + + template + void operator()( const char* name, std::function& memb, MemberPtr m )const { + OtherType* src = &_source; + memb = [src,m]( Args... args ){ + wdump( (uint64_t(src) ) ); + return (src->*m)(args...); + }; + } + OtherType& _source; + }; + + template class api { public: - typedef detail::vtable vtable_type; + typedef vtable vtable_type; - api(){} + api() + :_vtable( std::make_shared() ) + {} - template - api( InterfaceType* p ) - :_vtable( new vtable_type() ) { - _vtable->template visit_other( detail::vtable_visitor(p) ); + /** T is anything with pointer semantics */ + template + api( const T& p ) + :_vtable( std::make_shared() ) + { + _data = std::make_shared(p); + T& ptr = boost::any_cast(*_data); + auto& pointed_at = *ptr; + typedef typename std::remove_reference::type source_vtable_type; + _vtable->template visit_other( vtable_copy_visitor(pointed_at) ); } - template - api( const fc::shared_ptr& p ) - :_vtable( new vtable_type() ),_self(p){ - _vtable->template visit_other( detail::vtable_visitor(p.get()) ); + template + api( const std::shared_ptr< api >& p ) + :_vtable( std::make_shared() ) + { + _data = std::make_shared(p); + auto& ptr = *boost::any_cast< decltype(p) >(*_data); + auto& pointed_at = *ptr; + typedef typename std::remove_reference::type source_vtable_type; + _vtable->template visit_other( vtable_copy_visitor(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; + std::shared_ptr _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 \ - struct vtable : public fc::retainable { \ - vtable(){} \ - BOOST_PP_SEQ_FOR_EACH( FC_API_VTABLE_DEFINE_MEMBER, CLASS, METHODS ) \ - template \ + struct vtable : public std::enable_shared_from_this> { \ + BOOST_PP_SEQ_FOR_EACH( FC_API_VTABLE_DEFINE_MEMBER, CLASS, METHODS ) \ + template \ 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 diff --git a/include/fc/rpc/api_client.hpp b/include/fc/rpc/api_client.hpp new file mode 100644 index 0000000..d1841c3 --- /dev/null +++ b/include/fc/rpc/api_client.hpp @@ -0,0 +1,56 @@ +#pragma once +#include + +namespace fc { + + template + class api_client : public api + { + public: + api_client( fc::api server, uint64_t api_id = 0 ) + { + (*this)->visit( api_visitor(this, api_id, std::make_shared>(server)) ); + } + + private: + struct api_visitor + { + api_client* _client = nullptr; + uint32_t _api_id; + std::shared_ptr> _server; + + api_visitor( api_client* c, uint32_t api_id, std::shared_ptr> serv ) + :_client(c),_api_id(api_id),_server(std::move(serv)) + { wdump((int64_t(c))); } + + api_visitor() = delete; + + template + static Result from_variant( const variant& v, Result&&, const std::shared_ptr>& ) + { + return v.as(); + } + + template + static fc::api from_variant( const variant& v, fc::api&&, + const std::shared_ptr>& serv + ) + { + return api_client( serv, v.as_uint64() ); + } + + + template + void operator()( const char* name, std::function& memb )const + { + auto serv = _server; + auto api_id = _api_id; + memb = [serv,api_id,name]( Args... args ) { + vtable test; + auto var_result = (*serv)->call( api_id, name, {args...} ); + return from_variant( var_result, Result(), serv ); + }; + } + }; + }; +} diff --git a/include/fc/rpc/api_server.hpp b/include/fc/rpc/api_server.hpp new file mode 100644 index 0000000..24c5861 --- /dev/null +++ b/include/fc/rpc/api_server.hpp @@ -0,0 +1,179 @@ +#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) ) diff --git a/tests/api.cpp b/tests/api.cpp index bf62734..afa078b 100644 --- a/tests/api.cpp +++ b/tests/api.cpp @@ -1,6 +1,7 @@ #include #include -//#include +#include +#include class calculator { @@ -11,6 +12,19 @@ class calculator FC_API( calculator, (add)(sub) ) + +class nested_api +{ + public: + fc::api get_calc()const + { + FC_ASSERT( calc ); + return *calc; + } + fc::optional> 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 -//std::function bind_first_arg( const std::function& f, Arg0 ao ) -std::function bind_first_arg( const std::function& f, Arg0 ao ) -{ - return [=]( Args... args ) { return f( ao, args... ); }; -} - -template -R call_generic( const std::function& f, variants::const_iterator a0, variants::const_iterator e ) -{ - return f(a0->as()); -} -template -R call_generic( const std::function& f, variants::const_iterator a0, variants::const_iterator e ) -{ - return call_generic( bind_first_arg( f, a0->as() ), a0+1, e ); -} - -template -std::function to_generic( const std::function& 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 api_calc( &calc ); fc::api api_vcalc( &vcalc ); fc::api 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 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 serv( &server ); + + fc::api_client apic( serv ); + + fc::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; }