forked from IntelRealSense/librealsense
-
Notifications
You must be signed in to change notification settings - Fork 0
/
MatlabParamParser.h
340 lines (314 loc) · 15.7 KB
/
MatlabParamParser.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
#pragma once
#include "mex.h"
#include "matrix.h"
#include <string>
#include <memory>
#include <vector>
#include <array>
#include <type_traits>
namespace std
{
template <bool B> using bool_constant = integral_constant<bool, B>;
}
template <typename T> using is_basic_type = std::bool_constant<std::is_arithmetic<T>::value || std::is_pointer<T>::value || std::is_enum<T>::value>;
template <typename T> struct is_array_type : std::false_type {};
template <typename T> struct is_array_type<std::vector<T>> : std::true_type { using inner_t = T; };
// TODO: consider using nested impl/detail namespace
namespace MatlabParamParser
{
// in charge of which overloads to use
// helper information for overloaded functions
template <typename T, typename voider = void> struct mx_wrapper_fns
{
static T parse(const mxArray* cell);
static mxArray* wrap(T&& var);
static void destroy(const mxArray* cell);
};
template <typename T, typename = void> struct mx_wrapper;
// enums are exposed as 64bit integers, using the underlying type to determine signedness
template <typename T> struct mx_wrapper<T, typename std::enable_if<std::is_enum<T>::value>::type>
{
private:
using signed_t = std::integral_constant<mxClassID, mxINT64_CLASS>;
using unsigned_t = std::integral_constant<mxClassID, mxUINT64_CLASS>;
using value_t = typename std::conditional<bool(std::is_signed<typename std::underlying_type<T>::type>::value), signed_t, unsigned_t>::type;
public:
// static const mxClassID value = /*value_t::value*/ signed_t::value;
using value = signed_t;
using type = typename std::conditional<std::is_same<value_t, signed_t>::value, int64_t, uint64_t>::type;
};
// pointers are cast to uint64_t because matlab doesn't have a concept of pointers
template <typename T> struct mx_wrapper<T, typename std::enable_if<std::is_pointer<T>::value>::type>
{
// static const mxClassID value = mxUINT64_CLASS;
using value = std::integral_constant<mxClassID, mxUINT64_CLASS>;
using type = uint64_t;
};
// bools are exposed as matlab's native logical type
template <> struct mx_wrapper<bool, void>
{
// static const mxClassID value = mxLOGICAL_CLASS;
using value = std::integral_constant<mxClassID, mxLOGICAL_CLASS>;
using type = mxLogical;
};
// floating points are exposed as matlab's native double type
template <typename T> struct mx_wrapper<T, typename std::enable_if<std::is_floating_point<T>::value>::type>
{
// static const mxClassID value = mxDOUBLE_CLASS;
using value = std::integral_constant<mxClassID, mxDOUBLE_CLASS>;
using type = double;
};
// integral types are exposed like enums
template <typename T> struct mx_wrapper<T, typename std::enable_if<std::is_integral<T>::value>::type>
{
private:
using signed_t = std::integral_constant<mxClassID, mxINT64_CLASS>;
using unsigned_t = std::integral_constant<mxClassID, mxUINT64_CLASS>;
using value_t = typename std::conditional<std::is_signed<T>::value, signed_t, unsigned_t>::type;
public:
// static const mxClassID value = value_t::value;
using value = value_t;
using type = typename std::conditional<std::is_same<value_t, signed_t>::value, int64_t, uint64_t>::type;
};
// uint8_t, uint16_t, uint32_t, uint64_t, int8_t, int16_t, int32_t, and int64_t
// are exposed as the relevant native Matlab type
template <> struct mx_wrapper<uint8_t> {
using value = std::integral_constant<mxClassID, mxUINT8_CLASS>;
using type = uint8_t;
};
template <> struct mx_wrapper<uint16_t> {
using value = std::integral_constant<mxClassID, mxUINT16_CLASS>;
using type = uint16_t;
};
template <> struct mx_wrapper<uint32_t> {
using value = std::integral_constant<mxClassID, mxUINT32_CLASS>;
using type = uint32_t;
};
template <> struct mx_wrapper<uint64_t> {
using value = std::integral_constant<mxClassID, mxUINT64_CLASS>;
using type = uint64_t;
};
template <> struct mx_wrapper<int8_t> {
using value = std::integral_constant<mxClassID, mxINT8_CLASS>;
using type = int8_t;
};
template <> struct mx_wrapper<int16_t> {
using value = std::integral_constant<mxClassID, mxINT16_CLASS>;
using type = int16_t;
};
template <> struct mx_wrapper<int32_t> {
using value = std::integral_constant<mxClassID, mxINT32_CLASS>;
using type = int32_t;
};
template <> struct mx_wrapper<int64_t> {
using value = std::integral_constant<mxClassID, mxINT64_CLASS>;
using type = int64_t;
};
// by default non-basic types are wrapped as pointers
template <typename T> struct mx_wrapper<T, typename std::enable_if<!is_basic_type<T>::value>::type> : mx_wrapper<void*> {};
template <typename T, typename = void> struct type_traits {
// KEEP THESE COMMENTED. They are only here to show the signature
// should you choose to declare these functions
// static rs2_internal_t* to_internal(T&& val);
// static T from_internal(rs2_internal_t* ptr);
};
struct traits_trampoline {
private:
template <typename T> struct detector {
struct fallback { int to_internal, from_internal, use_cells; };
struct derived : type_traits<T>, fallback {};
template <typename U, U> struct checker;
typedef char ao1[1];
typedef char ao2[2];
template <typename U> static ao1& check_to(checker<int fallback::*, &U::to_internal> *);
template <typename U> static ao2& check_to(...);
template <typename U> static ao1& check_from(checker<int fallback::*, &U::from_internal> *);
template <typename U> static ao2& check_from(...);
template <typename U> static ao1& check_cells(checker<int fallback::*, &U::use_cells> *);
template <typename U> static ao2& check_cells(...);
// template <typename, typename = void> struct use_cells_t : false_type {};
// template <typename U> struct use_cells_t<U, typename std::enable_if<sizeof(check_cells<U>(0)) == 2>::type>
// : T::use_cells {};
enum { has_to = sizeof(check_to<derived>(0)) == 2 };
enum { has_from = sizeof(check_from<derived>(0)) == 2 };
// enum { use_cells = use_cells_t<derived>::value };
enum { use_cells = sizeof(check_cells<derived>(0)) == 2 };
};
template <typename T> using internal_t = typename type_traits<T>::rs2_internal_t;
public:
// selected if type_traits<T>::to_internal exists
template <typename T> static typename std::enable_if<detector<T>::has_to, internal_t<T>*>::type
to_internal(T&& val) { return type_traits<T>::to_internal(std::move(val)); }
// selected if it doesnt
template <typename T> static typename std::enable_if<!detector<T>::has_to, internal_t<T>*>::type
to_internal(T&& val) { mexLock(); return new internal_t<T>(val); }
// selected if type_traits<T>::from_internal exists
template <typename T> static typename std::enable_if<detector<T>::has_from, T>::type
from_internal(internal_t<T>* ptr) { return type_traits<T>::from_internal(ptr); }
// selected if it doesnt
template <typename T> static typename std::enable_if<!detector<T>::has_from, T>::type
from_internal(internal_t<T>* ptr) { return T(*ptr); }
template <typename T> using use_cells = std::integral_constant<bool, detector<T>::use_cells>;
};
// TODO: try/catch->err msg?
template <typename T> static T parse(const mxArray* cell) { return mx_wrapper_fns<T>::parse(cell); }
template <typename T> static mxArray* wrap(T&& var) { return mx_wrapper_fns<T>::wrap(std::move(var)); };
template <typename T> static void destroy(const mxArray* cell) { return mx_wrapper_fns<T>::destroy(cell); }
template <typename T> static typename std::enable_if<!is_basic_type<T>::value, std::vector<T>>::type parse_array(const mxArray* cells);
template <typename T> static typename std::enable_if<is_basic_type<T>::value, std::vector<T>>::type parse_array(const mxArray* cells);
template <typename T> static typename std::enable_if<!is_basic_type<T>::value && !traits_trampoline::use_cells<T>::value, mxArray*>::type wrap_array(const T* var, size_t length);
template <typename T> static typename std::enable_if<!is_basic_type<T>::value && traits_trampoline::use_cells<T>::value, mxArray*>::type wrap_array(const T* var, size_t length);
template <typename T> static typename std::enable_if<is_basic_type<T>::value, mxArray*>::type wrap_array(const T* var, size_t length);
};
#include "rs2_type_traits.h"
// for basic types (aka arithmetic, pointer, and enum types)
template <typename T> struct MatlabParamParser::mx_wrapper_fns<T, typename std::enable_if<is_basic_type<T>::value && !extra_checks<T>::value && !is_array_type<T>::value>::type>
{
// Use full width types when converting integers.
// Always using full width makes some things easier, and doing it this way
// still allows us to use the mx_wrapper<T> class to send more exact types
// for frame data arrays
using type = typename std::conditional<std::is_integral<T>::value, typename std::conditional<std::is_signed<T>::value, int64_t, uint64_t>::type, T>::type;
using wrapper = mx_wrapper<type>;
static T parse(const mxArray* cell)
{
// obtain pointer to data, cast to proper matlab type
auto *p = static_cast<typename wrapper::type*>(mxGetData(cell));
// dereference pointer and cast to correct C++ type
return T(*p);
}
static mxArray* wrap(T&& var)
{
// request 1x1 matlab matrix of correct type
mxArray* cell = mxCreateNumericMatrix(1, 1, wrapper::value::value, mxREAL);
// access matrix's data as pointer to correct C++ type
auto *outp = static_cast<typename wrapper::type*>(mxGetData(cell));
// cast object to correct C++ type and then store it in the matrix
*outp = typename wrapper::type(var);
return cell;
}
static void destroy(const mxArray* cell)
{
static_assert(!is_basic_type<T>::value, "Trying to destroy basic type. This shouldn't happen.");
static_assert(is_basic_type<T>::value, "Non-basic type ended up in basic type's destroy function. How?");
}
};
// default for non-basic types (eg classes)
template<typename T> struct MatlabParamParser::mx_wrapper_fns<T, typename std::enable_if<!is_basic_type<T>::value && !extra_checks<T>::value && !is_array_type<T>::value>::type>
{
// librealsense types are sent to matlab using a pointer to the internal type.
// to get it back from matlab we first parse that pointer and then reconstruct the C++ wrapper
static T parse(const mxArray* cell)
{
using internal_t = typename type_traits<T>::rs2_internal_t;
return traits_trampoline::from_internal<T>(mx_wrapper_fns<internal_t*>::parse(cell));
}
static mxArray* wrap(T&& var)
{
using internal_t = typename type_traits<T>::rs2_internal_t;
return mx_wrapper_fns<internal_t*>::wrap(traits_trampoline::to_internal<T>(std::move(var)));
}
static void destroy(const mxArray* cell)
{
using internal_t = typename type_traits<T>::rs2_internal_t;
// get pointer to the internal type we put on the heap
auto ptr = mx_wrapper_fns<internal_t*>::parse(cell);
delete ptr;
// signal to matlab that the wrapper owns one fewer objects
mexUnlock();
}
};
// simple helper overload to refer std::array and std::vector to wrap_array
template<typename T> struct MatlabParamParser::mx_wrapper_fns<T, typename std::enable_if<is_array_type<T>::value>::type>
{
static T parse(const mxArray* cell)
{
return MatlabParamParser::parse_array<typename is_array_type<T>::inner_t>(cell);
}
static mxArray* wrap(T&& var)
{
return MatlabParamParser::wrap_array(var.data(), var.size());
}
};
// overload for wrapping C-strings. TODO: do we need special parsing too?
template<> mxArray* MatlabParamParser::mx_wrapper_fns<const char *>::wrap(const char*&& str)
{
return mxCreateString(str);
}
template<> std::string MatlabParamParser::mx_wrapper_fns<std::string>::parse(const mxArray* cell)
{
auto str = mxArrayToString(cell);
auto ret = std::string(str);
mxFree(str);
return ret;
}
template<> mxArray* MatlabParamParser::mx_wrapper_fns<std::string>::wrap(std::string&& str)
{
return mx_wrapper_fns<const char*>::wrap(str.c_str());
}
template<> struct MatlabParamParser::mx_wrapper_fns<std::chrono::nanoseconds>
{
static std::chrono::nanoseconds parse(const mxArray* cell)
{
auto ptr = static_cast<double*>(mxGetData(cell));
return std::chrono::nanoseconds{ static_cast<long long>(*ptr * 1e6) }; // convert from milliseconds, smallest time unit that's easy to work with in matlab
}
static mxArray* wrap(std::chrono::nanoseconds&& dur)
{
auto cell = mxCreateNumericMatrix(1, 1, mxDOUBLE_CLASS, mxREAL);
auto ptr = static_cast<double*>(mxGetData(cell));
*ptr = dur.count() / 1.e6; // convert to milliseconds, smallest time unit that's easy to work with in matlab
return cell;
}
};
template <typename T> static typename std::enable_if<!is_basic_type<T>::value, std::vector<T>>::type MatlabParamParser::parse_array(const mxArray* cells)
{
using wrapper_t = mx_wrapper<T>;
using internal_t = typename type_traits<T>::rs2_internal_t;
std::vector<T> ret;
auto length = mxGetNumberOfElements(cells);
ret.reserve(length);
auto ptr = static_cast<typename wrapper_t::type*>(mxGetData(cells)); // array of uint64_t's
for (int i = 0; i < length; ++i) {
ret.push_back(traits_trampoline::from_internal<T>(reinterpret_cast<internal_t*>(ptr[i])));
}
return ret;
}
template <typename T> static typename std::enable_if<is_basic_type<T>::value, std::vector<T>>::type MatlabParamParser::parse_array(const mxArray* cells)
{
using wrapper_t = mx_wrapper<T>;
std::vector<T> ret;
auto length = mxGetNumberOfElements(cells);
ret.reserve(length);
auto ptr = static_cast<typename wrapper_t::type*>(mxGetData(cells)); // array of uint64_t's
for (int i = 0; i < length; ++i) {
ret.push_back(ptr[i]);
}
return ret;
}
template <typename T> static typename std::enable_if<!is_basic_type<T>::value && !MatlabParamParser::traits_trampoline::use_cells<T>::value, mxArray*>::type
MatlabParamParser::wrap_array(const T* var, size_t length)
{
auto cells = mxCreateNumericMatrix(1, length, MatlabParamParser::mx_wrapper<T>::value::value, mxREAL);
auto ptr = static_cast<typename mx_wrapper<T>::type*>(mxGetData(cells));
for (int x = 0; x < length; ++x)
ptr[x] = reinterpret_cast<typename mx_wrapper<T>::type>(traits_trampoline::to_internal<T>(T(var[x])));
return cells;
}
template <typename T> static typename std::enable_if<!is_basic_type<T>::value && MatlabParamParser::traits_trampoline::use_cells<T>::value, mxArray*>::type
MatlabParamParser::wrap_array(const T* var, size_t length)
{
auto cells = mxCreateCellMatrix(1, length);
for (int x = 0; x < length; ++x)
mxSetCell(cells, x, wrap(T(var[x])));
return cells;
}
template <typename T> static typename std::enable_if<is_basic_type<T>::value, mxArray*>::type MatlabParamParser::wrap_array(const T* var, size_t length)
{
auto cells = mxCreateNumericMatrix(1, length, MatlabParamParser::mx_wrapper<T>::value::value, mxREAL);
auto ptr = static_cast<typename mx_wrapper<T>::type*>(mxGetData(cells));
for (int x = 0; x < length; ++x)
ptr[x] = typename mx_wrapper<T>::type(var[x]);
return cells;
}
#include "types.h"