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