/* Copyright 2014-2020 The MathWorks, Inc. */ #ifndef MATLAB_DATA_ARRAY_FACTORY_HPP_ #define MATLAB_DATA_ARRAY_FACTORY_HPP_ #include "TypedArray.hpp" #include "ArrayDimensions.hpp" #include "CharArray.hpp" #include "StructArray.hpp" #include "SparseArray.hpp" #include "ObjectArray.hpp" #include "Object.hpp" #include "Enumeration.hpp" #include "EnumArray.hpp" #include "Exception.hpp" #include "GetArrayType.hpp" #include "String.hpp" #include "matlab_extdata_defs.hpp" #include "TypedIterator.hpp" #include "Optional.hpp" #include "GetReturnType.hpp" #include "MemoryLayout.hpp" #include "detail/ArrayFactoryHelpers.hpp" #include "detail/StringHelpers.hpp" #include "detail/HelperFunctions.hpp" #include "detail/FunctionType.hpp" #include "detail/ExceptionHelpers.hpp" #include "detail/publish_util.hpp" #include #include #include #include #include namespace matlab { namespace data { namespace impl { class ArrayFactoryImpl; class ObjectImpl; } namespace detail { class NameListImpl; } /** * The ArrayFactory base class provides all of the general APIs that * each of the concrete factories needs to support. */ class ArrayFactory { public: /** * Construct an ArrayFactory * * @throw FailedToLoadLibMatlabDataArrayException if necessary libraries are not found */ ArrayFactory() { typedef int (*CreateArrayFactoryFcnPtr)(typename impl::ArrayFactoryImpl**); static const CreateArrayFactoryFcnPtr fcn = detail::resolveFunction( detail::FunctionType::CREATE_ARRAY_FACTORY); impl::ArrayFactoryImpl* impl = nullptr; detail::throwIfError(fcn(&impl)); pImpl = std::shared_ptr(impl, [](impl::ArrayFactoryImpl* ptr) { typedef void (*DestroyArrayFactoryFcnPtr)(typename impl::ArrayFactoryImpl*); static const DestroyArrayFactoryFcnPtr destroyFcn = detail::resolveFunction( detail::FunctionType::DESTROY_ARRAY_FACTORY); destroyFcn(ptr); }); } /** * ArrayFactory destructor * * @throw none */ ~ArrayFactory() MW_NOEXCEPT { } /** * Creates a TypedArray with the given dimensions * * @param dims - the dimensions for the Array * * @return TypedArray - an Array with the appropriate type and dimensions * * @throw matlab::OutOfMemoryException - if the array could not be allocated * @throw InvalidArrayType - if array type of the new array is not recognized as valid * @throw NumberOfElementsExceedsMaximumException - if the ArrayDimensions create an Array which exceeds the max */ template TypedArray createArray(ArrayDimensions dims) { typedef int (*CreateArrayWithDimsFcnPtr)(typename impl::ArrayFactoryImpl*, int, size_t*, size_t, typename impl::ArrayImpl**); static const CreateArrayWithDimsFcnPtr fcn = detail::resolveFunction( detail::FunctionType::CREATE_ARRAY_WITH_DIMS); impl::ArrayImpl* impl = nullptr; detail::throwIfError(fcn(pImpl.get(), static_cast(GetArrayType::type), &dims[0], dims.size(), &impl)); return detail::Access::createObj>(impl); } /** * Creates a TypedArray with the given dimensions and filled in with the * data specified by the begin and end iterators. Data is copied and must be in column major * order. The data type for the array is determined by the value_type of the iterator. * * @param dims - the dimensions for the Array * @param begin - start of the user supplied data * @param end - end of the user supplied data * * @return TypedArray - an Array with the appropriate type, dimensions and data * * @throw matlab::OutOfMemoryException - if the array could not be allocated * @throw InvalidArrayType - if array type of the new array is not recognized as valid * @throw NumberOfElementsExceedsMaximumException - if the ArrayDimensions create an Array which exceeds the max */ template ::value_type>::type> TypedArray::type> createArray(ArrayDimensions dims, ItType begin, ItType end) { return detail::createArrayWithIterator(pImpl.get(), std::move(dims), begin, end); } /** * Creates a TypedArray with the given dimensions and filled in with the * supplied data in the initializer list. Data must be in column major order. * * @param dims - the dimensions for the Array * @param data - initializer list containing the data * * @return TypedArray - an Array with the appropriate type, dimensions and data * * @throw matlab::OutOfMemoryException - if the array could not be allocated * @throw InvalidArrayType - if array type of the new array is not recognized as valid * @throw NumberOfElementsExceedsMaximumException - if the ArrayDimensions create an Array which exceeds the max */ template TypedArray::type> createArray(ArrayDimensions dims, std::initializer_list data) { return detail::createArrayWithIterator(pImpl.get(), std::move(dims), data.begin(), data.end()); }; /** * Creates a TypedArray with the given dimensions and filled in with the * data specified by C-style begin and end pointers. Data is copied and must be in column major * order. This methods supports all arithmetic types. * * @param dims - the dimensions for the Array * @param T* - start of the user supplied data * @param T* - end of the user supplied data * * @return TypedArray - an Array with the appropriate type, dimensions and data * * @throw matlab::OutOfMemoryException - if the array could not be allocated * @throw InvalidArrayType - if array type of the new array is not recognized as valid * @throw NumberOfElementsExceedsMaximumException - if the ArrayDimensions create an Array which exceeds the max */ template typename std::enable_if::value || is_complex::value, TypedArray>::type createArray(ArrayDimensions dims, const T* const begin, const T* const end) { typedef int (*CreateArrayWithDimsAndDataFcnPtr)( typename impl::ArrayFactoryImpl * impl, int arrayType, size_t* dims, size_t numDims, const void* const dataStart, size_t numEl, typename impl::ArrayImpl**); static const CreateArrayWithDimsAndDataFcnPtr fcn = detail::resolveFunction( detail::FunctionType::CREATE_ARRAY_WITH_DIMS_AND_DATA); impl::ArrayImpl* impl = nullptr; detail::throwIfError(fcn(pImpl.get(), static_cast(GetArrayType::type), &dims[0], dims.size(), begin, (end - begin), &impl)); return detail::Access::createObj>(impl); } /** * Creates a scalar TypedArray with the given value. This methods supports * all arithmetic types. * * @param val - the value to be inserted into the scalar * * @return TypedArray - an Array with the appropriate type and data * * @throw InvalidArrayType - if array type of the new array is not recognized as valid * @throw matlab::OutOfMemoryException - if the array could not be allocated */ template typename std::enable_if::value, TypedArray>::type createScalar( const T val) { typedef int (*CreateScalarArrayFcnPtr)(typename impl::ArrayFactoryImpl * impl, int arrayType, const void* data, typename impl::ArrayImpl**); static const CreateScalarArrayFcnPtr fcn = detail::resolveFunction( detail::FunctionType::CREATE_SCALAR_ARRAY); impl::ArrayImpl* impl = nullptr; detail::throwIfError( fcn(pImpl.get(), static_cast(GetArrayType::type), &val, &impl)); return detail::Access::createObj>(impl); } /** * Creates a scalar TypedArray with the given value. This methods supports * all complex types. * * @param val - the value to be inserted into the scalar * * @return TypedArray - an Array with the appropriate type and data * * @throw InvalidArrayType - if array type of the new array is not recognized as valid * @throw matlab::OutOfMemoryException - if the array could not be allocated */ template typename std::enable_if::value, TypedArray>::type createScalar(const T val) { auto arr = createArray({1, 1}); arr[0] = val; return arr; } /** * Creates a scalar TypedArray with the given value * * @param val - the string to be inserted into the scalar * * @return TypedArray - a newly created TypedArray of MATLABString * * @throw matlab::OutOfMemoryException - if the array could not be allocated */ TypedArray createScalar(const String val) { typedef int (*CreateScalarStringFcnPtr)(typename impl::ArrayFactoryImpl * impl, const char16_t* data, size_t strlen, typename impl::ArrayImpl**); static const CreateScalarStringFcnPtr fcn = detail::resolveFunction( detail::FunctionType::CREATE_SCALAR_STRING); impl::ArrayImpl* impl = nullptr; detail::throwIfError(fcn(pImpl.get(), val.c_str(), val.size(), &impl)); return detail::Access::createObj>(impl); } /** * Creates a scalar TypedArray with the given value * * @param val - the string to be inserted into the scalar * * @return TypedArray - an Array containing data of MATLABString type * * @throw matlab::OutOfMemoryException - if the array could not be allocated */ TypedArray createScalar(const MATLABString val) { impl::ArrayImpl* impl = nullptr; if (val) { typedef int (*CreateScalarStringFcnPtr)(typename impl::ArrayFactoryImpl * impl, const char16_t* data, size_t strlen, typename impl::ArrayImpl**); static const CreateScalarStringFcnPtr fcn = detail::resolveFunction( detail::FunctionType::CREATE_SCALAR_STRING); String str = *val; detail::throwIfError(fcn(pImpl.get(), str.c_str(), str.size(), &impl)); } else { typedef int (*CreateScalarMissingStringFcnPtr)(typename impl::ArrayFactoryImpl * impl, typename impl::ArrayImpl**); static const CreateScalarMissingStringFcnPtr fcn2 = detail::resolveFunction( detail::FunctionType::CREATE_SCALAR_MISSING_STRING); detail::throwIfError(fcn2(pImpl.get(), &impl)); } return detail::Access::createObj>(impl); } /** * Creates a CellArray with specified dimensions. * * @param dims - the dimensions of the CellArray * * @return CellArray - newly created Cell Array * * @throw matlab::OutOfMemoryException - if the array could not be allocated * @throw NumberOfElementsExceedsMaximumException - if the ArrayDimensions create an Array which exceeds the max */ CellArray createCellArray(ArrayDimensions dims) { return createArray(std::move(dims)); } /** * Creates a CellArray with the data specified. Data is specified in column major order * * @param dims - the dimensions of the CellArray * @params data - elements to be inserted into the cell array. Primitive types, strings and * Arrays are allowable inputs. * * @return CellArray - newly created Cell Array * * @throw NumberOfElementsExceedsMaximumException - if the ArrayDimensions create an Array which exceeds the max * @throw matlab::OutOfMemoryException - if the array could not be allocated */ template TypedArray createCellArray(ArrayDimensions dims, Targs... data) { auto retVal = createArray(std::move(dims)); if (retVal.getNumberOfElements() != 0) { setCellValues(retVal.begin(), retVal.end(), data...); } return retVal; } /** * Creates a scalar TypedArray from 7-bit ascii input data * Input is converted to UTF16 prior to creating a TypedArray * * @param val - std::string to be inserted into the scalar StringArray * * @return TypedArray - a TypedArray with data of type MATLABString * * @throw matlab::OutOfMemoryException - if the array could not be allocated * @throw NonAsciiCharInInputDataException - if input contains non-ascii data */ TypedArray createScalar(const std::string& val) { if (!detail::isAscii7(val)) { throw NonAsciiCharInInputDataException( std::string("Input data can only contain ASCII characters")); } return createScalar(String(val.begin(), val.end())); } /** * Creates a scalar ObjectArray * * @param val - val Object to be used to create the ObjectArray scalar * * @return ObjectArray - ObjectArray containing the object * * @throw matlab::OutOfMemoryException - if the array could not be allocated */ ObjectArray createScalar(const Object& val) { impl::ArrayImpl* impl = nullptr; typedef int (*CreateScalarObjectFcnPtr)(typename impl::ArrayFactoryImpl* impl, impl::ObjectImpl* objImpl, typename impl::ArrayImpl**); static const CreateScalarObjectFcnPtr fcn = detail::resolveFunction( detail::FunctionType::CREATE_SCALAR_OBJECT); detail::throwIfError(fcn(pImpl.get(), detail::Access::getImpl(val), &impl)); return detail::Access::createObj(impl); } /** * Creates a 1xn CharArray from the specified char16_t string, where n is the * string length * * @param str - String containing the data to be filled into the Array * * @return CharArray - a CharArray object containing data copied from str * * @throw matlab::OutOfMemoryException - if the array could not be allocated */ CharArray createCharArray(String str) { typedef int (*CreateCharArrayFromChar16FcnPtr)( typename impl::ArrayFactoryImpl * impl, const char16_t* data, size_t strlen, typename impl::ArrayImpl**); static const CreateCharArrayFromChar16FcnPtr fcn = detail::resolveFunction( detail::FunctionType::CREATE_CHAR_ARRAY_FROM_CHAR16); impl::ArrayImpl* impl = nullptr; detail::throwIfError(fcn(pImpl.get(), str.c_str(), str.size(), &impl)); return detail::Access::createObj(impl); } /** * Creates a 1xn CharArray from the specified std::string, where n is the * string length. Data is checked for 7-bit ascii and converted from * std::string to std::string. * * @param str - std::string containing the data to be filled into the Array * * @return CharArray - a CharArray object containing data copied from str * * @throw matlab::OutOfMemoryException - if the array could not be allocated * @throw NonAsciiCharInInputDataException - if data contains non-ascii characters */ CharArray createCharArray(const std::string& str) { if (!detail::isAscii7(str)) { throw NonAsciiCharInInputDataException( std::string("Input data can only contain ASCII characters")); } typedef int (*CreateCharArrayFromStringFcnPtr)( typename impl::ArrayFactoryImpl * impl, const char* data, size_t strlen, typename impl::ArrayImpl**); static const CreateCharArrayFromStringFcnPtr fcn = detail::resolveFunction( detail::FunctionType::CREATE_CHAR_ARRAY_FROM_STRING); impl::ArrayImpl* impl = nullptr; detail::throwIfError(fcn(pImpl.get(), str.c_str(), str.size(), &impl)); return detail::Access::createObj(impl); } /** * Creates a StructArray with the given dimensions and fieldnames * * @param dims - the dimensions for the Array * @param fieldNames - vector of the field names for the struct * * @return StructArray - an Array with the appropriate dimensions and fieldNames * * @throw matlab::OutOfMemoryException - if the array could not be allocated * @throw NumberOfElementsExceedsMaximumException - if the ArrayDimensions create an Array which exceeds the max * @throw DuplicateFieldNameInStructArrayException - if duplicate field names are specified */ StructArray createStructArray(ArrayDimensions dims, std::vector fieldNames) { typedef detail::NameListImpl* (*CreateNamesFcnPtr)(size_t num); static const CreateNamesFcnPtr fcn3 = detail::resolveFunction(detail::FunctionType::CREATE_NAMES); NameList field_names(fcn3(fieldNames.size())); for (const auto& field : fieldNames) { typedef void (*AddNameFcnPtr)(typename detail::NameListImpl * impl, const char* name, size_t nameLen); static const AddNameFcnPtr fcn = detail::resolveFunction(detail::FunctionType::ADD_NAME); fcn(field_names.getImpl(), field.c_str(), field.size()); } impl::ArrayImpl* impl = nullptr; typedef int (*CreateStructArrayFcnPtr)(typename impl::ArrayFactoryImpl * impl, size_t * dims, size_t numDims, typename detail::NameListImpl * names, typename impl::ArrayImpl**); static const CreateStructArrayFcnPtr fcn2 = detail::resolveFunction( detail::FunctionType::CREATE_STRUCT_ARRAY); detail::throwIfError( fcn2(pImpl.get(), &dims[0], dims.size(), field_names.getImpl(), &impl)); return detail::Access::createObj(impl); } /** * Creates an Enumeration array. The class of the EnumArray is indicated by className. * The array is initialized with the list of enumeration names provided by the caller. * * @param dims array dimensions * @param className class name of the enumeration array * @param enums List of the enumeration names * * @return EnumArray - the EnumArray that was created * * @throw matlab::OutOfMemoryException - if the array could not be allocated * @throw MustSpecifyClassNameException - if an empty class name is specified * @throw WrongNumberOfEnumsSuppliedException - if the wrong number of enumerations is provided * @throw NumberOfElementsExceedsMaximumException - if the ArrayDimensions create an Array which exceeds the max */ EnumArray createEnumArray(ArrayDimensions dims, std::string className, std::vector enums) { typedef detail::NameListImpl* (*CreateNamesFcnPtr)(size_t num); static const CreateNamesFcnPtr fcn3 = detail::resolveFunction(detail::FunctionType::CREATE_NAMES); NameList enum_names(fcn3(enums.size())); for (const auto& e : enums) { typedef void (*AddNameFcnPtr)(typename detail::NameListImpl * impl, const char* name, size_t nameLen); static const AddNameFcnPtr fcn = detail::resolveFunction(detail::FunctionType::ADD_NAME); fcn(enum_names.getImpl(), e.c_str(), e.size()); } impl::ArrayImpl* impl = nullptr; typedef int (*CreateEnumArrayWithEnumsFcnPtr)( typename impl::ArrayFactoryImpl * impl, size_t * dims, size_t numDims, const char* cls, size_t clsSize, typename detail::NameListImpl* names, typename impl::ArrayImpl** retVal); static const CreateEnumArrayWithEnumsFcnPtr fcn2 = detail::resolveFunction( detail::FunctionType::CREATE_ENUM_ARRAY_WITH_ENUMS); detail::throwIfError(fcn2(pImpl.get(), &dims[0], dims.size(), className.c_str(), className.size(), enum_names.getImpl(), &impl)); return detail::Access::createObj(impl); } /** * Creates an Enumeration array. The class of the EnumArray is indicated by className. * * @param dims array dimensions * @param className class name of the enumeration array * * @return EnumArray - the EnumArray that was created * * @throw matlab::OutOfMemoryException - if the array could not be allocated * @throw NumberOfElementsExceedsMaximumException - if the ArrayDimensions create an Array which exceeds the max * @throw MustSpecifyClassNameException - if an empty class name is specified */ EnumArray createEnumArray(ArrayDimensions dims, std::string className) { typedef int (*CreateEnumArrayFcnPtr)(typename impl::ArrayFactoryImpl*, size_t*, size_t, const char*, size_t, typename impl::ArrayImpl**); static const CreateEnumArrayFcnPtr fcn = detail::resolveFunction(detail::FunctionType::CREATE_ENUM_ARRAY); impl::ArrayImpl* impl = nullptr; detail::throwIfError( fcn(pImpl.get(), &dims[0], dims.size(), className.c_str(), className.size(), &impl)); return detail::Access::createObj(impl); } /** * Creates an empty Array. It contains no elements. * * @return Array - an empty Array * @throw matlab::OutOfMemoryException - if the array could not be allocated */ Array createEmptyArray() { return createArray({0, 0}); } /** * Creates a buffer which can be passed into createArrayFromBuffer() * No data copies are made when creating an Array from a buffer. Data * must be in column major order. * * @param numberOfElements - the number of elements, not the actual buffer size * * @return buffer_ptr - unique_ptr containing the buffer * * @throw InvalidArrayTypeException - if buffer type is not recognized as valid * @throw matlab::OutOfMemoryException - if the array could not be allocated */ template buffer_ptr_t createBuffer(size_t numberOfElements) { void* buffer = nullptr; buffer_deleter_t deleter = nullptr; typedef int (*CreateBufferFcnPtr)(typename impl::ArrayFactoryImpl * impl, void** buffer, void (**deleter)(void*), int dataType, size_t numElements); static const CreateBufferFcnPtr fcn = detail::resolveFunction(detail::FunctionType::CREATE_BUFFER); detail::throwIfError(fcn(pImpl.get(), &buffer, &deleter, static_cast(GetArrayType::type), numberOfElements)); return buffer_ptr_t(static_cast(buffer), deleter); } /** * Creates a TypedArray using the given buffer * No data copies are made when creating an Array from a buffer. The TypedArray * takes ownership of the buffer. * * @param dims - the dimensions for the Array * @param buffer - buffer containing the data. Buffer will not be copied. Array takes ownership * * @return TypedArray - TypedArray which wraps the buffer * * @throw InvalidArrayTypeException - if buffer type is not recognized as valid * @throw FeatureNotSupportedException - if creating an array with a row major layout using a release prior to R2019a * @throw matlab::InvalidMemoryLayoutException - if the array could not be allocated * @throw matlab::OutOfMemoryException - if the array could not be allocated */ template TypedArray createArrayFromBuffer(ArrayDimensions dims, buffer_ptr_t buffer, MemoryLayout memoryLayout = MemoryLayout::COLUMN_MAJOR) { buffer_deleter_t deleter = buffer.get_deleter(); impl::ArrayImpl* impl = nullptr; typedef int (*CreateArrayFromBufferV2FcnPtr)( typename impl::ArrayFactoryImpl * impl, int arrayType, size_t* dims, size_t numDims, void* buffer, void (*deleter)(void*), typename impl::ArrayImpl**, int memoryLayout); static const CreateArrayFromBufferV2FcnPtr fcn = detail::resolveFunctionNoExcept( detail::FunctionType::CREATE_ARRAY_FROM_BUFFER_V2); if (fcn != nullptr) { detail::throwIfError(fcn(pImpl.get(), static_cast(GetArrayType::type), &dims[0], dims.size(), buffer.release(), deleter, &impl, static_cast(memoryLayout))); } else { // new version is not available // if asking for a row-major need to throw if (memoryLayout == MemoryLayout::ROW_MAJOR) { throw FeatureNotSupportedException(std::string("Row-major buffers require ") + std::string("2019a")); } typedef int (*CreateArrayFromBufferFcnPtr)( typename impl::ArrayFactoryImpl * impl, int arrayType, size_t* dims, size_t numDims, void* buffer, void (*deleter)(void*), typename impl::ArrayImpl**); static const CreateArrayFromBufferFcnPtr fcn = detail::resolveFunction( detail::FunctionType::CREATE_ARRAY_FROM_BUFFER); detail::throwIfError(fcn(pImpl.get(), static_cast(GetArrayType::type), &dims[0], dims.size(), buffer.release(), deleter, &impl)); } return detail::Access::createObj>(impl); } /** * Creates a SparseArray with the given dimensions. * Only two dimensions are allowed for sparse arrays. * * @param dims - the dimensions for the Array * @param nnz - number of non-zero elements * @param data - buffer containing the non-zero elements. Buffer will not be copied. Array takes * ownership * @param rows - buffer containing the row value for each element. Buffer will not be copied. * Array takes ownership * @param cols - buffer containing the column value for each element. Buffer will not be copied. * Array takes ownership * * @return SparseArray - a sparse array with the appropriate type and dimensions, and data * * @throw InvalidArrayTypeException - if array type is not recognized as valid * @throw InvalidDimensionsInSparseArrayException if more than two dimensions are specified. * @throw matlab::OutOfMemoryException - if the array could not be allocated * */ template typename std::enable_if::value || std::is_same::value || std::is_same, T>::value, SparseArray>::type createSparseArray(ArrayDimensions dims, size_t nnz, buffer_ptr_t data, buffer_ptr_t rows, buffer_ptr_t cols) { // If more than two dimensions are specified, throw an exception if (dims.size() > 2) { throw InvalidDimensionsInSparseArrayException( std::string("Sparse Array can only have 2 dimensions")); } buffer_deleter_t data_deleter = data.get_deleter(); buffer_deleter_t rows_deleter = rows.get_deleter(); buffer_deleter_t cols_deleter = cols.get_deleter(); impl::ArrayImpl* impl = nullptr; typedef int (*CreateSparseArrayFromBufferFcnPtr)( typename impl::ArrayFactoryImpl * impl, int arrayType, size_t* dims, size_t numDims, size_t nnz, void* dataBuffer, void (*dataDeleter)(void*), size_t* rowsBuffer, void (*rowsDeleter)(void*), size_t* colsBuffer, void (*colsDeleter)(void*), typename impl::ArrayImpl**); static const CreateSparseArrayFromBufferFcnPtr fcn = detail::resolveFunction( detail::FunctionType::CREATE_SPARSE_ARRAY_FROM_BUFFER); detail::throwIfError(fcn(pImpl.get(), static_cast(GetSparseArrayType::type), &dims[0], dims.size(), nnz, data.release(), data_deleter, rows.release(), rows_deleter, cols.release(), cols_deleter, &impl)); return detail::Access::createObj>(impl); } protected: std::shared_ptr pImpl; private: ArrayFactory& operator=(ArrayFactory const& rhs) = delete; ArrayFactory(const ArrayFactory& rhs) = delete; class NameList { public: NameList(detail::NameListImpl* impl) : pImpl(std::shared_ptr(impl, [](detail::NameListImpl* ptr) { typedef void (*DestroyNamesFcnPtr)(typename detail::NameListImpl * impl); static const DestroyNamesFcnPtr destroyFcn = detail::resolveFunction( detail::FunctionType::DESTROY_NAMES); destroyFcn(ptr); })) { } detail::NameListImpl* getImpl() const { return pImpl.get(); } private: std::shared_ptr pImpl; }; template typename std::enable_if::value, Array>::type createValue(T value) { return createScalar(std::move(value)); } template typename std::enable_if::value, Array>::type createValue(T value) { return matlab::data::Array(std::move(value)); } template void setCellValues(TypedIterator it, TypedIterator end, T value) { *it = createValue(std::move(value)); } template void setCellValues(TypedIterator it, TypedIterator end, T value, Targs... args) { *it = createValue(std::move(value)); if (++it != end) { setCellValues(std::move(it), std::move(end), args...); } } }; } // namespace data } // namespace matlab #endif