77#include < stdexcept>
88#include < string>
99
10- #include " ../../ext/nlohmann/json.hpp"
10+ #include " boost/callable_traits/args.hpp"
11+ #include " boost/callable_traits/class_of.hpp"
12+ #include " boost/callable_traits/return_type.hpp"
13+ #include " nlohmann/json.hpp"
1114
15+ #include " utils.hpp"
1216#include " wrapper.hpp"
1317
1418namespace testml {
@@ -19,60 +23,85 @@ namespace testml {
1923 using wrapper::cook;
2024 using wrapper::uncook;
2125
26+ namespace ct = boost::callable_traits;
27+
2228 // we need this details class so that we can have a non-templated value
2329 // stored in the Bridge _fns map.
2430 struct FnHolder {
2531 virtual json call (std::vector<json> const &) = 0;
2632 };
2733
2834 // the implementation of a FnHolder, which keeps the types around
29- template <typename Ret , typename ... Arg >
35+ template <typename BridgeT , typename Fn >
3036 class FnHolderImpl : public FnHolder {
31- using Fn = std::function<Ret(Arg...)>;
3237 Fn _fn;
38+ BridgeT* _bridge;
39+ static constexpr bool _is_pmf = std::is_member_function_pointer<Fn>::value;
40+ using RawArg = ct::args_t <Fn>;
41+ // in case of a PMF, remove the class type from the argument list
42+ using Arg = std::conditional_t <_is_pmf, typename utils::remove_first_type<RawArg>::type, RawArg>;
43+ using Ret = ct::return_type_t <Fn>;
44+ static constexpr std::size_t _num_args = std::tuple_size<Arg>::value;
3345
3446 // type of the N-th argument that the stored function takes
3547 template <std::size_t I>
36- using ArgType = typename std::tuple_element<I, std::tuple< Arg...> >::type;
48+ using ArgType = typename std::tuple_element<I, Arg>::type;
3749
3850 // uncook each argument to its expected type, and call the function
51+ // we do SFINAE in the return type, using comma+sizeof() to get a dependance on I.
52+
53+ // PMF case
54+ template <std::size_t ... I>
55+ auto call_impl (std::vector<json> const & args, std::index_sequence<I...>)
56+ -> typename std::enable_if<(sizeof ...(I), _is_pmf), Ret>::type {
57+ return (_bridge->*_fn)(uncook<ArgType<I>>(args[I])...);
58+ }
59+
60+ // non-PMF case (BridgeT = nullptr_t)
3961 template <std::size_t ... I>
40- Ret call_impl (std::vector<json> const & args, std::index_sequence<I...>) {
62+ auto call_impl (std::vector<json> const & args, std::index_sequence<I...>)
63+ -> typename std::enable_if<(sizeof ...(I), !_is_pmf), Ret>::type {
4164 return _fn (uncook<ArgType<I>>(args[I])...);
4265 }
4366
4467 public:
45- FnHolderImpl (Fn fn) : _fn{std::move (fn)} {
68+ FnHolderImpl (BridgeT* bridge, Fn fn)
69+ : _fn{std::move (fn)},
70+ _bridge{bridge} {
4671 }
4772
4873 // check arity and call the function using our little helper, before wrapping it back to json
4974 json call (std::vector<json> const & args) override {
50- if (args.size () != sizeof ...(Arg) ) {
51- throw std::runtime_error (" Bridge method call with wrong arity, expected " + std::to_string (sizeof ...(Arg) ) + " , got " + std::to_string (args.size ()) + " ." );
75+ if (args.size () != _num_args ) {
76+ throw std::runtime_error (" Bridge method call with wrong arity, expected " + std::to_string (_num_args ) + " , got " + std::to_string (args.size ()) + " ." );
5277 }
5378
54- // generate an index_sequence so that the call_impl() can spread on each argument
55- return cook (call_impl (args, std::make_index_sequence<sizeof ...(Arg)>{}));
79+ return cook (call_impl (args, std::make_index_sequence<_num_args>{}));
5680 }
5781
5882 };
5983
6084 }
6185
6286 class Bridge {
87+ // store a wrapper FnHolder in the map, with FnHolderImpl to keep the correct types around and do FFI correctly
6388 std::unordered_map<std::string, std::unique_ptr<details::FnHolder>> _fns;
6489
6590 public:
66- template <typename Ret, typename ... Arg>
67- void bind (std::string const & name, std::function<Ret(Arg...)> fn) {
68- // store a wrapper FnHolder in the map, with FnHolderImpl to keep the correct types around and do FFI correctly
69- using HolderType = details::FnHolderImpl<Ret, Arg...>;
70- _fns[name] = std::make_unique<HolderType>(std::move (fn));
91+ template <typename BridgeT, typename Fn>
92+ auto bind (std::string const & name, BridgeT* obj, Fn fn)
93+ -> typename std::enable_if<std::is_member_function_pointer<Fn>::value, void>::type {
94+ static_assert (std::is_same<details::ct::class_of_t <Fn>, BridgeT>::value, " Bridge subclass must pass itself" );
95+
96+ using HolderType = details::FnHolderImpl<BridgeT, Fn>;
97+ _fns[name] = std::make_unique<HolderType>(obj, std::move (fn));
7198 }
7299
73- template <typename Ret, typename ... Arg>
74- void bind (std::string const & name, Ret(*fn)(Arg...)) {
75- bind (name, std::function<Ret (Arg...)>(fn));
100+ template <typename Fn>
101+ auto bind (std::string const & name, Fn fn)
102+ -> typename std::enable_if<!std::is_member_function_pointer<Fn>::value, void>::type {
103+ using HolderType = details::FnHolderImpl<std::nullptr_t , Fn>;
104+ _fns[name] = std::make_unique<HolderType>(nullptr , std::move (fn));
76105 }
77106
78107 nlohmann::json call (std::string const & name, std::vector<nlohmann::json> const & args);
0 commit comments