Updates from BitShares FC #22

Closed
nathanielhourt wants to merge 693 commits from dapp-support into latest-fc
2 changed files with 78 additions and 12 deletions
Showing only changes of commit 24eff3ab6d - Show all commits

View file

@ -3,6 +3,10 @@
#include <functional>
#include <boost/any.hpp>
#include <boost/config.hpp>
#include <boost/hana/tuple.hpp>
#include <boost/hana/take_back.hpp>
#include <boost/hana/transform.hpp>
#include <boost/hana/concat.hpp>
// ms visual c++ (as of 2013) doesn't accept the standard syntax for calling a
// templated member function (foo->template bar();)
@ -13,13 +17,67 @@
#endif
namespace fc {
struct identity_member {
namespace detail {
namespace hana = boost::hana;
/// This metafunction determines whether its template argument is an instantiation of fc::optional
template<typename T> struct is_optional : public std::false_type {};
template<typename T> struct is_optional<fc::optional<T>> : public std::true_type {};
/// This metafunction determines whether all of its template arguments are instantiations of fc::optional
template<typename... Ts> struct all_optionals;
template<> struct all_optionals<> : public std::true_type {};
template<typename T, typename... Ts> struct all_optionals<T, Ts...> : public std::false_type {};
template<typename T, typename... Ts> struct all_optionals<fc::optional<T>, Ts...> : public all_optionals<Ts...> {};
/// A wrapper of std::function allowing callers to omit the last several arguments if those arguments are
/// fc::optional types. i.e. given a function taking (int, double, bool, fc::optional<string>, fc::optional<char>),
/// whereas normally the last two arguments must be provided, this template allows them to be omitted.
/// Note that this only applies to trailing optional arguments, i.e. given a callable taking
/// (fc::optional<int>, int, fc::optional<int>), only the last argument can be omitted.
template<typename R, typename... Parameters>
struct optionals_callable : public std::function<R(Parameters...)> {
using std::function<R(Parameters...)>::operator();
/// Overload the function call operator, enabled if the caller provides fewer arguments than there are parameters.
/// Pads out the provided arguments with default-constructed optionals, checking that they are indeed optional types
template<class... Args>
std::enable_if_t<sizeof...(Args) < sizeof...(Parameters), R> operator()(Args... args) {
auto arguments = hana::make_tuple(std::forward<Args>(args)...);
// Get the parameter types corresponding to the omitted arguments
auto optional_types = hana::take_back(hana::tuple_t<Parameters...>,
hana::size_c<sizeof...(Parameters) - sizeof...(Args)>);
// Transform the types into default-constructed values, checking that they are optional types
auto optional_values = hana::transform(optional_types, [](auto hanatype) {
using type = std::decay_t<typename decltype(hanatype)::type>;
static_assert(is_optional<type>::value,
"All omitted arguments must correspond to optional parameters.");
return type();
});
auto padded_arguments = hana::concat(arguments, optional_values);
return hana::unpack(padded_arguments,
[this](auto... params) { return (*this)(std::forward<decltype(params)>(params)...); });
}
};
}
// This is no longer used and probably no longer can be used without generalizing the infrastructure around it, but I
// kept it because it is informative.
// 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...) );
// template<typename R, typename C, typename P, typename... Args>
// static std::function<R(Args...)> functor( P&& p, R (C::*mem_func)(Args...)const );
// };
/// Used as the Transform template parameter for APIs, this type has two main purposes: first, it reads the argument
/// list and return type of a method into template parameters; and second, it uses those types in conjunction with the
/// optionals_callable template above to create a function pointer which supports optional arguments.
struct identity_member_with_optionals {
template<typename R, typename C, typename P, typename... Args>
static std::function<R(Args...)> functor( P&& p, R (C::*mem_func)(Args...) );
static detail::optionals_callable<R, Args...> functor( P&& p, R (C::*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 );
static detail::optionals_callable<R, Args...> functor( P&& p, R (C::*mem_func)(Args...)const );
};
template< typename Interface, typename Transform >
struct vtable : public std::enable_shared_from_this<vtable<Interface,Transform>>
{ private: vtable(); };
@ -57,13 +115,13 @@ namespace fc {
// defined in api_connection.hpp
template< typename T >
api<T, identity_member> as();
api<T, identity_member_with_optionals> as();
};
typedef std::shared_ptr< api_base > api_ptr;
class api_connection;
template<typename Interface, typename Transform = identity_member >
template<typename Interface, typename Transform = identity_member_with_optionals >
class api : public api_base {
public:
typedef vtable<Interface,Transform> vtable_type;

View file

@ -33,7 +33,9 @@ namespace fc {
template<typename R, typename Arg0, typename ... Args>
std::function<R(Args...)> bind_first_arg( const std::function<R(Arg0,Args...)>& f, Arg0 a0 )
{
return [=]( Args... args ) { return f( a0, args... ); };
// Capture a0 this way because of a {compiler,fc,???} bug that causes optional<bool>() to be incorrectly
// captured as optional<bool>(false).
return [f, a0 = std::decay_t<Arg0>(a0)]( 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, uint32_t max_depth = 1 )
@ -44,9 +46,11 @@ namespace fc {
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, uint32_t max_depth )
{
FC_ASSERT( a0 != e );
bool optional_args = all_optionals<std::decay_t<Arg0>, std::decay_t<Args>...>::value;
FC_ASSERT( a0 != e || optional_args );
FC_ASSERT( max_depth > 0, "Recursion depth exceeded!" );
return call_generic<R,Args...>( bind_first_arg<R,Arg0,Args...>( f, a0->as< typename std::decay<Arg0>::type >( max_depth - 1 ) ), a0+1, e, max_depth - 1 );
auto arg = (a0 == e)? std::decay_t<Arg0>() : a0->as<std::decay_t<Arg0>>(max_depth - 1);
return call_generic<R,Args...>( bind_first_arg<R,Arg0,Args...>( f, arg ), a0+1, e, max_depth - 1 );
}
template<typename R, typename ... Args>
@ -137,7 +141,9 @@ namespace fc {
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... ); };
// Capture a0 this way because of a {compiler,fc,???} bug that causes optional<bool>() to be incorrectly
// captured as optional<bool>(false).
return [f, a0 = std::decay_t<Arg0>(a0)]( Args... args ) { return f( a0, args... ); };
}
template<typename R>
@ -172,9 +178,11 @@ namespace fc {
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, uint32_t max_depth )
{
FC_ASSERT( a0 != e, "too few arguments passed to method" );
bool optional_args = detail::all_optionals<std::decay_t<Arg0>, std::decay_t<Args>...>::value;
FC_ASSERT( a0 != e || optional_args, "too few arguments passed to method" );
FC_ASSERT( max_depth > 0, "Recursion depth exceeded!" );
return call_generic<R,Args...>( this->bind_first_arg<R,Arg0,Args...>( f, a0->as< typename std::decay<Arg0>::type >( max_depth - 1 ) ), a0+1, e, max_depth - 1 );
auto arg = (a0 == e)? std::decay_t<Arg0>() : a0->as<std::decay_t<Arg0>>(max_depth - 1);
return call_generic<R,Args...>( this->bind_first_arg<R,Arg0,Args...>( f, arg ), a0+1, e, max_depth - 1 );
}
struct api_visitor