7
7
#include < stdexcept>
8
8
#include < string>
9
9
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"
11
14
15
+ #include " utils.hpp"
12
16
#include " wrapper.hpp"
13
17
14
18
namespace testml {
@@ -19,60 +23,85 @@ namespace testml {
19
23
using wrapper::cook;
20
24
using wrapper::uncook;
21
25
26
+ namespace ct = boost::callable_traits;
27
+
22
28
// we need this details class so that we can have a non-templated value
23
29
// stored in the Bridge _fns map.
24
30
struct FnHolder {
25
31
virtual json call (std::vector<json> const &) = 0;
26
32
};
27
33
28
34
// the implementation of a FnHolder, which keeps the types around
29
- template <typename Ret , typename ... Arg >
35
+ template <typename BridgeT , typename Fn >
30
36
class FnHolderImpl : public FnHolder {
31
- using Fn = std::function<Ret(Arg...)>;
32
37
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;
33
45
34
46
// type of the N-th argument that the stored function takes
35
47
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;
37
49
38
50
// 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)
39
61
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 {
41
64
return _fn (uncook<ArgType<I>>(args[I])...);
42
65
}
43
66
44
67
public:
45
- FnHolderImpl (Fn fn) : _fn{std::move (fn)} {
68
+ FnHolderImpl (BridgeT* bridge, Fn fn)
69
+ : _fn{std::move (fn)},
70
+ _bridge{bridge} {
46
71
}
47
72
48
73
// check arity and call the function using our little helper, before wrapping it back to json
49
74
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 ()) + " ." );
52
77
}
53
78
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>{}));
56
80
}
57
81
58
82
};
59
83
60
84
}
61
85
62
86
class Bridge {
87
+ // store a wrapper FnHolder in the map, with FnHolderImpl to keep the correct types around and do FFI correctly
63
88
std::unordered_map<std::string, std::unique_ptr<details::FnHolder>> _fns;
64
89
65
90
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));
71
98
}
72
99
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));
76
105
}
77
106
78
107
nlohmann::json call (std::string const & name, std::vector<nlohmann::json> const & args);
0 commit comments