// Copyright 2017-2020 The MathWorks, Inc. #ifndef MATLAB_DATA_VISITORS_HPP #define MATLAB_DATA_VISITORS_HPP #include #include #include "ArrayType.hpp" #include "TypedArray.hpp" #include "Struct.hpp" #include "Object.hpp" #include "ObjectArray.hpp" #include "StructArray.hpp" #include "EnumArray.hpp" #include "CharArray.hpp" #include "SparseArray.hpp" #include "Exception.hpp" #include "TypedArrayRef.hpp" #include "SparseArrayRef.hpp" #if (__cplusplus >= 201703L) || (_MSVC_LANG >= 201703L) // Using C++17, where std::is_invocable is available. namespace matlab { namespace data { namespace detail { template using is_invocable = std::is_invocable; }}} #else // Unable to find a usable invocable implementation. // Passing Array to the visitor by non-const lvalue reference will be disabled but passing // - by value // - as const& // - as && // - as const&& // is still supported. namespace matlab { namespace data { namespace detail { template using is_invocable = std::false_type; }}} #endif #include namespace matlab { namespace data { namespace detail { template struct is_reference : public std::false_type {}; template struct is_reference> : public std::true_type {}; #define AV_MSG_MUST_BE_ARRAY_TYPE "The array argument must be a matlab::data::Array type." /// If the visitor allows an rvalue reference, this version will be invoked. template::type>::value> struct InvokeVisitor { static_assert(std::is_base_of::type>::value || is_reference::type>::value, AV_MSG_MUST_BE_ARRAY_TYPE); auto operator()(A a, V visitor) const -> decltype(std::forward(visitor)(std::move(a))) { return std::forward(visitor)(std::move(a)); } }; /// If the visitor does not allow an rvalue reference, this version will be invoked. template struct InvokeVisitor { static_assert(std::is_base_of::type>::value || is_reference::type>::value, AV_MSG_MUST_BE_ARRAY_TYPE); auto operator()(A a, V visitor) const -> decltype(std::forward(visitor)(a)) { return std::forward(visitor)(a); } }; template auto invokeVisitor(A&& a, V&& visitor) -> decltype(InvokeVisitor()(std::forward(a), std::forward(visitor))) { return InvokeVisitor()(std::forward(a), std::forward(visitor)); } #define AV_MSG_INCONSISTENT_TYPE "Visitor is returning inconsistent types." template::type>::value> struct ApplyVisitor { using return_type = decltype(invokeVisitor(std::declval(), std::declval())); static_assert(std::is_convertible(std::declval()),std::declval())),return_type>::value, AV_MSG_INCONSISTENT_TYPE); static_assert(std::is_convertible(std::declval()),std::declval())),return_type>::value, AV_MSG_INCONSISTENT_TYPE); static_assert(std::is_convertible(std::declval()),std::declval())),return_type>::value, AV_MSG_INCONSISTENT_TYPE); static_assert(std::is_convertible(std::declval()),std::declval())),return_type>::value, AV_MSG_INCONSISTENT_TYPE); static_assert(std::is_convertible(std::declval()),std::declval())),return_type>::value, AV_MSG_INCONSISTENT_TYPE); static_assert(std::is_convertible(std::declval()),std::declval())),return_type>::value, AV_MSG_INCONSISTENT_TYPE); static_assert(std::is_convertible(std::declval()),std::declval())),return_type>::value, AV_MSG_INCONSISTENT_TYPE); static_assert(std::is_convertible(std::declval()),std::declval())),return_type>::value, AV_MSG_INCONSISTENT_TYPE); static_assert(std::is_convertible(std::declval()),std::declval())),return_type>::value, AV_MSG_INCONSISTENT_TYPE); static_assert(std::is_convertible(std::declval()),std::declval())),return_type>::value, AV_MSG_INCONSISTENT_TYPE); static_assert(std::is_convertible(std::declval()),std::declval())),return_type>::value, AV_MSG_INCONSISTENT_TYPE); static_assert(std::is_convertible(std::declval()),std::declval())),return_type>::value, AV_MSG_INCONSISTENT_TYPE); static_assert(std::is_convertible>(std::declval()),std::declval())),return_type>::value, AV_MSG_INCONSISTENT_TYPE); static_assert(std::is_convertible>(std::declval()),std::declval())),return_type>::value, AV_MSG_INCONSISTENT_TYPE); static_assert(std::is_convertible>(std::declval()),std::declval())),return_type>::value, AV_MSG_INCONSISTENT_TYPE); static_assert(std::is_convertible>(std::declval()),std::declval())),return_type>::value, AV_MSG_INCONSISTENT_TYPE); static_assert(std::is_convertible>(std::declval()),std::declval())),return_type>::value, AV_MSG_INCONSISTENT_TYPE); static_assert(std::is_convertible>(std::declval()),std::declval())),return_type>::value, AV_MSG_INCONSISTENT_TYPE); static_assert(std::is_convertible>(std::declval()),std::declval())),return_type>::value, AV_MSG_INCONSISTENT_TYPE); static_assert(std::is_convertible>(std::declval()),std::declval())),return_type>::value, AV_MSG_INCONSISTENT_TYPE); static_assert(std::is_convertible>(std::declval()),std::declval())),return_type>::value, AV_MSG_INCONSISTENT_TYPE); static_assert(std::is_convertible>(std::declval()),std::declval())),return_type>::value, AV_MSG_INCONSISTENT_TYPE); static_assert(std::is_convertible(std::declval()),std::declval())),return_type>::value, AV_MSG_INCONSISTENT_TYPE); static_assert(std::is_convertible()),std::declval())),return_type>::value, AV_MSG_INCONSISTENT_TYPE); static_assert(std::is_convertible()),std::declval())),return_type>::value, AV_MSG_INCONSISTENT_TYPE); return_type operator()(A a, V visitor) const { switch (a.getType()) { case ArrayType::CHAR: return invokeVisitor(CharArray(std::forward(a)), std::forward(visitor)); case ArrayType::MATLAB_STRING: return invokeVisitor(TypedArray(std::forward(a)), std::forward(visitor)); case ArrayType::LOGICAL: return invokeVisitor(TypedArray(std::forward(a)), std::forward(visitor)); case ArrayType::DOUBLE: return invokeVisitor(TypedArray(std::forward(a)), std::forward(visitor)); case ArrayType::SINGLE: return invokeVisitor(TypedArray(std::forward(a)), std::forward(visitor)); case ArrayType::INT8: return invokeVisitor(TypedArray(std::forward(a)), std::forward(visitor)); case ArrayType::UINT8: return invokeVisitor(TypedArray(std::forward(a)), std::forward(visitor)); case ArrayType::INT16: return invokeVisitor(TypedArray(std::forward(a)), std::forward(visitor)); case ArrayType::UINT16: return invokeVisitor(TypedArray(std::forward(a)), std::forward(visitor)); case ArrayType::INT32: return invokeVisitor(TypedArray(std::forward(a)), std::forward(visitor)); case ArrayType::UINT32: return invokeVisitor(TypedArray(std::forward(a)), std::forward(visitor)); case ArrayType::INT64: return invokeVisitor(TypedArray(std::forward(a)), std::forward(visitor)); case ArrayType::UINT64: return invokeVisitor(TypedArray(std::forward(a)), std::forward(visitor)); case ArrayType::COMPLEX_DOUBLE: return invokeVisitor(TypedArray>(std::forward(a)), std::forward(visitor)); case ArrayType::COMPLEX_SINGLE: return invokeVisitor(TypedArray>(std::forward(a)), std::forward(visitor)); case ArrayType::COMPLEX_INT8: return invokeVisitor(TypedArray>(std::forward(a)), std::forward(visitor)); case ArrayType::COMPLEX_UINT8: return invokeVisitor(TypedArray>(std::forward(a)), std::forward(visitor)); case ArrayType::COMPLEX_INT16: return invokeVisitor(TypedArray>(std::forward(a)), std::forward(visitor)); case ArrayType::COMPLEX_UINT16: return invokeVisitor(TypedArray>(std::forward(a)), std::forward(visitor)); case ArrayType::COMPLEX_INT32: return invokeVisitor(TypedArray>(std::forward(a)), std::forward(visitor)); case ArrayType::COMPLEX_UINT32: return invokeVisitor(TypedArray>(std::forward(a)), std::forward(visitor)); case ArrayType::COMPLEX_INT64: return invokeVisitor(TypedArray>(std::forward(a)), std::forward(visitor)); case ArrayType::COMPLEX_UINT64: return invokeVisitor(TypedArray>(std::forward(a)), std::forward(visitor)); case ArrayType::CELL: return invokeVisitor(TypedArray(std::forward(a)), std::forward(visitor)); case ArrayType::STRUCT: return invokeVisitor(StructArray(std::forward(a)), std::forward(visitor)); case ArrayType::VALUE_OBJECT: return invokeVisitor(ObjectArray(std::forward(a)), std::forward(visitor)); case ArrayType::HANDLE_OBJECT_REF: return invokeVisitor(ObjectArray(std::forward(a)), std::forward(visitor)); case ArrayType::ENUM: return invokeVisitor(EnumArray(std::forward(a)), std::forward(visitor)); case ArrayType::SPARSE_LOGICAL: return invokeVisitor(SparseArray(std::forward(a)), std::forward(visitor)); case ArrayType::SPARSE_DOUBLE: return invokeVisitor(SparseArray(std::forward(a)), std::forward(visitor)); case ArrayType::SPARSE_COMPLEX_DOUBLE: return invokeVisitor(SparseArray>(std::forward(a)), std::forward(visitor)); default: throw InvalidArrayTypeException("Invalid type of array passed to apply_visitor."); break; } // switch } }; template struct ApplyVisitor { using return_type = decltype(invokeVisitor(std::declval(), std::declval())); return_type operator()(A a, V visitor) const { Array array = std::forward(a); return ApplyVisitor()(std::move(array), std::forward(visitor)); } }; } /** * apply_visitor allows the caller to pass in an instance of Array (or one of * its subclasses) and a visitor functor, and invokes the operator() method, which must be * defined in the user-defined functor, with the appropriate concrete array type. The array, * and the visitor, can be passed in by value, by const lvalue ref, by nonconst lvalue ref, * or by rvalue ref. They will be copied, modified, or moved accordingly. * * The return type of a functor is void by default. Functors can optionally return values * of any arbitrary type. Note that the return type for all overloads must be convertible * to the type specified in the signature. * * @param an Array to pass into the functor * @param visitor functor class * @return result type defined inside the functor or void, if none has been defined. * @throw UnknownMatlabArrayDataType exception */ template auto apply_visitor(A&& a, V&& visitor) -> decltype(detail::ApplyVisitor()(std::forward(a), std::forward(visitor))) { return detail::ApplyVisitor()(std::forward(a), std::forward(visitor)); } /** * apply_numeric_visitor is like apply_visitor, but only applies to numeric types * (including char, logical, and complex). If types like StructArray need to be * covered, apply_visitor should be used. This is the lvalue ref version. * * @param an Array to pass into the functor * @param visitor functor class * @return result_type defined inside the functor, or void if none has been defined * @throw UnknownMatlabArrayDataType exception */ template auto apply_numeric_visitor(A&& a, V&& visitor, typename std::enable_if::type>::value, const void *>::type = nullptr) -> decltype (detail::invokeVisitor(std::declval(), std::forward(visitor))) { switch (a.getType()) { case ArrayType::CHAR: return detail::invokeVisitor(CharArray(std::forward(a)), std::forward(visitor)); case ArrayType::LOGICAL: return detail::invokeVisitor(TypedArray(std::forward(a)), std::forward(visitor)); case ArrayType::DOUBLE: return detail::invokeVisitor(TypedArray(std::forward(a)), std::forward(visitor)); case ArrayType::SINGLE: return detail::invokeVisitor(TypedArray(std::forward(a)), std::forward(visitor)); case ArrayType::INT8: return detail::invokeVisitor(TypedArray(std::forward(a)), std::forward(visitor)); case ArrayType::UINT8: return detail::invokeVisitor(TypedArray(std::forward(a)), std::forward(visitor)); case ArrayType::INT16: return detail::invokeVisitor(TypedArray(std::forward(a)), std::forward(visitor)); case ArrayType::UINT16: return detail::invokeVisitor(TypedArray(std::forward(a)), std::forward(visitor)); case ArrayType::INT32: return detail::invokeVisitor(TypedArray(std::forward(a)), std::forward(visitor)); case ArrayType::UINT32: return detail::invokeVisitor(TypedArray(std::forward(a)), std::forward(visitor)); case ArrayType::INT64: return detail::invokeVisitor(TypedArray(std::forward(a)), std::forward(visitor)); case ArrayType::UINT64: return detail::invokeVisitor(TypedArray(std::forward(a)), std::forward(visitor)); case ArrayType::COMPLEX_DOUBLE: return detail::invokeVisitor(TypedArray>(std::forward(a)), std::forward(visitor)); case ArrayType::COMPLEX_SINGLE: return detail::invokeVisitor(TypedArray>(std::forward(a)), std::forward(visitor)); case ArrayType::COMPLEX_INT8: return detail::invokeVisitor(TypedArray>(std::forward(a)), std::forward(visitor)); case ArrayType::COMPLEX_UINT8: return detail::invokeVisitor(TypedArray>(std::forward(a)), std::forward(visitor)); case ArrayType::COMPLEX_INT16: return detail::invokeVisitor(TypedArray>(std::forward(a)), std::forward(visitor)); case ArrayType::COMPLEX_UINT16: return detail::invokeVisitor(TypedArray>(std::forward(a)), std::forward(visitor)); case ArrayType::COMPLEX_INT32: return detail::invokeVisitor(TypedArray>(std::forward(a)), std::forward(visitor)); case ArrayType::COMPLEX_UINT32: return detail::invokeVisitor(TypedArray>(std::forward(a)), std::forward(visitor)); case ArrayType::COMPLEX_INT64: return detail::invokeVisitor(TypedArray>(std::forward(a)), std::forward(visitor)); case ArrayType::COMPLEX_UINT64: return detail::invokeVisitor(TypedArray>(std::forward(a)), std::forward(visitor)); default: throw InvalidArrayTypeException("Invalid type of array passed to apply_numeric_visitor."); break; } /* switch */ } /* apply_numeric_visitor */ template auto apply_numeric_visitor(A&& a, V&& visitor, typename std::enable_if::type>::value, const void *>::type = nullptr) -> decltype(apply_visitor(std::move(a), std::forward(visitor))) { Array array = a; return apply_visitor(std::move(array), std::forward(visitor)); } /** * apply_visitor_ref allows the caller to modify an element of a matlab::data::Array using a * passed-in functor whose operator() for the appropriate type will be invoked. * @param a matlab::data::ArrayRef pointing to an element that is to be modified * @param visitor functor class whose operator() will be invoked * @return the return type defined within the functor, or void if none has been defined */ template auto apply_visitor_ref(const ArrayRef& a, V visitor) -> decltype (visitor(static_cast(a))) { switch (a.getType()) { case ArrayType::CHAR: return visitor(static_cast(a)); case ArrayType::MATLAB_STRING: return visitor(static_cast>(a)); case ArrayType::LOGICAL: return visitor(static_cast>(a)); case ArrayType::DOUBLE: return visitor(static_cast>(a)); case ArrayType::SINGLE: return visitor(static_cast>(a)); case ArrayType::INT8: return visitor(static_cast>(a)); case ArrayType::UINT8: return visitor(static_cast>(a)); case ArrayType::INT16: return visitor(static_cast>(a)); case ArrayType::UINT16: return visitor(static_cast>(a)); case ArrayType::INT32: return visitor(static_cast>(a)); case ArrayType::UINT32: return visitor(static_cast>(a)); case ArrayType::INT64: return visitor(static_cast>(a)); case ArrayType::UINT64: return visitor(static_cast>(a)); case ArrayType::COMPLEX_DOUBLE: return visitor(static_cast>>(a)); case ArrayType::COMPLEX_SINGLE: return visitor(static_cast>>(a)); case ArrayType::COMPLEX_INT8: return visitor(static_cast>>(a)); case ArrayType::COMPLEX_UINT8: return visitor(static_cast>>(a)); case ArrayType::COMPLEX_INT16: return visitor(static_cast>>(a)); case ArrayType::COMPLEX_UINT16: return visitor(static_cast>>(a)); case ArrayType::COMPLEX_INT32: return visitor(static_cast>>(a)); case ArrayType::COMPLEX_UINT32: return visitor(static_cast>>(a)); case ArrayType::COMPLEX_INT64: return visitor(static_cast>>(a)); case ArrayType::COMPLEX_UINT64: return visitor(static_cast>>(a)); case ArrayType::CELL: return visitor(static_cast>(a)); case ArrayType::STRUCT: return visitor(static_cast(a)); case ArrayType::VALUE_OBJECT: return visitor(static_cast>(a)); case ArrayType::HANDLE_OBJECT_REF: return visitor(static_cast>(a)); case ArrayType::ENUM: return visitor(static_cast(a)); case ArrayType::SPARSE_LOGICAL: return visitor(static_cast>(a)); case ArrayType::SPARSE_DOUBLE: return visitor(static_cast>(a)); case ArrayType::SPARSE_COMPLEX_DOUBLE: return visitor(static_cast>>(a)); default: throw InvalidArrayTypeException("Invalid type of array passed to apply_visitor_ref."); break; } /* switch */ } /* apply_visitor_ref */ } } #endif