Drake
mexify.h
Go to the documentation of this file.
1 #pragma once
2 
3 //
4 // Created by Twan Koolen on 8/22/15.
5 //
6 
7 #include <functional>
8 #include <sstream>
9 #include <stdexcept>
11 
12 #if defined(_MSC_VER) && (_MSC_VER < 1900)
13 #define NOEXCEPT
14 #else
15 #define NOEXCEPT noexcept
16 #endif
17 
57 /*
58  * remove reference because in general you don't want to return a reference from
59  * fromMex
60  * remove const to have fewer template specializations
61  */
62 template <typename T>
63 using FromMexType =
64  typename std::remove_cv<typename std::remove_reference<T>::type>::type;
65 
71 // base case with return
72 template <typename R>
73 void mexCallFunctionUnsafe(std::function<R(void)> func, int nlhs,
74  mxArray *plhs[], int nrhs, const mxArray *prhs[]) {
75  // call function and convert to mxArray
76  toMex(func(), plhs, nlhs);
77 }
78 
79 // base case without return
80 template <>
81 void mexCallFunctionUnsafe(std::function<void(void)> func, int nlhs,
82  mxArray *plhs[], int nrhs, const mxArray *prhs[]) {
83  func();
84 }
85 
86 // recursive case
87 template <typename R, typename Arg0, typename... Args>
88 void mexCallFunctionUnsafe(std::function<R(Arg0, Args...)> func, int nlhs,
89  mxArray *plhs[], int nrhs, const mxArray *prhs[]) {
90  // bind the first argument
91  auto partially_applied = [&](Args &&... tail) {
92  return std::move(func)(
93  fromMexUnsafe(prhs[0], static_cast<FromMexType<Arg0> *>(nullptr)),
94  std::forward<Args>(tail)...);
95  };
96 
97  // recursively call mexCallFunctionUnsafe with partially applied function
98  return mexCallFunctionUnsafe(std::function<R(Args...)>{partially_applied},
99  nlhs, plhs, nrhs - 1, &prhs[1]);
100 }
101 
108 // base case
109 bool areArgumentsConvertibleFromMex(int nrhs, const mxArray *prhs[],
110  int arg_num, std::ostream *log) NOEXCEPT {
111  return true; // nothing to convert, all done
112 }
113 
114 // recursive case
115 template <typename Arg0, typename... Args>
116 bool areArgumentsConvertibleFromMex(int nrhs, const mxArray *prhs[],
117  int arg_num, std::ostream *log, Arg0 *,
118  Args *... tail) NOEXCEPT {
119  if (arg_num == 0) {
120  // check number of input arguments when starting to process arguments
121  const int expected_num_args = sizeof...(Args) + 1; // don't forget Arg0
122  if (nrhs != expected_num_args) {
123  if (log)
124  *log << "Expected " << expected_num_args << " arguments, but got "
125  << nrhs << ".";
126  return false;
127  }
128  }
129 
130  if (!isConvertibleFromMex(prhs[0], (FromMexType<Arg0> *)nullptr, log)) {
131  if (log)
132  *log << std::endl
133  << "Error occurred while checking argument " << arg_num << " of "
134  << nrhs << ".";
135  return false;
136  }
137 
138  return areArgumentsConvertibleFromMex(nrhs, &prhs[1], arg_num + 1, log,
139  tail...);
140 }
141 
148 template <typename R, typename... Args>
149 bool mexCallFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[],
150  bool throw_on_error, std::function<R(Args...)> func) {
152  nrhs, prhs, 0, nullptr,
153  (static_cast<FromMexType<Args> *>(nullptr))...)) {
154  mexCallFunctionUnsafe(func, nlhs, plhs, nrhs, prhs);
155  return true;
156  } else if (throw_on_error) {
157  std::ostringstream log;
158  log << "mexCallFunction: unable to convert mex arguments:" << std::endl;
160  nrhs, prhs, 0, &log, (static_cast<FromMexType<Args> *>(nullptr))...);
161  throw std::runtime_error(log.str());
162  }
163  return false;
164 }
165 
171 // base case
172 void collectMexTryCallFunctionsErrorDiagnostics(int nrhs, const mxArray *prhs[],
173  std::ostream &log,
174  int function_num) {
175  // empty
176 }
177 
178 // recursive case
179 template <typename R0, typename... Args0, typename... Funcs>
181  int nrhs, const mxArray *prhs[], std::ostream &log, int function_num,
182  std::function<R0(Args0...)> function0, Funcs... functions) {
183  log << std::endl
184  << std::endl
185  << "Trying function number " << function_num << std::endl;
187  nrhs, prhs, 0, &log, (static_cast<FromMexType<Args0> *>(nullptr))...);
188  collectMexTryCallFunctionsErrorDiagnostics(nrhs, prhs, log, function_num + 1,
189  functions...);
190 }
191 
197 // base case
198 bool mexTryToCallFunctions(int nlhs, mxArray *plhs[], int nrhs,
199  const mxArray *prhs[], bool throw_on_error) {
200  return false; // no functions to try, so return failure
201 }
202 
203 // recursive case
204 template <typename Func0, typename... Funcs>
205 bool mexTryToCallFunctions(int nlhs, mxArray *plhs[], int nrhs,
206  const mxArray *prhs[], bool throw_on_error,
207  Func0 function0, Funcs... functions) {
208  bool success = mexCallFunction(nlhs, plhs, nrhs, prhs, false,
209  function0); // try the first one
210  if (!success) { // recurse to try the other options
211  success = mexTryToCallFunctions(nlhs, plhs, nrhs, prhs, false,
212  functions...); // don't throw immediately,
213  // wait until all options
214  // exhausted
215  if (throw_on_error && !success) {
216  std::ostringstream log;
217  log << "mexTryToCallFunctions: failed to find a function to call after "
218  "trying " << sizeof...(Funcs) + 1 << " options. Errors:";
219  collectMexTryCallFunctionsErrorDiagnostics(nrhs, prhs, log, 1, function0,
220  functions...);
221  throw std::runtime_error(log.str());
222  }
223  }
224  return success;
225 }
226 
233 template <typename T>
234 auto fromMex(const mxArray *source, T *trigger_type)
235  -> decltype(fromMexUnsafe(source, trigger_type)) {
236  if (isConvertibleFromMex(source, trigger_type, nullptr)) {
237  return fromMexUnsafe(source, trigger_type);
238  } else {
239  std::ostringstream log;
240  isConvertibleFromMex(source, trigger_type, &log);
241  throw std::runtime_error(log.str());
242  }
243 }
void mexCallFunctionUnsafe(std::function< R(void)> func, int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[])
mexCallFunctionUnsafe Converts arguments and calls function without checking whether arguments are co...
Definition: mexify.h:73
bool mexTryToCallFunctions(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[], bool throw_on_error)
mexTryToCallFunctions keep trying functions in order until one is called without error.
Definition: mexify.h:198
FunctionalForm log(FunctionalForm const &x)
Definition: functional_form.cc:198
bool areArgumentsConvertibleFromMex(int nrhs, const mxArray *prhs[], int arg_num, std::ostream *log) NOEXCEPT
areArgumentsConvertibleFromMex Checks whether the Matlab input arguments match the function signature...
Definition: mexify.h:109
RigidBodyTree & fromMexUnsafe(const mxArray *source, RigidBodyTree *)
Definition: rigidBodyTreeMexConversions.h:55
bool isConvertibleFromMex(const mxArray *source, RigidBodyTree *ptr, std::ostream *log) NOEXCEPT
fromMex specializations
Definition: rigidBodyTreeMexConversions.h:50
void toMex(const KinematicPath &path, mxArray *dest[], int nlhs)
toMex specializations
Definition: rigidBodyTreeMexConversions.h:90
typename std::remove_cv< typename std::remove_reference< T >::type >::type FromMexType
Functions that make it easy to create a mex file that calls a std::function.
Definition: mexify.h:64
auto fromMex(const mxArray *source, T *trigger_type) -> decltype(fromMexUnsafe(source, trigger_type))
fromMex For if you only want to convert a single argument.
Definition: mexify.h:234
bool mexCallFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[], bool throw_on_error, std::function< R(Args...)> func)
mexCallFunction checks whether arguments are convertible, and then calls mexCallFunctionUnsafe.
Definition: mexify.h:149
#define NOEXCEPT
Definition: mexify.h:15
void collectMexTryCallFunctionsErrorDiagnostics(int nrhs, const mxArray *prhs[], std::ostream &log, int function_num)
collectMexTryCallFunctionsErrorDiagnostics write diagnostic information to log; called after mexTryTo...
Definition: mexify.h:172