#pragma once #include "mex.h" #include "matrix.h" #include #include #include #include #include namespace std { template using bool_constant = integral_constant; } template using is_basic_type = std::bool_constant::value || std::is_pointer::value || std::is_enum::value>; template struct is_array_type : std::false_type {}; template struct is_array_type> : 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 struct mx_wrapper_fns { static T parse(const mxArray* cell); static mxArray* wrap(T&& var); static void destroy(const mxArray* cell); }; template struct mx_wrapper; // enums are exposed as 64bit integers, using the underlying type to determine signedness template struct mx_wrapper::value>::type> { private: using signed_t = std::integral_constant; using unsigned_t = std::integral_constant; using value_t = typename std::conditional::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::value, int64_t, uint64_t>::type; }; // pointers are cast to uint64_t because matlab doesn't have a concept of pointers template struct mx_wrapper::value>::type> { // static const mxClassID value = mxUINT64_CLASS; using value = std::integral_constant; using type = uint64_t; }; // bools are exposed as matlab's native logical type template <> struct mx_wrapper { // static const mxClassID value = mxLOGICAL_CLASS; using value = std::integral_constant; using type = mxLogical; }; // floating points are exposed as matlab's native double type template struct mx_wrapper::value>::type> { // static const mxClassID value = mxDOUBLE_CLASS; using value = std::integral_constant; using type = double; }; // integral types are exposed like enums template struct mx_wrapper::value>::type> { private: using signed_t = std::integral_constant; using unsigned_t = std::integral_constant; using value_t = typename std::conditional::value, signed_t, unsigned_t>::type; public: // static const mxClassID value = value_t::value; using value = value_t; using type = typename std::conditional::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 { using value = std::integral_constant; using type = uint8_t; }; template <> struct mx_wrapper { using value = std::integral_constant; using type = uint16_t; }; template <> struct mx_wrapper { using value = std::integral_constant; using type = uint32_t; }; template <> struct mx_wrapper { using value = std::integral_constant; using type = uint64_t; }; template <> struct mx_wrapper { using value = std::integral_constant; using type = int8_t; }; template <> struct mx_wrapper { using value = std::integral_constant; using type = int16_t; }; template <> struct mx_wrapper { using value = std::integral_constant; using type = int32_t; }; template <> struct mx_wrapper { using value = std::integral_constant; using type = int64_t; }; // by default non-basic types are wrapped as pointers template struct mx_wrapper::value>::type> : mx_wrapper {}; template 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 struct detector { struct fallback { int to_internal, from_internal, use_cells; }; struct derived : type_traits, fallback {}; template struct checker; typedef char ao1[1]; typedef char ao2[2]; template static ao1& check_to(checker *); template static ao2& check_to(...); template static ao1& check_from(checker *); template static ao2& check_from(...); template static ao1& check_cells(checker *); template static ao2& check_cells(...); // template struct use_cells_t : false_type {}; // template struct use_cells_t(0)) == 2>::type> // : T::use_cells {}; enum { has_to = sizeof(check_to(0)) == 2 }; enum { has_from = sizeof(check_from(0)) == 2 }; // enum { use_cells = use_cells_t::value }; enum { use_cells = sizeof(check_cells(0)) == 2 }; }; template using internal_t = typename type_traits::rs2_internal_t; public: // selected if type_traits::to_internal exists template static typename std::enable_if::has_to, internal_t*>::type to_internal(T&& val) { return type_traits::to_internal(std::move(val)); } // selected if it doesnt template static typename std::enable_if::has_to, internal_t*>::type to_internal(T&& val) { mexLock(); return new internal_t(val); } // selected if type_traits::from_internal exists template static typename std::enable_if::has_from, T>::type from_internal(internal_t* ptr) { return type_traits::from_internal(ptr); } // selected if it doesnt template static typename std::enable_if::has_from, T>::type from_internal(internal_t* ptr) { return T(*ptr); } template using use_cells = std::integral_constant::use_cells>; }; // TODO: try/catch->err msg? template static T parse(const mxArray* cell) { return mx_wrapper_fns::parse(cell); } template static mxArray* wrap(T&& var) { return mx_wrapper_fns::wrap(std::move(var)); }; template static void destroy(const mxArray* cell) { return mx_wrapper_fns::destroy(cell); } template static typename std::enable_if::value, std::vector>::type parse_array(const mxArray* cells); template static typename std::enable_if::value, std::vector>::type parse_array(const mxArray* cells); template static typename std::enable_if::value && !traits_trampoline::use_cells::value, mxArray*>::type wrap_array(const T* var, size_t length); template static typename std::enable_if::value && traits_trampoline::use_cells::value, mxArray*>::type wrap_array(const T* var, size_t length); template static typename std::enable_if::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 struct MatlabParamParser::mx_wrapper_fns::value && !extra_checks::value && !is_array_type::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 class to send more exact types // for frame data arrays using type = typename std::conditional::value, typename std::conditional::value, int64_t, uint64_t>::type, T>::type; using wrapper = mx_wrapper; static T parse(const mxArray* cell) { // obtain pointer to data, cast to proper matlab type auto *p = static_cast(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(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::value, "Trying to destroy basic type. This shouldn't happen."); static_assert(is_basic_type::value, "Non-basic type ended up in basic type's destroy function. How?"); } }; // default for non-basic types (eg classes) template struct MatlabParamParser::mx_wrapper_fns::value && !extra_checks::value && !is_array_type::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::rs2_internal_t; return traits_trampoline::from_internal(mx_wrapper_fns::parse(cell)); } static mxArray* wrap(T&& var) { using internal_t = typename type_traits::rs2_internal_t; return mx_wrapper_fns::wrap(traits_trampoline::to_internal(std::move(var))); } static void destroy(const mxArray* cell) { using internal_t = typename type_traits::rs2_internal_t; // get pointer to the internal type we put on the heap auto ptr = mx_wrapper_fns::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 struct MatlabParamParser::mx_wrapper_fns::value>::type> { static T parse(const mxArray* cell) { return MatlabParamParser::parse_array::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::wrap(const char*&& str) { return mxCreateString(str); } template<> std::string MatlabParamParser::mx_wrapper_fns::parse(const mxArray* cell) { auto str = mxArrayToString(cell); auto ret = std::string(str); mxFree(str); return ret; } template<> mxArray* MatlabParamParser::mx_wrapper_fns::wrap(std::string&& str) { return mx_wrapper_fns::wrap(str.c_str()); } template<> struct MatlabParamParser::mx_wrapper_fns { static std::chrono::nanoseconds parse(const mxArray* cell) { auto ptr = static_cast(mxGetData(cell)); return std::chrono::nanoseconds{ static_cast(*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(mxGetData(cell)); *ptr = dur.count() / 1.e6; // convert to milliseconds, smallest time unit that's easy to work with in matlab return cell; } }; template static typename std::enable_if::value, std::vector>::type MatlabParamParser::parse_array(const mxArray* cells) { using wrapper_t = mx_wrapper; using internal_t = typename type_traits::rs2_internal_t; std::vector ret; auto length = mxGetNumberOfElements(cells); ret.reserve(length); auto ptr = static_cast(mxGetData(cells)); // array of uint64_t's for (int i = 0; i < length; ++i) { ret.push_back(traits_trampoline::from_internal(reinterpret_cast(ptr[i]))); } return ret; } template static typename std::enable_if::value, std::vector>::type MatlabParamParser::parse_array(const mxArray* cells) { using wrapper_t = mx_wrapper; std::vector ret; auto length = mxGetNumberOfElements(cells); ret.reserve(length); auto ptr = static_cast(mxGetData(cells)); // array of uint64_t's for (int i = 0; i < length; ++i) { ret.push_back(ptr[i]); } return ret; } template static typename std::enable_if::value && !MatlabParamParser::traits_trampoline::use_cells::value, mxArray*>::type MatlabParamParser::wrap_array(const T* var, size_t length) { auto cells = mxCreateNumericMatrix(1, length, MatlabParamParser::mx_wrapper::value::value, mxREAL); auto ptr = static_cast::type*>(mxGetData(cells)); for (int x = 0; x < length; ++x) ptr[x] = reinterpret_cast::type>(traits_trampoline::to_internal(T(var[x]))); return cells; } template static typename std::enable_if::value && MatlabParamParser::traits_trampoline::use_cells::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 static typename std::enable_if::value, mxArray*>::type MatlabParamParser::wrap_array(const T* var, size_t length) { auto cells = mxCreateNumericMatrix(1, length, MatlabParamParser::mx_wrapper::value::value, mxREAL); auto ptr = static_cast::type*>(mxGetData(cells)); for (int x = 0; x < length; ++x) ptr[x] = typename mx_wrapper::type(var[x]); return cells; } #include "types.h"