/* Copyright 2017 The MathWorks, Inc. */ #ifndef MATLAB_EXECUTION_INTERFACE_IMPL_HPP #define MATLAB_EXECUTION_INTERFACE_IMPL_HPP #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace { inline matlab::execution::MATLABExecutionException createMATLABExecutionException(const matlab::data::StructArray& mException); inline std::vector createCause(const matlab::data::CellArray& cause) { size_t nCauses = cause.getNumberOfElements(); std::vector causes(nCauses); for (size_t i = 0; i < nCauses; i++) { matlab::data::Array exRef = cause[i]; matlab::data::StructArray ex(exRef); causes[i] = createMATLABExecutionException(ex); } return causes; } inline std::vector createStackTrace(const matlab::data::StructArray& stack) { size_t nFrames = stack.getNumberOfElements(); std::vector stackFrames(nFrames); for (size_t i = 0; i < nFrames; i++) { matlab::data::Array fileRef = stack[i]["File"]; matlab::data::CharArray fileStr(fileRef); matlab::data::Array nameRef = stack[i]["Name"]; matlab::data::CharArray nameStr(nameRef); matlab::data::Array lineRef = stack[i]["Line"]; double line = lineRef[0]; stackFrames[i] = matlab::execution::StackFrame(fileStr.toUTF16(), nameStr.toUTF16(), uint32_t(line)); } return stackFrames; } inline matlab::execution::MATLABExecutionException createMATLABExecutionException(const matlab::data::StructArray& mException) { matlab::data::Array idRef = mException[0][std::string("identifier")]; matlab::data::CharArray id(idRef); matlab::data::Array messageRef = mException[0][std::string("message")]; matlab::data::CharArray message(messageRef); matlab::data::Array stackRef = mException[0][std::string("stack")]; matlab::data::StructArray stack(stackRef); matlab::data::Array causeRef = mException[0][std::string("cause")]; matlab::data::CellArray cause(causeRef); std::vector meCause = createCause(cause); std::vector meStack = createStackTrace(stack); return matlab::execution::MATLABExecutionException(id.toAscii(), message.toUTF16(), meStack, meCause); } inline matlab::execution::MATLABSyntaxException createMATLABSyntaxException(const matlab::data::StructArray& mException) { matlab::data::Array idRef = mException[0][std::string("identifier")]; matlab::data::CharArray id(idRef); matlab::data::Array messageRef = mException[0][std::string("message")]; matlab::data::CharArray message(messageRef); return matlab::execution::MATLABSyntaxException(id.toAscii(), message.toUTF16()); } template inline void set_promise_exception(void *p, size_t excTypeNumber, const void* msg) { std::promise* prom = reinterpret_cast*>(p); ::detail::ExceptionType excType = static_cast<::detail::ExceptionType>(excTypeNumber); switch (excType) { case ::detail::ExceptionType::CANCELLED:{ const char* message = reinterpret_cast(msg); matlab::execution::CancelledException exception(message); prom->set_exception(std::make_exception_ptr(exception)); break; } case ::detail::ExceptionType::INTERRUPTED: { const char* message = reinterpret_cast(msg); matlab::execution::InterruptedException exception(message); prom->set_exception(std::make_exception_ptr(exception)); break; } case ::detail::ExceptionType::EXECUTION: case ::detail::ExceptionType::SYNTAX: { matlab::data::impl::ArrayImpl* exceptionImpl = const_cast(reinterpret_cast(msg)); matlab::data::Array mdaException = matlab::data::detail::Access::createObj(exceptionImpl); matlab::data::StructArray mException(mdaException); if (excType == ::detail::ExceptionType::SYNTAX) { matlab::execution::MATLABSyntaxException exception = createMATLABSyntaxException(mException); prom->set_exception(std::make_exception_ptr(exception)); } else { matlab::execution::MATLABExecutionException exception(createMATLABExecutionException(mException)); prom->set_exception(std::make_exception_ptr(exception)); } break; } case ::detail::ExceptionType::OTHER: { const char* message = reinterpret_cast(msg); matlab::execution::Exception exception(message); prom->set_exception(std::make_exception_ptr(exception)); break; } case ::detail::ExceptionType::STOPPED: { const char* message = reinterpret_cast(msg); matlab::execution::MATLABNotAvailableException exception(message); prom->set_exception(std::make_exception_ptr(exception)); break; } break; } delete prom; } } namespace matlab { namespace execution { inline ExecutionInterface::ExecutionInterface(uint64_t handle) : matlabHandle(handle) { } inline void set_eval_promise_data(void *p) { std::promise* prom = reinterpret_cast*>(p); prom->set_value(); delete prom; } inline void set_eval_promise_exception(void *p, size_t excTypeNumber, const void* msg) { set_promise_exception(p, excTypeNumber, msg); } inline void set_feval_promise_data(void *p, size_t nlhs, bool straight, matlab::data::impl::ArrayImpl** plhs) { if (nlhs == 0 && straight) { std::promise* prom = reinterpret_cast*>(p); prom->set_value(); delete prom; return; } if (nlhs == 1 && straight) { std::promise* prom = reinterpret_cast*>(p); matlab::data::Array v_ = matlab::data::detail::Access::createObj(plhs[0]); prom->set_value(v_); delete prom; return; } std::promise >* prom = reinterpret_cast >*>(p); std::vector result; for (size_t i = 0; i < nlhs; i++) { matlab::data::Array v_ = matlab::data::detail::Access::createObj(plhs[i]); result.push_back(v_); } prom->set_value(result); delete prom; } template void set_exception(T p, std::exception_ptr e) { p->set_exception(e); } inline void set_feval_promise_exception(void *p, size_t nlhs, bool straight, size_t excTypeNumber, const void* msg) { if (nlhs == 0 && straight) { set_promise_exception(p, excTypeNumber, msg); } else if (nlhs == 1 && straight) { set_promise_exception(p, excTypeNumber, msg); } else { set_promise_exception>(p, excTypeNumber, msg); } } inline std::vector ExecutionInterface::feval(const std::u16string &function, const size_t nlhs, const std::vector &args, const std::shared_ptr& output, const std::shared_ptr& error) { return fevalAsync(function, nlhs, args, output, error).get(); } inline std::vector ExecutionInterface::feval(const std::string &function, const size_t nlhs, const std::vector &args, const std::shared_ptr& output, const std::shared_ptr& error) { return feval(std::u16string(function.cbegin(), function.cend()), nlhs, args, output, error); } inline matlab::data::Array ExecutionInterface::feval(const std::u16string &function, const std::vector &args, const std::shared_ptr& output, const std::shared_ptr& error) { FutureResult future = fevalAsync(function, args, output, error); return future.get(); } inline matlab::data::Array ExecutionInterface::feval(const std::string &function, const std::vector &args, const std::shared_ptr& output, const std::shared_ptr& error) { return feval(std::u16string(function.cbegin(), function.cend()), args, output, error); } inline matlab::data::Array ExecutionInterface::feval(const std::u16string &function, const matlab::data::Array &arg, const std::shared_ptr& output, const std::shared_ptr& error) { FutureResult future = fevalAsync(function, arg, output, error); return future.get(); } inline matlab::data::Array ExecutionInterface::feval(const std::string &function, const matlab::data::Array &arg, const std::shared_ptr& output, const std::shared_ptr& error) { return feval(std::u16string(function.cbegin(), function.cend()), arg, output, error); } template ReturnType ExecutionInterface::feval(const std::u16string &function, const std::shared_ptr& output, const std::shared_ptr& error, RhsArgs&&... rhsArgs ) { return fevalAsync(function, output, error, std::forward(rhsArgs)...).get(); } template ReturnType ExecutionInterface::feval(const std::string &function, const std::shared_ptr& output, const std::shared_ptr& error, RhsArgs&&... rhsArgs ) { return feval(std::u16string(function.cbegin(), function.cend()), output, error, std::forward(rhsArgs)...); } namespace detail { template inline void validateTIsSupported() { using U = typename std::remove_cv::type>::type; static_assert( std::is_same::value || std::is_same::value || std::is_same::value || std::is_same::value || std::is_same::value || std::is_same::value || std::is_same::value || std::is_same::value || std::is_same::value || std::is_same::value || std::is_same::value || std::is_same::value, "Attempted to use unsupported types."); } template matlab::data::Array createRhs(matlab::data::ArrayFactory& factory, T&& value) { validateTIsSupported(); using U = typename std::remove_cv::type>::type; return factory.createArray({ 1, 1 }, {value}); } template matlab::data::Array createRhs(matlab::data::ArrayFactory& factory, std::vector && value) { validateTIsSupported(); return factory.createArray({ 1, value.size() }, value.begin(), value.end()); } template struct index_sequence { using value_type = std::size_t; static std::size_t size() { return sizeof...(Ints); } }; template struct make_index_sequence_impl { using type = typename make_index_sequence_impl::type; }; template struct make_index_sequence_impl < 0, Values... > { using type = index_sequence < Values... > ; }; template using make_index_sequence = typename make_index_sequence_impl::type; template struct createLhs { static const size_t nlhs = 1; T operator()(std::vector&& lhs) const { if (lhs.empty()) { throw matlab::execution::TypeConversionException("The result is empty."); } T value; try { value = (*this)(matlab::data::TypedArray(std::move(lhs.front()))); } catch (const std::exception& e) { throw matlab::execution::TypeConversionException(e.what()); } return value; } T operator()(matlab::data::TypedArray lhs) const { validateTIsSupported(); auto const begin = lhs.begin(); auto const end = lhs.end(); if (begin == end) { throw matlab::execution::TypeConversionException("The result is empty."); } return *begin; } }; template<> struct createLhs < void > { static const size_t nlhs = 0; void operator()(std::vector&& lhs) const {} }; template struct createLhs < std::tuple > { static const size_t nlhs = sizeof...(TupleTypes); using T = std::tuple < TupleTypes... > ; T operator()(std::vector&& lhs) const { //we are not validating the LHS here as it can be any combinations of types for std::tuple. if (lhs.size() < sizeof...(TupleTypes)) { throw std::runtime_error(""); } return (*this)(std::move(lhs), detail::make_index_sequence()); } private: template using TupleElement = typename std::remove_cv >::type>::type>::type; template std::tuple operator()(std::vector&& lhs, detail::index_sequence) const { return std::tuple (createLhs>()(std::move(lhs[IndexList]))...); } }; } template ReturnType ExecutionInterface::feval(const std::u16string &function, RhsArgs&&...rhsArgs ) { const std::shared_ptr defaultStream; auto future = fevalAsync(function, defaultStream, defaultStream, std::forward(rhsArgs)...); return future.get(); } template ReturnType ExecutionInterface::feval(const std::string &function, RhsArgs&&...rhsArgs ) { return feval(std::u16string(function.cbegin(), function.cend()), std::forward(rhsArgs)...); } inline ExecutionInterface::~ExecutionInterface() { matlabHandle = 0; } inline FutureResult > ExecutionInterface::fevalAsync(const std::u16string &function, const size_t nlhs, const std::vector &args, const std::shared_ptr &output, const std::shared_ptr &error ) { size_t nrhs = args.size(); std::unique_ptr argsImplPtr(new matlab::data::impl::ArrayImpl*[nrhs], [](matlab::data::impl::ArrayImpl** ptr) { delete[] ptr; }); matlab::data::impl::ArrayImpl** argsImpl = argsImplPtr.get(); size_t i = 0; for (auto e : args) { argsImpl[i++] = matlab::data::detail::Access::getImpl(e); } std::promise >* p = new std::promise >(); std::future > f = p->get_future(); void* output_ = output ? new std::shared_ptr(std::move(output)) : nullptr; void* error_ = error ? new std::shared_ptr(std::move(error)) : nullptr; std::string utf8functionname = convertUTF16StringToASCIIString(function); uintptr_t handle = cpp_engine_feval_with_completion(matlabHandle, utf8functionname.c_str(), nlhs, false, argsImpl, nrhs, &set_feval_promise_data, &set_feval_promise_exception, p, output_, error_, &writeToStreamBuffer, &deleteStreamBufferImpl); return FutureResult>(std::move(f), std::make_shared(handle, cpp_engine_cancel_feval_with_completion )); } inline FutureResult > ExecutionInterface::fevalAsync(const std::string &function, const size_t nlhs, const std::vector &args, const std::shared_ptr &output, const std::shared_ptr &error ) { return fevalAsync(std::u16string(function.cbegin(), function.cend()), nlhs, args, output, error); } inline FutureResult ExecutionInterface::fevalAsync(const std::u16string &function, const std::vector &args, const std::shared_ptr &output, const std::shared_ptr &error ) { size_t nrhs = args.size(); std::unique_ptr argsImplPtr(new matlab::data::impl::ArrayImpl*[nrhs], [](matlab::data::impl::ArrayImpl** ptr) { delete[] ptr; }); matlab::data::impl::ArrayImpl** argsImpl = argsImplPtr.get(); size_t i = 0; for (auto e : args) { argsImpl[i++] = matlab::data::detail::Access::getImpl(e); } std::promise* p = new std::promise(); std::future f = p->get_future(); void* output_ = output ? new std::shared_ptr(std::move(output)) : nullptr; void* error_ = error ? new std::shared_ptr(std::move(error)) : nullptr; std::string utf8functionname = convertUTF16StringToASCIIString(function); uintptr_t handle = cpp_engine_feval_with_completion(matlabHandle, utf8functionname.c_str(), 1, true, argsImpl, nrhs, &set_feval_promise_data, &set_feval_promise_exception, p, output_, error_, &writeToStreamBuffer, &deleteStreamBufferImpl); return FutureResult(std::move(f), std::make_shared(handle, cpp_engine_cancel_feval_with_completion)); } inline FutureResult ExecutionInterface::fevalAsync(const std::string &function, const std::vector &args, const std::shared_ptr &output, const std::shared_ptr &error ) { return fevalAsync(std::u16string(function.cbegin(), function.cend()), args, output, error); } inline FutureResult ExecutionInterface::fevalAsync(const std::u16string &function, const matlab::data::Array &arg, const std::shared_ptr &output, const std::shared_ptr &error ) { return fevalAsync(function, std::vector({ arg }), output, error); } inline FutureResult ExecutionInterface::fevalAsync(const std::string &function, const matlab::data::Array &arg, const std::shared_ptr &output, const std::shared_ptr &error ) { return fevalAsync(std::u16string(function.cbegin(), function.cend()), arg, output, error); } template FutureResult ExecutionInterface::fevalAsync(const std::u16string &function, const std::shared_ptr &output, const std::shared_ptr &error, RhsArgs&&... rhsArgs ) { matlab::data::ArrayFactory factory; std::vector rhsList({ detail::createRhs(factory, std::forward(rhsArgs))... }); auto const nlhs = detail::createLhs::nlhs; size_t nrhs = rhsList.size(); std::unique_ptr argsImplPtr(new matlab::data::impl::ArrayImpl*[nrhs], [](matlab::data::impl::ArrayImpl** ptr) { delete[] ptr; }); matlab::data::impl::ArrayImpl** argsImpl = argsImplPtr.get(); size_t i = 0; for (auto e : rhsList) { argsImpl[i++] = matlab::data::detail::Access::getImpl(e); } FutureResult> f = fevalAsync(function, nlhs, rhsList, output, error); // c++11 lambdas do not correctly handle move operations... // when c++14 is available, this should be: // auto convertToResultType = [copyableF = std::move(f)]()->ReturnType { ....... }; auto copyableF = std::make_shared>>(std::move(f)); auto convertToResultType = [copyableF]() ->ReturnType { std::vector vec = copyableF->get(); detail::createLhs lhsFactory; return lhsFactory(std::move(vec)); }; std::future future = std::async(std::launch::deferred, std::move(convertToResultType)); return FutureResult(std::move(future), copyableF->getTaskReference()); } template FutureResult ExecutionInterface::fevalAsync(const std::string &function, const std::shared_ptr &output, const std::shared_ptr &error, RhsArgs&&... rhsArgs ) { return convertUTF8StringToUTF16String(std::u16string(function.cbegin(), function.cend()), output, error, std::forward(rhsArgs)...); } template FutureResult ExecutionInterface::fevalAsync(const std::u16string &function, RhsArgs&&... rhsArgs ) { const std::shared_ptr defaultBuffer; return fevalAsync(function, defaultBuffer, defaultBuffer, std::forward(rhsArgs)...); } template FutureResult ExecutionInterface::fevalAsync(const std::string &function, RhsArgs&&... rhsArgs ) { return fevalAsync(std::u16string(function.cbegin(), function.cend()), std::forward(rhsArgs)...); } inline std::string ExecutionInterface::convertUTF16StringToASCIIString(const std::u16string &str) { std::unique_ptr asciistr_ptr(new char[str.size()+1]); asciistr_ptr.get()[str.size()] = '\0'; const char* u16_src = reinterpret_cast(str.c_str()); for(size_t n = 0; n < str.size(); ++n) { asciistr_ptr.get()[n] = u16_src[2*n]; } return std::string(asciistr_ptr.get()); } inline void writeToStreamBuffer(void* buffer, const char16_t* stream, size_t n) { std::shared_ptr* output = reinterpret_cast*>(buffer); output->get()->sputn(stream, n); } inline void deleteStreamBufferImpl(void* impl) { delete static_cast*>(impl); } } } #endif /* MATLAB_EXECUTION_INTERFACE_IMPL_HPP */