Merge branch 'master' of https://github.com/steemit/fc
This commit is contained in:
commit
b34e8584ae
4 changed files with 146 additions and 6 deletions
|
|
@ -38,8 +38,33 @@ namespace fc {
|
|||
OtherType& _source;
|
||||
};
|
||||
|
||||
template<typename Interface, typename Transform >
|
||||
class api;
|
||||
|
||||
class api_connection;
|
||||
|
||||
typedef uint32_t api_id_type;
|
||||
|
||||
class api_base
|
||||
{
|
||||
public:
|
||||
api_base() {}
|
||||
virtual ~api_base() {}
|
||||
|
||||
virtual uint64_t get_handle()const = 0;
|
||||
|
||||
virtual api_id_type register_api( api_connection& conn )const = 0;
|
||||
|
||||
// defined in api_connection.hpp
|
||||
template< typename T >
|
||||
api<T, identity_member> as();
|
||||
};
|
||||
typedef std::shared_ptr< api_base > api_ptr;
|
||||
|
||||
class api_connection;
|
||||
|
||||
template<typename Interface, typename Transform = identity_member >
|
||||
class api {
|
||||
class api : public api_base {
|
||||
public:
|
||||
typedef vtable<Interface,Transform> vtable_type;
|
||||
|
||||
|
|
@ -58,10 +83,12 @@ namespace fc {
|
|||
}
|
||||
|
||||
api( const api& cpy ):_vtable(cpy._vtable),_data(cpy._data) {}
|
||||
virtual ~api() {}
|
||||
|
||||
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); }
|
||||
uint64_t get_handle()const { return uint64_t(_data.get()); }
|
||||
virtual uint64_t get_handle()const override { return uint64_t(_data.get()); }
|
||||
virtual api_id_type register_api( api_connection& conn )const override; // defined in api_connection.hpp
|
||||
|
||||
vtable_type& operator*()const { FC_ASSERT( _vtable ); return *_vtable; }
|
||||
vtable_type* operator->()const { FC_ASSERT( _vtable ); return _vtable.get(); }
|
||||
|
|
|
|||
|
|
@ -13,8 +13,6 @@
|
|||
namespace fc {
|
||||
class api_connection;
|
||||
|
||||
typedef uint32_t api_id_type;
|
||||
|
||||
namespace detail {
|
||||
template<typename Signature>
|
||||
class callback_functor
|
||||
|
|
@ -68,6 +66,33 @@ namespace fc {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* If api<T> is returned from a remote method, the API is eagerly bound to api<T> of
|
||||
* the correct type in api_visitor::from_variant(). This binding [1] needs a reference
|
||||
* to the api_connection, which is made available to from_variant() as a parameter.
|
||||
*
|
||||
* However, in the case of a remote method which returns api_base which can subsequently
|
||||
* be cast by the caller with as<T>, we need to keep track of the connection because
|
||||
* the binding is done later (when the client code actually calls as<T>).
|
||||
*
|
||||
* [1] The binding actually happens in get_remote_api().
|
||||
*/
|
||||
class any_api : public api_base
|
||||
{
|
||||
public:
|
||||
any_api( api_id_type api_id, const std::shared_ptr<fc::api_connection>& con )
|
||||
: _api_id(api_id), _api_connection(con) {}
|
||||
|
||||
virtual uint64_t get_handle()const override
|
||||
{ return _api_id; }
|
||||
|
||||
virtual api_id_type register_api( api_connection& conn )const override
|
||||
{ FC_ASSERT( false ); return api_id_type(); }
|
||||
|
||||
api_id_type _api_id;
|
||||
std::weak_ptr<fc::api_connection> _api_connection;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
class generic_api
|
||||
|
|
@ -151,6 +176,9 @@ namespace fc {
|
|||
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 ... Args>
|
||||
std::function<variant(const fc::variants&)> to_generic( const std::function<fc::api_ptr(Args...)>& f )const;
|
||||
|
||||
template<typename R, typename ... Args>
|
||||
std::function<variant(const fc::variants&)> to_generic( const std::function<R(Args...)>& f )const;
|
||||
|
||||
|
|
@ -267,6 +295,15 @@ namespace fc {
|
|||
return con->get_remote_api<ResultInterface>( v.as_uint64() );
|
||||
}
|
||||
|
||||
static fc::api_ptr from_variant(
|
||||
const variant& v,
|
||||
fc::api_ptr* /* used for template deduction */,
|
||||
const std::shared_ptr<fc::api_connection>& con
|
||||
)
|
||||
{
|
||||
return fc::api_ptr( new detail::any_api( v.as_uint64(), con ) );
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static fc::variant convert_callbacks( const std::shared_ptr<fc::api_connection>&, const T& v )
|
||||
{
|
||||
|
|
@ -370,6 +407,24 @@ namespace fc {
|
|||
return variant();
|
||||
};
|
||||
}
|
||||
|
||||
template<typename ... Args>
|
||||
std::function<variant(const fc::variants&)> generic_api::api_visitor::to_generic(
|
||||
const std::function<fc::api_ptr(Args...)>& f )const
|
||||
{
|
||||
auto api_con = _api_con;
|
||||
auto gapi = &api;
|
||||
return [=]( const variants& args ) -> fc::variant {
|
||||
auto con = api_con.lock();
|
||||
FC_ASSERT( con, "not connected" );
|
||||
|
||||
auto api_result = gapi->call_generic( f, args.begin(), args.end() );
|
||||
if( !api_result )
|
||||
return variant();
|
||||
return api_result->register_api( *con );
|
||||
};
|
||||
}
|
||||
|
||||
template<typename R, typename ... Args>
|
||||
std::function<variant(const fc::variants&)> generic_api::api_visitor::to_generic( const std::function<R(Args...)>& f )const
|
||||
{
|
||||
|
|
@ -389,6 +444,40 @@ namespace fc {
|
|||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* It is slightly unclean tight coupling to have this method in the api class.
|
||||
* It breaks encapsulation by requiring an api class method to have a pointer
|
||||
* to an api_connection. The reason this is necessary is we have a goal of being
|
||||
* able to call register_api() on an api<T> through its base class api_base. But
|
||||
* register_api() must know the template parameters!
|
||||
*
|
||||
* The only reasonable way to achieve the goal is to implement register_api()
|
||||
* as a method in api<T> (which obviously knows the template parameter T),
|
||||
* then make the implementation accessible through the base class (by making
|
||||
* it a pure virtual method in the base class which is overridden by the subclass's
|
||||
* implementation).
|
||||
*/
|
||||
template< typename Interface, typename Transform >
|
||||
api_id_type api< Interface, Transform >::register_api( api_connection& conn )const
|
||||
{
|
||||
return conn.register_api( *this );
|
||||
}
|
||||
|
||||
template< typename T >
|
||||
api<T> api_base::as()
|
||||
{
|
||||
// TODO: this method should probably be const (if it is not too hard)
|
||||
api<T>* maybe_requested_type = dynamic_cast< api<T>* >(this);
|
||||
if( maybe_requested_type != nullptr )
|
||||
return *maybe_requested_type;
|
||||
|
||||
detail::any_api* maybe_any = dynamic_cast< detail::any_api* >(this);
|
||||
FC_ASSERT( maybe_any != nullptr );
|
||||
std::shared_ptr< api_connection > api_conn = maybe_any->_api_connection.lock();
|
||||
FC_ASSERT( api_conn );
|
||||
return api_conn->get_remote_api<T>( maybe_any->_api_id );
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
template<typename Signature>
|
||||
template<typename... Args>
|
||||
|
|
|
|||
|
|
@ -11,9 +11,22 @@ http_api_connection::http_api_connection()
|
|||
{
|
||||
_rpc_state.add_method( "call", [this]( const variants& args ) -> variant
|
||||
{
|
||||
// TODO: This logic is duplicated between http_api_connection and websocket_api_connection
|
||||
// it should be consolidated into one place instead of copy-pasted
|
||||
FC_ASSERT( args.size() == 3 && args[2].is_array() );
|
||||
api_id_type api_id;
|
||||
if( args[0].is_string() )
|
||||
{
|
||||
variants subargs;
|
||||
subargs.push_back( args[0] );
|
||||
variant subresult = this->receive_call( 1, "get_api_by_name", subargs );
|
||||
api_id = subresult.as_uint64();
|
||||
}
|
||||
else
|
||||
api_id = args[0].as_uint64();
|
||||
|
||||
return this->receive_call(
|
||||
args[0].as_uint64(),
|
||||
api_id,
|
||||
args[1].as_string(),
|
||||
args[2].get_array() );
|
||||
} );
|
||||
|
|
|
|||
|
|
@ -13,8 +13,19 @@ websocket_api_connection::websocket_api_connection( fc::http::websocket_connecti
|
|||
_rpc_state.add_method( "call", [this]( const variants& args ) -> variant
|
||||
{
|
||||
FC_ASSERT( args.size() == 3 && args[2].is_array() );
|
||||
api_id_type api_id;
|
||||
if( args[0].is_string() )
|
||||
{
|
||||
variants subargs;
|
||||
subargs.push_back( args[0] );
|
||||
variant subresult = this->receive_call( 1, "get_api_by_name", subargs );
|
||||
api_id = subresult.as_uint64();
|
||||
}
|
||||
else
|
||||
api_id = args[0].as_uint64();
|
||||
|
||||
return this->receive_call(
|
||||
args[0].as_uint64(),
|
||||
api_id,
|
||||
args[1].as_string(),
|
||||
args[2].get_array() );
|
||||
} );
|
||||
|
|
|
|||
Loading…
Reference in a new issue