/* Copyright 2016-2020 The MathWorks, Inc. */ #ifndef MATLAB_EXTDATA_REFERENCE_HELPERS_HPP #define MATLAB_EXTDATA_REFERENCE_HELPERS_HPP #include "publish_util.hpp" #include "FunctionType.hpp" #include "StringHelpers.hpp" #include "exception_interface.hpp" #include "ExceptionHelpers.hpp" #include "FunctionType.hpp" #include <stdexcept> #include <iostream> #include <codecvt> #include <locale> namespace matlab { namespace data { template <typename T> class Reference; namespace impl { class ObjectImpl; } namespace detail { template <typename T, typename U> inline typename std::enable_if<std::is_base_of<Array, U>::value && !std::is_same<Struct, T>::value>::type validateReference(ReferenceImpl* impl, bool unshare) { typedef int(*TypedArrayReferenceValidateTypeFcnPtr)(ReferenceImpl*, int, bool); static const TypedArrayReferenceValidateTypeFcnPtr fcn = resolveFunction<TypedArrayReferenceValidateTypeFcnPtr> (FunctionType::TYPED_ARRAY_REFERENCE_VALIDATE_TYPE); throwIfError(fcn(impl, static_cast<int>(U::type), false)); } template <typename T, typename U> inline typename std::enable_if<std::is_base_of<Array, U>::value && std::is_same<Struct, T>::value>::type validateReference(ReferenceImpl* impl, bool unshare) { throwIfError(static_cast<int>(ExceptionType::InvalidDataType)); } template <typename T, typename U> inline typename std::enable_if<!std::is_base_of<Array, U>::value>::type validateReference(ReferenceImpl* impl, bool unshare) { static_assert(std::is_same<T, U>::value, "Reference type must match array type"); typedef int(*ReferenceValidateIndexFcnPtr)(ReferenceImpl*); static const ReferenceValidateIndexFcnPtr fcn = resolveFunction<ReferenceValidateIndexFcnPtr> (FunctionType::REFERENCE_VALIDATE_INDEX); throwIfError(fcn(impl)); } template <typename T> inline typename std::enable_if<std::is_base_of<Array, T>::value>::type validateUntypedReference(ReferenceImpl* impl) { typedef int(*TypedArrayReferenceValidateTypeFcnPtr)(ReferenceImpl*, int, bool); static const TypedArrayReferenceValidateTypeFcnPtr fcn = resolveFunction<TypedArrayReferenceValidateTypeFcnPtr> (FunctionType::TYPED_ARRAY_REFERENCE_VALIDATE_TYPE); throwIfError(fcn(impl, static_cast<int>(T::type), false)); } template <typename T> inline typename std::enable_if<!std::is_base_of<Array, T>::value>::type validateUntypedReference(ReferenceImpl* impl) { typedef int(*ReferenceValidateTypeFcnPtr)(ReferenceImpl*, int); static const ReferenceValidateTypeFcnPtr fcn = resolveFunction<ReferenceValidateTypeFcnPtr> (FunctionType::REFERENCE_VALIDATE_TYPE); throwIfError(fcn(impl, static_cast<int>(GetArrayType<T>::type))); } template <typename T> inline typename std::enable_if<!std::is_base_of<Array, T>::value>::type validateUntypedArrayReference(ReferenceImpl* impl) { typedef int(*TypedArrayReferenceValidateTypeFcnPtr)(ReferenceImpl*, int, bool); static const TypedArrayReferenceValidateTypeFcnPtr fcn = resolveFunction<TypedArrayReferenceValidateTypeFcnPtr> (FunctionType::TYPED_ARRAY_REFERENCE_VALIDATE_TYPE); throwIfError(fcn(impl, static_cast<int>(GetArrayType<T>::type), false)); } template <typename T> inline typename std::enable_if<std::is_base_of<Array, T>::value>::type validateUntypedArrayReference(ReferenceImpl* impl) { typedef int(*TypedArrayReferenceValidateTypeFcnPtr)(ReferenceImpl*, int, bool); static const TypedArrayReferenceValidateTypeFcnPtr fcn = resolveFunction<TypedArrayReferenceValidateTypeFcnPtr> (FunctionType::TYPED_ARRAY_REFERENCE_VALIDATE_TYPE); throwIfError(fcn(impl, static_cast<int>(GetArrayType<T>::type), false)); } template <typename T> inline T castToType(void* value, ArrayType type) { switch (type) { case ArrayType::LOGICAL: return static_cast<T>(*static_cast<bool*>(value)); case ArrayType::DOUBLE: return static_cast<T>(*static_cast<double*>(value)); case ArrayType::SINGLE: return static_cast<T>(*static_cast<float*>(value)); case ArrayType::INT64: return static_cast<T>(*static_cast<int64_t*>(value)); case ArrayType::INT32: return static_cast<T>(*static_cast<int32_t*>(value)); case ArrayType::INT16: return static_cast<T>(*static_cast<int16_t*>(value)); case ArrayType::INT8: return static_cast<T>(*static_cast<int8_t*>(value)); case ArrayType::UINT64: return static_cast<T>(*static_cast<uint64_t*>(value)); case ArrayType::UINT32: return static_cast<T>(*static_cast<uint32_t*>(value)); case ArrayType::UINT16: return static_cast<T>(*static_cast<uint16_t*>(value)); case ArrayType::UINT8: return static_cast<T>(*static_cast<uint8_t*>(value)); default: throw TypeMismatchException(std::string("Can't convert this element")); } return T(); } template <> inline bool castToType<bool>(void* value, ArrayType type) { switch (type) { case ArrayType::LOGICAL: return *static_cast<bool*>(value); case ArrayType::DOUBLE: return *static_cast<double*>(value) != 0; case ArrayType::SINGLE: return *static_cast<float*>(value) != 0; case ArrayType::INT64: return *static_cast<int64_t*>(value) != 0; case ArrayType::INT32: return *static_cast<int32_t*>(value) != 0; case ArrayType::INT16: return *static_cast<int16_t*>(value) != 0; case ArrayType::INT8: return *static_cast<int8_t*>(value) != 0; case ArrayType::UINT64: return *static_cast<uint64_t*>(value) != 0; case ArrayType::UINT32: return *static_cast<uint32_t*>(value) != 0; case ArrayType::UINT16: return *static_cast<uint16_t*>(value) != 0; case ArrayType::UINT8: return *static_cast<uint8_t*>(value) != 0; default: throw TypeMismatchException(std::string("Can't convert this element")); } return false; } template <> inline CHAR16_T castToType<CHAR16_T>(void* value, ArrayType type) { switch (type) { case ArrayType::CHAR: return *static_cast<CHAR16_T*>(value); default: throw TypeMismatchException(std::string("Can't convert this element")); } return false; } template <typename U> inline typename std::enable_if<std::is_arithmetic<U>::value, U&>::type getElement(std::shared_ptr<ReferenceImpl> impl) { void* value = nullptr; typedef int(*TypedReferenceGetPodValueFcnPtr)(ReferenceImpl*, void**); static const TypedReferenceGetPodValueFcnPtr fcn = resolveFunction<TypedReferenceGetPodValueFcnPtr> (FunctionType::TYPED_REFERENCE_GET_POD_VALUE); throwIfError(fcn(impl.get(), &value)); return *(static_cast<U*>(value)); } template <typename U> inline typename std::enable_if<is_complex<U>::value, U>::type getElement(std::shared_ptr<ReferenceImpl> impl) { void* real = nullptr; void* imag = nullptr; typedef int(*TypedReferenceGetComplexValueFcnPtr)(ReferenceImpl*, void**, void**); static const TypedReferenceGetComplexValueFcnPtr fcn = resolveFunction<TypedReferenceGetComplexValueFcnPtr> (FunctionType::TYPED_REFERENCE_GET_COMPLEX_VALUE); throwIfError(fcn(impl.get(), &real, &imag)); typename U::value_type r = *(static_cast<typename U::value_type*>(real)); typename U::value_type i = *(static_cast<typename U::value_type*>(imag)); return U(r,i); } template <typename U> inline typename std::enable_if<std::is_same<MATLABString, U>::value, MATLABString>::type getElement(std::shared_ptr<ReferenceImpl> impl) { const char16_t* str = nullptr; size_t strlen = 0; typedef int(*StringGetValueFcnPtr)(ReferenceImpl*, const char16_t**, size_t*); static const StringGetValueFcnPtr fcn = resolveFunction<StringGetValueFcnPtr> (FunctionType::STRING_GET_VALUE); throwIfError(fcn(impl.get(), &str, &strlen)); if (str != nullptr) { return MATLABString(String(str, strlen)); } return MATLABString(); } template <typename U> inline typename std::enable_if<std::is_same<Array, U>::value, Array>::type getElement(std::shared_ptr<ReferenceImpl> impl) { impl::ArrayImpl* arr_impl = nullptr; typedef void(*ArrayReferenceSharedCopyFcnPtr)(ReferenceImpl*, matlab::data::impl::ArrayImpl**); static const ArrayReferenceSharedCopyFcnPtr fcn = resolveFunction<ArrayReferenceSharedCopyFcnPtr> (FunctionType::ARRAY_REFERENCE_SHARED_COPY); fcn(impl.get(), &arr_impl); return Access::createObj<U>(arr_impl); } template <typename U> inline typename std::enable_if<std::is_same<Struct, U>::value, Struct>::type getElement(std::shared_ptr<ReferenceImpl> impl) { return Access::createObj<U>(impl); } template <typename U> inline typename std::enable_if<std::is_same<Object, U>::value, Object>::type getElement(std::shared_ptr<ReferenceImpl> impl) { impl::ObjectImpl* obj_impl = nullptr; char msg[100]; typedef int(*ObjectSharedCopyFcnPtr)(ReferenceImpl*, impl::ObjectImpl**, char*); static const ObjectSharedCopyFcnPtr fcn = resolveFunction<ObjectSharedCopyFcnPtr> (FunctionType::OBJECT_SHARED_COPY); auto res = fcn(impl.get(), &obj_impl, msg); throwIfError(res, std::string(msg)); return Access::createObj<U>(obj_impl); } template <typename U> inline typename std::enable_if<std::is_arithmetic<U>::value>::type setElement(ReferenceImpl* impl, U rhs, int type) { typedef int(*TypedReferenceSetPodValueFcnPtr)(ReferenceImpl*, int, void*); static const TypedReferenceSetPodValueFcnPtr fcn = resolveFunction<TypedReferenceSetPodValueFcnPtr> (FunctionType::TYPED_REFERENCE_SET_POD_VALUE); throwIfError(fcn(impl, type, &rhs)); } template <typename U> inline typename std::enable_if<is_complex<U>::value>::type setElement(ReferenceImpl* impl, U rhs, int type) { typename U::value_type real = rhs.real(); typename U::value_type imag = rhs.imag(); typedef int(*TypedReferenceSetComplexValueFcnPtr)(ReferenceImpl*, int, void*, void*); static const TypedReferenceSetComplexValueFcnPtr fcn = resolveFunction<TypedReferenceSetComplexValueFcnPtr> (FunctionType::TYPED_REFERENCE_SET_COMPLEX_VALUE); throwIfError(fcn(impl, type, &real, &imag)); } template <typename U> inline typename std::enable_if<std::is_same<String, U>::value>::type setElement(ReferenceImpl* impl, U rhs, int) { typedef int(*ReferenceSetChar16StringFcnPtr)(ReferenceImpl*, const char16_t*, size_t); static const ReferenceSetChar16StringFcnPtr fcn = resolveFunction<ReferenceSetChar16StringFcnPtr> (FunctionType::REFERENCE_SET_CHAR16_STRING); throwIfError(fcn(impl, rhs.c_str(), rhs.size())); } template <typename U> inline typename std::enable_if<std::is_same<std::string, U>::value>::type setElement(ReferenceImpl* impl, U rhs, int) { typedef int(*ReferenceSetStringFcnPtr)(ReferenceImpl*, const char*, size_t); static const ReferenceSetStringFcnPtr fcn = resolveFunction<ReferenceSetStringFcnPtr> (FunctionType::REFERENCE_SET_STRING); throwIfError(fcn(impl, rhs.c_str(), rhs.size())); } template <typename U> inline typename std::enable_if<std::is_base_of<Array, U>::value>::type setElement(ReferenceImpl* impl, U rhs, int) { typedef int(*ReferenceSetReferenceValueFcnPtr)(ReferenceImpl*, matlab::data::impl::ArrayImpl*); static const ReferenceSetReferenceValueFcnPtr fcn = resolveFunction<ReferenceSetReferenceValueFcnPtr> (FunctionType::REFERENCE_SET_REFERENCE_VALUE); throwIfError(fcn(impl, Access::getImpl<matlab::data::impl::ArrayImpl>(rhs))); } template <typename U> inline typename std::enable_if<std::is_same<Object, U>::value, Object>::type setElement(ReferenceImpl* impl, U rhs, int) { char msg[100]; typedef int(*ReferenceSetReferenceObjectFcnPtr)(ReferenceImpl*, matlab::data::impl::ObjectImpl*, char*); static const ReferenceSetReferenceObjectFcnPtr fcn = resolveFunction<ReferenceSetReferenceObjectFcnPtr> (FunctionType::REFERENCE_SET_REFERENCE_OBJECT); auto res = fcn(impl, Access::getImpl<matlab::data::impl::ObjectImpl>(rhs), msg); throwIfError(res, std::string(msg)); return rhs; } template <typename U> inline typename std::enable_if<std::is_same<MATLABString, U>::value>::type setElement(ReferenceImpl* impl, U rhs, int) { if (rhs) { String elem = *rhs; typedef int(*ReferenceSetChar16StringFcnPtr)(ReferenceImpl*, const char16_t*, size_t); static const ReferenceSetChar16StringFcnPtr fcn = resolveFunction<ReferenceSetChar16StringFcnPtr> (FunctionType::REFERENCE_SET_CHAR16_STRING); throwIfError(fcn(impl, elem.c_str(), elem.size())); } else { typedef int(*ReferenceSetMissingChar16StringFcnPtr)(ReferenceImpl*); static const ReferenceSetMissingChar16StringFcnPtr fcn = resolveFunction<ReferenceSetMissingChar16StringFcnPtr> (FunctionType::REFERENCE_SET_MISSING_CHAR16_STRING); throwIfError(fcn(impl)); } } template <typename U> inline typename std::enable_if<!std::is_same<Enumeration, U>::value && !std::is_same<MATLABString, U>::value, std::string>::type getString(ReferenceImpl* impl) { throwIfError(static_cast<int>(ExceptionType::InvalidDataType)); } template <typename U> inline typename std::enable_if<std::is_same<Enumeration, U>::value, std::string>::type getString(ReferenceImpl* impl) { const char* str = nullptr; size_t strlen = 0; typedef int(*EnumArrayGetValueFcnPtr)(ReferenceImpl*, const char**, size_t*); static const EnumArrayGetValueFcnPtr fcn = resolveFunction<EnumArrayGetValueFcnPtr> (FunctionType::ENUM_ARRAY_GET_VALUE); throwIfError(fcn(impl, &str, &strlen)); return std::string(str, strlen); } template <typename U> inline typename std::enable_if<std::is_same<MATLABString, U>::value, std::string>::type getString(ReferenceImpl* impl) { const char16_t* str = nullptr; size_t strlen = 0; bool missing = false; typedef int(*StringGetValueFcnPtr)(ReferenceImpl* impl, const char16_t**, size_t*); static const StringGetValueFcnPtr fcn = resolveFunction<StringGetValueFcnPtr> (FunctionType::STRING_GET_VALUE); throwIfError(fcn(impl, &str, &strlen)); if (str == nullptr) { throw std::runtime_error("Missing string"); } String temp(str, strlen); if (!isAscii7(temp)) { throw NonAsciiCharInInputDataException(std::string("Input data can only contain ASCII characters")); } return toAsciiHelper(str, strlen); } template <typename U> inline typename std::enable_if<!std::is_same<Enumeration, U>::value && !std::is_same<MATLABString, U>::value>::type setString(ReferenceImpl* impl, std::string rhs) { throwIfError(static_cast<int>(ExceptionType::InvalidDataType)); } template <typename U> inline typename std::enable_if<std::is_same<Enumeration, U>::value>::type setString(ReferenceImpl* impl, std::string rhs) { typedef int(*EnumArraySetValueFcnPtr)(ReferenceImpl*, const char*, size_t); static const EnumArraySetValueFcnPtr fcn = resolveFunction<EnumArraySetValueFcnPtr> (FunctionType::ENUM_ARRAY_SET_VALUE); throwIfError(fcn(impl, rhs.c_str(), rhs.size())); } template <typename U> inline typename std::enable_if<std::is_same<MATLABString, U>::value>::type setString(ReferenceImpl* impl, std::string rhs) { if (!isAscii7(rhs)) { throw NonAsciiCharInInputDataException(std::string("Input data can only contain ASCII characters")); } String tmp(rhs.begin(), rhs.end()); setElement<String>(impl, std::move(tmp), 0); } template <typename U> inline typename std::enable_if<std::is_same<MATLABString, U>::value>::type setString(ReferenceImpl* impl, String rhs) { setElement<String>(impl, std::move(rhs), 0); } template <typename U> inline typename std::enable_if<std::is_same<MATLABString, U>::value>::type setString(ReferenceImpl* impl, MATLABString rhs) { setElement<MATLABString>(impl, std::move(rhs), 0); } inline ReferenceImpl* getRef(ReferenceImpl* impl, const char* field, size_t len, bool unshare) { ReferenceImpl* retVal = nullptr; size_t numIdx = 0; typedef int(*StructReferenceGetIndexFcnPtr)(ReferenceImpl*, const char*, size_t, size_t*); static const StructReferenceGetIndexFcnPtr fcn = resolveFunction<StructReferenceGetIndexFcnPtr> (FunctionType::STRUCT_REFERENCE_GET_INDEX); throwIfError(fcn(impl, field, len, &numIdx)); typedef int(*ReferenceGetReferenceValueFcnPtr)(ReferenceImpl*, bool, ReferenceImpl**); static const ReferenceGetReferenceValueFcnPtr fcn2 = resolveFunction<ReferenceGetReferenceValueFcnPtr> (FunctionType::REFERENCE_GET_REFERENCE_VALUE); fcn2(impl, unshare, &retVal); typedef int(*ReferenceAddIndexFcnPtr)(ReferenceImpl*, size_t); static const ReferenceAddIndexFcnPtr fcn3 = resolveFunction<ReferenceAddIndexFcnPtr> (FunctionType::REFERENCE_ADD_INDEX); fcn3(retVal, numIdx); return retVal; } template <typename T, bool is_const_ref> typename std::enable_if<std::is_arithmetic<T>::value, T>::type castTo(std::shared_ptr<ReferenceImpl> impl) { void* value = nullptr; typedef int(*TypedReferenceGetPodValueFcnPtr)(ReferenceImpl*, void**); static const TypedReferenceGetPodValueFcnPtr fcn = resolveFunction<TypedReferenceGetPodValueFcnPtr> (FunctionType::TYPED_REFERENCE_GET_POD_VALUE); throwIfError(fcn(impl.get(), &value)); typedef int(*ReferenceGetContainerTypeFcnPtr)(ReferenceImpl*, int*); static const ReferenceGetContainerTypeFcnPtr fcn2 = resolveFunction<ReferenceGetContainerTypeFcnPtr> (FunctionType::REFERENCE_GET_CONTAINER_TYPE); int type; throwIfError(fcn2(impl.get(), &type)); return castToType<T>(value, static_cast<ArrayType>(type)); } template <typename T, bool is_const_ref> typename std::enable_if<std::is_base_of<Array, T>::value, T>::type castTo(std::shared_ptr<ReferenceImpl> impl) { typedef int(*TypedArrayReferenceValidateTypeFcnPtr)(ReferenceImpl*, int, bool); static const TypedArrayReferenceValidateTypeFcnPtr fcn2 = resolveFunction<TypedArrayReferenceValidateTypeFcnPtr> (FunctionType::TYPED_ARRAY_REFERENCE_VALIDATE_TYPE); throwIfError(fcn2(impl.get(), static_cast<int>(T::type), false)); impl::ArrayImpl* arr_impl = nullptr; typedef void(*ArrayReferenceSharedCopyFcnPtr)(ReferenceImpl*, matlab::data::impl::ArrayImpl**); static const ArrayReferenceSharedCopyFcnPtr fcn = resolveFunction<ArrayReferenceSharedCopyFcnPtr> (FunctionType::ARRAY_REFERENCE_SHARED_COPY); fcn(impl.get(), &arr_impl); return Access::createObj<T>(arr_impl); } template <typename T, bool is_const_ref> typename std::enable_if<is_complex<T>::value, T>::type castTo(std::shared_ptr<ReferenceImpl> impl) { void* real = nullptr; void* imag = nullptr; typedef int(*TypedReferenceGetComplexValueFcnPtr)(ReferenceImpl*, void**, void**); static const TypedReferenceGetComplexValueFcnPtr fcn = resolveFunction<TypedReferenceGetComplexValueFcnPtr> (FunctionType::TYPED_REFERENCE_GET_COMPLEX_VALUE); throwIfError(fcn(impl.get(), &real, &imag)); typename T::value_type r = *(static_cast<typename T::value_type*>(real)); typename T::value_type i = *(static_cast<typename T::value_type*>(imag)); return T(r,i); } template <typename T, bool is_const_ref> typename std::enable_if<std::is_same<T, String>::value, String>::type castTo(std::shared_ptr<ReferenceImpl> impl) { typedef int(*ReferenceGetContainerTypeFcnPtr)(ReferenceImpl*, int*); static const ReferenceGetContainerTypeFcnPtr fcn2 = resolveFunction<ReferenceGetContainerTypeFcnPtr> (FunctionType::REFERENCE_GET_CONTAINER_TYPE); int type; throwIfError(fcn2(impl.get(), &type)); if (ArrayType(type) == ArrayType::MATLAB_STRING) { const char16_t* str = nullptr; size_t strlen = 0; typedef int(*StringGetValueFcnPtr)(ReferenceImpl* impl, const char16_t**, size_t*); static const StringGetValueFcnPtr fcn = resolveFunction<StringGetValueFcnPtr> (FunctionType::STRING_GET_VALUE); throwIfError(fcn(impl.get(), &str, &strlen)); return String(str, strlen); } throw TypeMismatchException(std::string("Can't convert this element to a matlab::data::String")); } template <typename T, bool is_const_ref> typename std::enable_if<std::is_base_of<ReferenceHolder, T>::value, T>::type castTo(std::shared_ptr<ReferenceImpl> impl) { static_assert(!is_const_ref, "Can only get a reference to a non const ref"); validateUntypedReference<typename T::ref_type>(impl.get()); return Access::createObj<T>(impl); } template <typename T, bool is_const_ref> typename std::enable_if<std::is_same<T, std::string>::value, std::string>::type castTo(std::shared_ptr<ReferenceImpl> impl) { typedef int(*ReferenceGetContainerTypeFcnPtr)(ReferenceImpl*, int*); static const ReferenceGetContainerTypeFcnPtr fcn2 = resolveFunction<ReferenceGetContainerTypeFcnPtr> (FunctionType::REFERENCE_GET_CONTAINER_TYPE); int arrType; throwIfError(fcn2(impl.get(), &arrType)); if (ArrayType(arrType) == ArrayType::ENUM) { const char* str = nullptr; size_t strlen = 0; typedef int(*EnumArrayGetValueFcnPtr)(ReferenceImpl* impl, const char**, size_t*); static const EnumArrayGetValueFcnPtr fcn = resolveFunction<EnumArrayGetValueFcnPtr> (FunctionType::ENUM_ARRAY_GET_VALUE); throwIfError(fcn(impl.get(), &str, &strlen)); return std::string(str, strlen); } else if (ArrayType(arrType) == ArrayType::MATLAB_STRING) { const char16_t* str = nullptr; size_t strlen = 0; typedef int(*StringGetValueFcnPtr)(ReferenceImpl* impl, const char16_t**, size_t*); static const StringGetValueFcnPtr fcn = resolveFunction<StringGetValueFcnPtr> (FunctionType::STRING_GET_VALUE); throwIfError(fcn(impl.get(), &str, &strlen)); String temp(str, strlen); if (!isAscii7(temp)) { throw NonAsciiCharInInputDataException(std::string("Input data can only contain ASCII characters")); } return toAsciiHelper(str, strlen); } throw TypeMismatchException(std::string("Can't convert this element to a std::string")); } } } } #endif