diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fde22ba0..f4edacf3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,8 +5,6 @@ on: pull_request: env: - # commit from vcpkg's master branch on 2020/10/06 - vcpkg_TAG: 76a7e9248fb3c57350b559966dcaa2d52a5e4458 Catch2_TAG: v2.11.3 jobs: @@ -16,7 +14,7 @@ jobs: strategy: matrix: build_type: [Release, Debug] - os: [ubuntu-latest, windows-latest, macOS-latest] + os: [ubuntu-latest, macOS-latest] fail-fast: false steps: @@ -39,15 +37,6 @@ jobs: for apt_file in `grep -lr microsoft /etc/apt/sources.list.d/`; do sudo rm $apt_file; done - # Restore from cache the previously built ports. If "cache miss" - # then provision vcpkg, install desired ports, finally cache everything for the next run. - - name: Dependencies [Windows] - if: matrix.os == 'windows-latest' - uses: lukka/run-vcpkg@v6 - with: - vcpkgArguments: '--triplet x64-windows matio catch2' - vcpkgGitCommitId: ${{ env.vcpkg_TAG }} - - name: Dependencies [macOS] if: matrix.os == 'macOS-latest' run: | @@ -58,7 +47,7 @@ jobs: if: matrix.os == 'ubuntu-latest' run: | sudo apt-get update - sudo apt-get install libmatio-dev valgrind + sudo apt-get install libmatio-dev valgrind libeigen3-dev - name: Cache Source-based Dependencies id: cache-source-deps @@ -88,17 +77,6 @@ jobs: # CMAKE-BASED PROJECT # =================== - - name: Configure [Windows] - # Use bash also on Windows (otherwise cd, mkdir, ... do not work) - if: matrix.os == 'windows-latest' - shell: bash - run: | - mkdir -p build - cd build - cmake -A x64 -DCMAKE_TOOLCHAIN_FILE=${VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake \ - -DCMAKE_PREFIX_PATH=${GITHUB_WORKSPACE}/install/deps \ - -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} -DCMAKE_INSTALL_PREFIX=${GITHUB_WORKSPACE}/install \ - -DBUILD_TESTING:BOOL=ON .. - name: Configure [Ubuntu] if: matrix.os == 'ubuntu-latest' diff --git a/.github/workflows/conda-forge-ci.yml b/.github/workflows/conda-forge-ci.yml index 95f5b17b..c5055a71 100644 --- a/.github/workflows/conda-forge-ci.yml +++ b/.github/workflows/conda-forge-ci.yml @@ -15,7 +15,7 @@ jobs: strategy: matrix: build_type: [Release] - os: [ubuntu-latest, windows-latest, macOS-latest] + os: [ubuntu-latest, windows-2019, macOS-latest] fail-fast: false steps: @@ -31,9 +31,9 @@ jobs: shell: bash -l {0} run: | # Compilation related dependencies - mamba install cmake compilers make ninja pkg-config + mamba install cmake compilers make ninja pkg-config # Actual dependencies - mamba install libmatio catch2 + mamba install libmatio catch2 eigen - name: Configure [Linux&macOS] if: contains(matrix.os, 'macos') || contains(matrix.os, 'ubuntu') diff --git a/CHANGELOG.md b/CHANGELOG.md index 237ac7c9..50718a72 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] - Using a custom variable in InstallBasicPackageFile to backup the module path: [#47](https://github.com/ami-iit/matio-cpp/pull/47). - Added more checks before creating a file: [#49](https://github.com/ami-iit/matio-cpp/pull/49). +- Added reflection and conversions from common types [#55](https://github.com/ami-iit/matio-cpp/pull/55) ## [0.1.1] - 2021-03-22 diff --git a/CMakeLists.txt b/CMakeLists.txt index ce5500e1..9614a3fd 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,7 +3,7 @@ # BSD-2-Clause license (https://opensource.org/licenses/BSD-2-Clause). cmake_minimum_required(VERSION 3.10) -project(matioCpp VERSION 0.1.1 LANGUAGES CXX) +project(matioCpp VERSION 0.2.0 LANGUAGES CXX) # Defines the CMAKE_INSTALL_LIBDIR, CMAKE_INSTALL_BINDIR and many other useful macros. # See https://cmake.org/cmake/help/latest/module/GNUInstallDirs.html @@ -89,6 +89,30 @@ endif() ## Dependencies find_package(MATIO REQUIRED) +find_package(Eigen3 QUIET) + +if (Eigen3_FOUND) + set(MATIOCPP_HAS_EIGEN TRUE) +endif() + +# Fetching visit_struct +include(CMakeDependentOption) +find_package(visit_struct QUIET) +cmake_dependent_option(USE_SYSTEM_visit_struct "Use system visit_struct" ON "visit_struct_FOUND" OFF) +if(USE_SYSTEM_visit_struct) + find_package(visit_struct REQUIRED) +else() + include(FetchContent) + FetchContent_Declare(visit_struct + GIT_REPOSITORY https://github.com/ami-iit/visit_struct + GIT_TAG 969fc563477906432a9fcc91addf2c1e13c56f4c) + + FetchContent_GetProperties(visit_struct) + if(NOT visit_struct_POPULATED) + message(STATUS "Fetching visit_struct...") + FetchContent_MakeAvailable(visit_struct) + endif() +endif() set(MATIOCPP_SRC src/Variable.cpp src/ConversionUtilities.cpp @@ -98,11 +122,14 @@ set(MATIOCPP_SRC src/Variable.cpp src/CellArray.cpp src/File.cpp src/Struct.cpp - src/StructArray.cpp) + src/StructArray.cpp + src/ExogenousConversions.cpp) set(MATIOCPP_HDR include/matioCpp/Span.h include/matioCpp/VectorIterator.h include/matioCpp/ConversionUtilities.h + include/matioCpp/ExogenousConversions.h + include/matioCpp/EigenConversions.h include/matioCpp/Variable.h include/matioCpp/ForwardDeclarations.h include/matioCpp/Vector.h @@ -121,7 +148,10 @@ set(MATIOCPP_TPP include/matioCpp/impl/Vector.tpp include/matioCpp/impl/MultiDimensionalArray.tpp include/matioCpp/impl/Element.tpp include/matioCpp/impl/StructArrayElement.tpp - include/matioCpp/impl/File.tpp) + include/matioCpp/impl/File.tpp + include/matioCpp/impl/EigenConversions.tpp + include/matioCpp/impl/ExogenousConversions.tpp + include/matioCpp/impl/ExogenousConversionHelpers.tpp) source_group("Template Implementation Files" FILES ${MATIOCPP_TPP}) @@ -149,7 +179,13 @@ target_include_directories(matioCpp PUBLIC "$" "$/${CMAKE_INSTALL_INCLUDEDIR}>") -target_link_libraries(matioCpp PUBLIC MATIO::MATIO) +target_link_libraries(matioCpp PUBLIC MATIO::MATIO visit_struct::visit_struct) +list(APPEND MATIOCPP_DEPENDENCIES MATIO visit_struct) + +if (Eigen3_FOUND) + target_link_libraries(matioCpp PUBLIC Eigen3::Eigen) + list(APPEND MATIOCPP_DEPENDENCIES Eigen3) +endif() target_compile_features(matioCpp PUBLIC cxx_std_14) @@ -182,7 +218,7 @@ install_basic_package_files(${PROJECT_NAME} COMPATIBILITY AnyNewerVersion VARS_PREFIX ${PROJECT_NAME} NO_CHECK_REQUIRED_COMPONENTS_MACRO - DEPENDENCIES MATIO + DEPENDENCIES ${MATIOCPP_DEPENDENCIES} OVERRIDE_MODULE_PATH ${CMAKE_INSTALL_PREFIX}/share/matioCpp/cmake) # Add the uninstall target include(AddUninstallTarget) diff --git a/README.md b/README.md index 5274781d..d99d2aad 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,8 @@ The depencies are [``CMake``](https://cmake.org/) (minimum version 3.10) and [`` - macOS: ``brew install libmatio`` - Windows, via [``vcpkg``](https://github.com/microsoft/vcpkg): ``vcpkg install --triplet x64-windows matio`` +[`Eigen`](https://eigen.tuxfamily.org/index.php) is an optional dependency. If available, some conversions are defined. + For running the tests, it is necessary to install [`Catch2`](https://github.com/catchorg/Catch2). Where supported, [``valgrind``](https://valgrind.org/) can be installed to check for memory leaks. ## Linux/macOS @@ -109,6 +111,71 @@ file.write(testString); ``` +It is possibile to convert common types to ``matioCpp`` types with the function ``matioCpp::make_variable``. Examples: +```c++ +std::vector stdVec = {1.0, 2.0, 3.0, 4.0, 5.0}; +auto toMatioVec = matioCpp::make_variable("test", stdVec); + +std::array array = {1.0, 2.0, 3.0}; +auto toMatioArray = matioCpp::make_variable("test", array); + +int classicalArray[] = {1, 2, 3}; +auto toMatioClassic = matioCpp::make_variable("test", matioCpp::make_span(classicalArray, 3)); + +std::string string("something"); +auto toMatioString = matioCpp::make_variable("name", string); + +std::vector vecOfBool = {true, false, true}; +auto toVecofBool = matioCpp::make_variable("vecOfBool", vecOfBool); + +auto matioDouble = matioCpp::make_variable("double", 5.0); + +auto matioBool = matioCpp::make_variable("bool", true); + +auto matioInt = matioCpp::make_variable("int", 2); + +auto matioChar = matioCpp::make_variable("char", 'f'); + +std::vector stringVector = {"Huey", "Dewey", "Louie"}; +auto matioCell = matioCpp::make_variable("stringVector", stringVector); +``` +If ``eigen`` is available, it is also possible to convert from and to ``eigen`` types: +```c++ +matioCpp::Vector vector("vector", 5); +Eigen::VectorXd eigenVec = matioCpp::to_eigen(vector); +matioCpp::MultiDimensionalArray matioCppMatrix("matrix"); + +Eigen::MatrixXf toEigenMatrix = matioCpp::to_eigen(matioCppMatrix); + +Eigen::Matrix3f eigenMatrix; +eigenMatrix << 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0; +auto toMatioMatrix = matioCpp::make_variable("testMatrix", eigenMatrix); +Eigen::Vector3i eigenVec; +eigenVec << 2, 4, 6; +auto toMatioEigenVec = matioCpp::make_variable("testEigen", eigenVec); +``` + +``matioCpp`` also exploits [``visit_struct``](https://github.com/garbageslam/visit_struct) to parse C++ structs into ``matioCpp`` structs. Example: +```c++ +struct testStruct +{ + int i{1}; + double d{2.0}; + std::string s{"test"}; + std::vector stdVec = {1.0, 2.0, 3.0, 4.0, 5.0}; + int* notSupported = nullptr; + std::vector stringVector = {"Huey", "Dewey", "Louie"}; + std::vector vecOfBool = {true, false, true}; +}; +VISITABLE_STRUCT(testStruct, i, d, s, stdVec, vecOfBool, stringVector); + +//---------- + +testStruct s; +matioCpp::Struct automaticStruct = matioCpp::make_variable("testStruct", s); +``` + + # Known Limitations - Complex arrays are not yet supported - Cannot read timeseries from a ``.mat`` file (this is a ``matio`` limitation https://github.com/tbeu/matio/issues/99) diff --git a/cmake/Config.h.in b/cmake/Config.h.in index 9ed1f556..459781ff 100644 --- a/cmake/Config.h.in +++ b/cmake/Config.h.in @@ -3,8 +3,6 @@ // This is an automatically generated file. // Please DO NOT edit this file manually. -#ifndef MATIOCPP_CONFIG_H -#define MATIOCPP_CONFIG_H /* * Copyright (C) 2020 Fondazione Istituto Italiano di Tecnologia * @@ -12,6 +10,13 @@ * BSD-2-Clause license (https://opensource.org/licenses/BSD-2-Clause). */ +#ifndef MATIOCPP_CONFIG_H +#define MATIOCPP_CONFIG_H + #define MATIOCPP_VER "@PROJECT_VERSION@" +#ifndef MATIOCPP_NO_EIGEN +#cmakedefine MATIOCPP_HAS_EIGEN +#endif + #endif // MATIOCPP_CONFIG_H diff --git a/include/matioCpp/ConversionUtilities.h b/include/matioCpp/ConversionUtilities.h index 9d3e89b0..5894f80a 100644 --- a/include/matioCpp/ConversionUtilities.h +++ b/include/matioCpp/ConversionUtilities.h @@ -46,20 +46,28 @@ template struct get_type static_assert (matioCpp::dependent_false::value, "Unsupported type."); static_assert (!std::is_same::value, "Use matioCpp::Logical instead of bool."); }; + +#ifdef __APPLE__ +using size_t_type = unsigned long; +#else +using size_t_type = uint64_t; +#endif + // specializations -template <> struct get_type { using type = int8_t; static inline ValueType valueType(){return ValueType::INT8;}; static inline std::string toString(){return "int8_t" ;};}; -template <> struct get_type { using type = uint8_t; static inline ValueType valueType(){return ValueType::UINT8;}; static inline std::string toString(){return "uint8_t" ;};}; -template <> struct get_type { using type = int16_t; static inline ValueType valueType(){return ValueType::INT16;}; static inline std::string toString(){return "int16_t" ;};}; -template <> struct get_type { using type = uint16_t; static inline ValueType valueType(){return ValueType::UINT16;}; static inline std::string toString(){return "uint16_t" ;};}; -template <> struct get_type { using type = int32_t; static inline ValueType valueType(){return ValueType::INT32;}; static inline std::string toString(){return "int32_t" ;};}; -template <> struct get_type { using type = uint32_t; static inline ValueType valueType(){return ValueType::UINT32;}; static inline std::string toString(){return "uint32_t" ;};}; -template <> struct get_type { using type = float; static inline ValueType valueType(){return ValueType::SINGLE;}; static inline std::string toString(){return "float" ;};}; -template <> struct get_type { using type = double; static inline ValueType valueType(){return ValueType::DOUBLE;}; static inline std::string toString(){return "double" ;};}; -template <> struct get_type { using type = int64_t; static inline ValueType valueType(){return ValueType::INT64;}; static inline std::string toString(){return "int64_t" ;};}; -template <> struct get_type { using type = char; static inline ValueType valueType(){return ValueType::UTF8;}; static inline std::string toString(){return "char" ;};}; -template <> struct get_type { using type = char16_t; static inline ValueType valueType(){return ValueType::UTF16;}; static inline std::string toString(){return "char16_t" ;};}; -template <> struct get_type { using type = char32_t; static inline ValueType valueType(){return ValueType::UTF32;}; static inline std::string toString(){return "char32_t" ;};}; -template <> struct get_type { using type = uint8_t; static inline ValueType valueType(){return ValueType::LOGICAL;}; static inline std::string toString(){return "matioCpp::Logical" ;};}; +template <> struct get_type { using type = int8_t; static inline ValueType valueType(){return ValueType::INT8;}; static inline std::string toString(){return "int8_t" ;};}; +template <> struct get_type { using type = uint8_t; static inline ValueType valueType(){return ValueType::UINT8;}; static inline std::string toString(){return "uint8_t" ;};}; +template <> struct get_type { using type = int16_t; static inline ValueType valueType(){return ValueType::INT16;}; static inline std::string toString(){return "int16_t" ;};}; +template <> struct get_type { using type = uint16_t; static inline ValueType valueType(){return ValueType::UINT16;}; static inline std::string toString(){return "uint16_t" ;};}; +template <> struct get_type { using type = int32_t; static inline ValueType valueType(){return ValueType::INT32;}; static inline std::string toString(){return "int32_t" ;};}; +template <> struct get_type { using type = uint32_t; static inline ValueType valueType(){return ValueType::UINT32;}; static inline std::string toString(){return "uint32_t" ;};}; +template <> struct get_type { using type = float; static inline ValueType valueType(){return ValueType::SINGLE;}; static inline std::string toString(){return "float" ;};}; +template <> struct get_type { using type = double; static inline ValueType valueType(){return ValueType::DOUBLE;}; static inline std::string toString(){return "double" ;};}; +template <> struct get_type { using type = int64_t; static inline ValueType valueType(){return ValueType::INT64;}; static inline std::string toString(){return "int64_t" ;};}; +template <> struct get_type { using type = size_t_type; static inline ValueType valueType(){return ValueType::UINT64;}; static inline std::string toString(){return "size_t" ;};}; +template <> struct get_type { using type = char; static inline ValueType valueType(){return ValueType::UTF8;}; static inline std::string toString(){return "char" ;};}; +template <> struct get_type { using type = char16_t; static inline ValueType valueType(){return ValueType::UTF16;}; static inline std::string toString(){return "char16_t" ;};}; +template <> struct get_type { using type = char32_t; static inline ValueType valueType(){return ValueType::UTF32;}; static inline std::string toString(){return "char32_t" ;};}; +template <> struct get_type { using type = uint8_t; static inline ValueType valueType(){return ValueType::LOGICAL;}; static inline std::string toString(){return "matioCpp::Logical" ;};}; /** * @brief Utility meta-function to check if a type is compatible with a std::string @@ -123,7 +131,7 @@ bool is_convertible_to_primitive_type(matioCpp::ValueType type) return std::is_same::value; break; case matioCpp::ValueType::UINT64: - return std::is_same::value; + return std::is_same::value; break; case matioCpp::ValueType::UTF8: return (std::is_same::value || std::is_same::value); diff --git a/include/matioCpp/EigenConversions.h b/include/matioCpp/EigenConversions.h new file mode 100644 index 00000000..f437ee05 --- /dev/null +++ b/include/matioCpp/EigenConversions.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2022 Fondazione Istituto Italiano di Tecnologia + * + * This software may be modified and distributed under the terms of the + * BSD-2-Clause license (https://opensource.org/licenses/BSD-2-Clause). + */ + +#ifndef MATIOCPP_EIGENCONVERSIONS_H +#define MATIOCPP_EIGENCONVERSIONS_H + +#include + +#ifdef MATIOCPP_HAS_EIGEN + +#include +#include + +#include + +namespace matioCpp +{ + +/** + * @brief Conversion from a MultiDimensionalArray to an Eigen matrix + * @param input The MultiDimensionalArray + * @return A map from the internal data of the MultiDimensionalArray + */ +template +inline Eigen::Map> to_eigen(MultiDimensionalArray& input); + +/** + * @brief Conversion from a const MultiDimensionalArray to an Eigen matrix + * @param input The MultiDimensionalArray + * @return A const map from the internal data of the MultiDimensionalArray + */ +template +inline const Eigen::Map> to_eigen(const MultiDimensionalArray& input); + +/** + * @brief Conversion from a Vector to an Eigen vector + * @param input The Vector + * @return A map from the internal data of the Vector + */ +template +inline Eigen::Map> to_eigen(Vector& input); + +/** + * @brief Conversion from a const Vector to an Eigen vector + * @param input The Vector + * @return A const map from the internal data of the Vector + */ +template +inline const Eigen::Map> to_eigen(const Vector& input); + +/** + * @brief Conversion from an Eigen matrix to a MultiDimensionalArray + * @param name The name of the resulting matioCpp variable. + * @param input The input matrix. + * @return A MultiDimensionalArray containing a copy of the input data + */ +template ::RowsAtCompileTime != 1 && + Eigen::MatrixBase::ColsAtCompileTime != 1>> +inline MultiDimensionalArray make_variable(const std::string& name, const Eigen::MatrixBase& input); + +} + +#include "impl/EigenConversions.tpp" + +#endif + + +#endif // MATIOCPP_EIGENCONVERSIONS_H diff --git a/include/matioCpp/Element.h b/include/matioCpp/Element.h index 8d3e781c..6d05e6ea 100644 --- a/include/matioCpp/Element.h +++ b/include/matioCpp/Element.h @@ -105,6 +105,16 @@ class matioCpp::Element : public matioCpp::Variable */ Element& operator=(element_type value); + /** + * @brief Get this Vector as a Span + */ + matioCpp::Span toSpan(); + + /** + * @brief Get this Vector as a Span (const version) + */ + const matioCpp::Span toSpan() const; + /** * @brief Change the name of the Variable * @param newName The new name diff --git a/include/matioCpp/ExogenousConversions.h b/include/matioCpp/ExogenousConversions.h new file mode 100644 index 00000000..b900a747 --- /dev/null +++ b/include/matioCpp/ExogenousConversions.h @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2022 Fondazione Istituto Italiano di Tecnologia + * + * This software may be modified and distributed under the terms of the + * BSD-2-Clause license (https://opensource.org/licenses/BSD-2-Clause). + */ +#ifndef MATIOCPP_EXOGENOUSCONVERSION_H +#define MATIOCPP_EXOGENOUSCONVERSION_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "impl/ExogenousConversionHelpers.tpp" + +namespace matioCpp +{ + +/** + * @brief Conversion from a generic vector to a matioCpp::Vector + * @param name The name of the resulting matioCpp variable. + * @param input The input vector. + * @return A matioCpp::Vector containing a copy of the input data + */ +template ::value && + !std::is_same::value>> +inline matioCpp::Vector::type>> make_variable(const std::string& name, const Vector& input); + +/** + * @brief Conversion from a std::string to a matioCpp::String + * @param name The name of the resulting matioCpp variable. + * @param input The input string. + * @return A matioCpp::String containing a copy of the input data + */ +matioCpp::String make_variable(const std::string& name, const std::string& input); + +/** + * @brief Conversion from a boolean vector to a matioCpp::Vector + * @param name The name of the resulting matioCpp variable. + * @param input The input vector. + * @return A matioCpp::Vector containing a copy of the input data + */ +matioCpp::Vector make_variable(const std::string& name, const std::vector& input); + +/** + * @brief Conversion from a fundamental type to the corresponding matioCpp::Element + * @param name The name of the resulting matioCpp variable. + * @param input The input element. + * @return A matioCpp::Element containing a copy of the input data + */ +template::value && !std::is_same::value>::type> +inline matioCpp::Element make_variable(const std::string& name, const type& input); + +/** + * @brief Conversion from a boolean to a matioCpp::Element + * @param name The name of the resulting matioCpp variable. + * @param input The input element. + * @return A matioCpp::Element whose value is equal to the input. + */ +matioCpp::Element make_variable(const std::string& name, bool input); + +/** + * @brief Conversion from a vector of strings to a matioCpp::CellArray containing the input strings + * @param name The name of the resulting matioCpp variable. + * @param input The input vector of strings. + * @return A matioCpp::CellArray of dimensions nx1 (with n the number of strings) + */ +matioCpp::CellArray make_variable(const std::string& name, const std::vector& input); + +/** + * @brief Conversion from a visitable struct to a matioCpp::Struct. + * See https://github.com/garbageslam/visit_struct on how to make a Struct "visitable" + * @param name The name of the resulting matioCpp variable. + * @param input The input struct. + * @return A matioCpp::Struct containing the visitable fields + */ +template::value>> +inline matioCpp::Struct make_variable(const std::string& name, const Struct& input); + +/** + * @brief Create a matioCpp::Struct starting from the begin and end iterators of a map-like container + * The dereferenced value of the iterator has to be a pair (like with std::maps and std::unordered_map) + * with the key being a string. For each key, there is the corresponding field in the Struct. + * @param name The name of the struct. + * @param begin The iterator to the first element + * @param end The iterator to the element after the last. + * @return The corresponding matioCpp::Struct + */ +template::value>> +inline matioCpp::Struct make_struct(const std::string& name, iterator begin, iterator end); + +/** + * @brief Create a matioCpp::CellArray starting from the begin and end iterators of a container. + * It need the dereferenced value of the iterator to be a pair (like with std::maps and std::unordered_map). + * The first element is used as name, while the second is the value. + * @param name The name of the CellArray. + * @param begin The iterator to the first element + * @param end The iterator to the element after the last. + * @return The corresponding matioCpp::CellArray. + */ +template::value>* = nullptr> +inline matioCpp::CellArray make_cell_array(const std::string& name, const iterator& begin, const iterator& end); + +/** + * @brief Create a matioCpp::CellArray starting from the begin and end iterators of a container. + * The name of the imported variable in the CellArray + * is "imported_element_x", where "x" is the corresponding raw index. + * @param name The name of the CellArray. + * @param begin The iterator to the first element + * @param end The iterator to the element after the last. + * @return The corresponding matioCpp::CellArray. + */ +template())>::value>* = nullptr> +inline matioCpp::CellArray make_cell_array(const std::string& name, const iterator& begin, const iterator& end); + +/** + * @brief is_make_variable_callable is a template utility to check if the make_variable works for a give type + */ +template +struct is_make_variable_callable : std::false_type +{}; + +template +struct is_make_variable_callable(), std::declval()))>> : std::true_type +{ +}; + +/** + * @brief make_variable_output is a template utility to check the type that make_variable would output. void is case make_variable is not callable + */ +template +struct make_variable_output +{ + using type = typename std::conditional_t::value, decltype(matioCpp::make_variable(std::declval(), std::declval())), void>; +}; + +} + +#include "impl/ExogenousConversions.tpp" + +#endif // MATIOCPP_EXOGENOUSCONVERSION_H diff --git a/include/matioCpp/ForwardDeclarations.h b/include/matioCpp/ForwardDeclarations.h index 85bf47a9..736c7631 100644 --- a/include/matioCpp/ForwardDeclarations.h +++ b/include/matioCpp/ForwardDeclarations.h @@ -30,6 +30,8 @@ #include +#include + #ifdef _MSC_VER #pragma warning(push) diff --git a/include/matioCpp/Span.h b/include/matioCpp/Span.h index 794a6d15..e9eaf578 100644 --- a/include/matioCpp/Span.h +++ b/include/matioCpp/Span.h @@ -76,6 +76,54 @@ template struct has_size_method().size())>> : std::true_type { }; + +//Small utility to detect if class T defines value_type +template< class, typename = void > +struct has_type_member : std::false_type { }; + +template< class T > +struct has_type_member> : std::true_type { }; + +//container_data is a utility metafunction to detect the type of container. If T is not a supported container, it throws + // an assertion at compile time. +template +struct container_data +{ + static_assert(dependent_false::value, "Unable to detect type of data in the container."); +}; + +template +struct container_data::value>::type> +{ + using type = typename T::value_type; +}; + +// This specialization is enabled if T::value_type<\code> is not available, but the method data()<\code> is. +template +struct container_data::value && has_data_method::value>::type> +{ + using type = typename std::remove_pointer().data())>::type; +}; + +//This specialization is enabled if T is an array. +template +struct container_data::value>::type> +{ + using type = typename std::remove_all_extents_t; +}; + +//is_span_constructible is a utility metafunction to check if matioCpp::Span is constructible given a reference to Class. +template +struct is_span_constructible : std::false_type +{}; + +template +struct is_span_constructible::type>, Class&>::value>::type> + : std::true_type +{}; + } // implementation details @@ -680,7 +728,7 @@ MATIOCPP_CONSTEXPR Span make_span(ElementType (&arr)[N]) noexcep return Span(arr); } -template +template ::value>::type> MATIOCPP_CONSTEXPR Span make_span(Container& cont) { return Span(cont); @@ -712,6 +760,27 @@ MATIOCPP_CONSTEXPR Span().data())>::type>(cont); } +template ::value && + !SpanUtils::is_element_defined::value && + SpanUtils::has_data_method::value>::type> +MATIOCPP_CONSTEXPR Span().data())>::type> make_span(const Container& cont) +{ + return Span().data())>::type>(cont); +} + +namespace SpanUtils { +//is_make_span_callable is a utility metafunction to check if matioCpp::make_span can be called given the input Class +template +struct is_make_span_callable : std::false_type +{}; + +template +struct is_make_span_callable()))>> : std::true_type +{ +}; + +} + } // namespace matioCpp #ifdef _MSC_VER diff --git a/include/matioCpp/impl/EigenConversions.tpp b/include/matioCpp/impl/EigenConversions.tpp new file mode 100644 index 00000000..ddc9876d --- /dev/null +++ b/include/matioCpp/impl/EigenConversions.tpp @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2022 Fondazione Istituto Italiano di Tecnologia + * + * This software may be modified and distributed under the terms of the + * BSD-2-Clause license (https://opensource.org/licenses/BSD-2-Clause). + */ + +#ifndef EIGENCONVERSIONS_TPP +#define EIGENCONVERSIONS_TPP + +#include + +template +inline Eigen::Map> matioCpp::to_eigen(matioCpp::MultiDimensionalArray& input) +{ + assert(input.isValid()); + assert(input.dimensions().size() == 2); + return Eigen::Map>(input.data(), input.dimensions()(0), input.dimensions()(1)); +} + +template +inline const Eigen::Map> matioCpp::to_eigen(const matioCpp::MultiDimensionalArray& input) +{ + assert(input.isValid()); + assert(input.dimensions().size() == 2); + return Eigen::Map>(input.data(), input.dimensions()(0), input.dimensions()(1)); +} + +template +inline Eigen::Map> matioCpp::to_eigen(matioCpp::Vector& input) +{ + assert(input.isValid()); + return Eigen::Map>(input.data(), input.size()); +} + +template +inline const Eigen::Map> matioCpp::to_eigen(const matioCpp::Vector& input) +{ + assert(input.isValid()); + return Eigen::Map>(input.data(), input.size()); +} + +template +inline matioCpp::MultiDimensionalArray matioCpp::make_variable(const std::string& name, const Eigen::MatrixBase& input) +{ + matioCpp::MultiDimensionalArray matio(name, {static_cast(input.rows()), static_cast(input.cols())}); + matioCpp::to_eigen(matio) = input; + return matio; +} + +#endif // EIGENCONVERSIONS_TPP diff --git a/include/matioCpp/impl/Element.tpp b/include/matioCpp/impl/Element.tpp index d3c835ce..a47eb786 100644 --- a/include/matioCpp/impl/Element.tpp +++ b/include/matioCpp/impl/Element.tpp @@ -1,6 +1,3 @@ -#ifndef MATIOCPP_ELEMENT_TPP -#define MATIOCPP_ELEMENT_TPP - /* * Copyright (C) 2020 Fondazione Istituto Italiano di Tecnologia * @@ -8,6 +5,9 @@ * BSD-2-Clause license (https://opensource.org/licenses/BSD-2-Clause). */ +#ifndef MATIOCPP_ELEMENT_TPP +#define MATIOCPP_ELEMENT_TPP + template bool matioCpp::Element::checkCompatibility(const matvar_t* inputPtr, matioCpp::VariableType variableType, matioCpp::ValueType valueType) const { @@ -136,6 +136,18 @@ matioCpp::Element &matioCpp::Element::operator=(typename matioCpp::Element return *this; } +template +matioCpp::Span::element_type> matioCpp::Element::toSpan() +{ + return matioCpp::make_span(static_cast::pointer>(toMatio()->data), 1); +} + +template +const matioCpp::Span::element_type> matioCpp::Element::toSpan() const +{ + return matioCpp::make_span(static_cast::const_pointer>(toMatio()->data), 1); +} + template bool matioCpp::Element::setName(const std::string &newName) { diff --git a/include/matioCpp/impl/ExogenousConversionHelpers.tpp b/include/matioCpp/impl/ExogenousConversionHelpers.tpp new file mode 100644 index 00000000..73a7922b --- /dev/null +++ b/include/matioCpp/impl/ExogenousConversionHelpers.tpp @@ -0,0 +1,94 @@ +/* + * Copyright (C) 2022 Fondazione Istituto Italiano di Tecnologia + * + * This software may be modified and distributed under the terms of the + * BSD-2-Clause license (https://opensource.org/licenses/BSD-2-Clause). + */ +#ifndef MATIOCPP_EXOGENOUSCONVERSIONHELPERS_TPP +#define MATIOCPP_EXOGENOUSCONVERSIONHELPERS_TPP + +namespace matioCpp +{ +/** + * is_eigen_matrix is a template metafunction to check if T is an Eigen matrix. + */ +template +struct is_eigen_matrix : std::false_type +{ +}; + +#ifdef MATIOCPP_HAS_EIGEN + +/** + * is_eigen_matrix is a template metafunction to check if T is an Eigen matrix. In this specialization, we first check if + * the template parameter inherits from Eigen::MatrixBase (Eigen exploits CRTP). If this is the case, + * we check that neither the rows, nor the columns at compile time are identically equal to 1. If that is the case, + * is_eigen_matrix::value is true. + */ +template +struct is_eigen_matrix, Derived>::value>, + typename std::enable_if_t::RowsAtCompileTime != 1 && + Eigen::MatrixBase::ColsAtCompileTime != 1>>: std::true_type +{ +}; + +#endif + +/** + * is_vector_compatible is a utility metafunction to check if the input vector T is compatible with matioCpp + */ +template +struct is_vector_compatible : std::false_type +{ +}; + +/** + * is_vector_compatible is a utility metafunction to check if the input vector T is compatible with matioCpp + * This specialization first checks if T is an array, or if it is possible to deduce the type of vector, + * and that it is not an Eigen matrix (Eigen defines the size() method also for matrices). + * If not, this specialization is not used (SFINAE). Otherwise, given the vector type, it checks if a matioCpp::Span is construbile. + * In addition, the type has not to be bool<\code>. + * If all the above checks are true, is_vector_compatible::value = true<\code>. + */ +template + struct is_vector_compatible::value || matioCpp::SpanUtils::has_type_member::value || matioCpp::SpanUtils::has_data_method::value) && !is_eigen_matrix::value>::type, + typename std::enable_if::value && + !std::is_same::type, bool>::value>::type> : std::true_type +{ +}; + +/** + * Template metafunction to check if the input type is a pair + */ +template +struct is_pair : std::false_type +{ }; + +/** + * Template metafunction to check if the input type is a pair + */ +template +struct is_pair().first)>, matioCpp::SpanUtils::void_t().second)>> : std::true_type +{ }; + +/** + * Template metafunction to check if the input type is a pair iterator with a string as first element. + */ +template +struct is_pair_iterator_string : std::false_type +{ }; + +/** + * Template metafunction to check if the input type is a pair iterator with a string as first element. + */ +template +struct is_pair_iterator_string())>::value>, + typename std::enable_if_t()->first), std::string>::value>> : std::true_type +{ }; + +} + +#endif // MATIOCPP_EXOGENOUSCONVERSIONHELPERS_TPP diff --git a/include/matioCpp/impl/ExogenousConversions.tpp b/include/matioCpp/impl/ExogenousConversions.tpp new file mode 100644 index 00000000..2ef72b9f --- /dev/null +++ b/include/matioCpp/impl/ExogenousConversions.tpp @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2022 Fondazione Istituto Italiano di Tecnologia + * + * This software may be modified and distributed under the terms of the + * BSD-2-Clause license (https://opensource.org/licenses/BSD-2-Clause). + */ +#ifndef MATIOCPP_EXOGENOUSCONVERSIONS_TPP +#define MATIOCPP_EXOGENOUSCONVERSIONS_TPP + +template +inline matioCpp::Vector::type>> matioCpp::make_variable(const std::string& name, const Vector& input) +{ + using type = typename matioCpp::SpanUtils::container_data::type; + return matioCpp::Vector>(name, matioCpp::make_span(input)); //data is copied +} + +template +inline matioCpp::Element matioCpp::make_variable(const std::string& name, const type& input) +{ + return matioCpp::Element(name, input); +} + +template +inline matioCpp::Struct matioCpp::make_variable(const std::string& name, const Struct& input) +{ + matioCpp::Struct matioStruct(name); + + visit_struct::for_each(input, + [& matioStruct](const char * name, const auto & value) { + static_assert (is_make_variable_callable::value, "The input struct contains non-compatible fields."); + bool ok = matioStruct.setField(make_variable(name, value)); + matioCpp::unused(ok); + assert(ok); + }); + return matioStruct; +} + +template +inline matioCpp::Struct matioCpp::make_struct(const std::string& name, iterator begin, iterator end) +{ + matioCpp::Struct matioStruct(name); + for (iterator it = begin; it != end; it++) + { + bool ok = matioStruct.setField(make_variable(it->first, it->second)); + matioCpp::unused(ok); + assert(ok); + } + + return matioStruct; +} + +template::value>*> +inline matioCpp::CellArray matioCpp::make_cell_array(const std::string& name, const iterator& begin, const iterator& end) +{ + matioCpp::CellArray matioCellArray(name, {static_cast(std::distance(begin, end)), 1}); + + size_t index = 0; + iterator it = begin; + while (it != end) + { + bool ok = matioCellArray.setElement(index, make_variable(it->first, it->second)); + matioCpp::unused(ok); + assert(ok); + index++; + it++; + } + + return matioCellArray; +} + +template())>::value>*> +inline matioCpp::CellArray matioCpp::make_cell_array(const std::string& name, const iterator& begin, const iterator& end) +{ + matioCpp::CellArray matioCellArray(name, {static_cast(std::distance(begin, end)), 1}); + + size_t index = 0; + iterator it = begin; + while (it != end) + { + bool ok = matioCellArray.setElement(index, make_variable("imported_element_" + std::to_string(index), *it)); + matioCpp::unused(ok); + assert(ok); + index++; + it++; + } + + return matioCellArray; +} + +#endif // MATIOCPP_EXOGENOUSCONVERSIONS_TPP diff --git a/include/matioCpp/impl/File.tpp b/include/matioCpp/impl/File.tpp index 7e7bbac3..0002a607 100644 --- a/include/matioCpp/impl/File.tpp +++ b/include/matioCpp/impl/File.tpp @@ -1,16 +1,13 @@ -#ifndef MATIOCPP_FILE_TPP -#define MATIOCPP_FILE_TPP - /* - * Copyright (C) 2020 Fondazione Istituto Italiano di Tecnologia + * Copyright (C) 2022 Fondazione Istituto Italiano di Tecnologia * - * Licensed under either the GNU Lesser General Public License v3.0 : - * https://www.gnu.org/licenses/lgpl-3.0.html - * or the GNU Lesser General Public License v2.1 : - * https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html - * at your option. + * This software may be modified and distributed under the terms of the + * BSD-2-Clause license (https://opensource.org/licenses/BSD-2-Clause). */ +#ifndef MATIOCPP_FILE_TPP +#define MATIOCPP_FILE_TPP + template const input &matioCpp::File::getVariable(const input& it) { diff --git a/include/matioCpp/impl/MultiDimensionalArray.tpp b/include/matioCpp/impl/MultiDimensionalArray.tpp index db7efde7..5367ccd5 100644 --- a/include/matioCpp/impl/MultiDimensionalArray.tpp +++ b/include/matioCpp/impl/MultiDimensionalArray.tpp @@ -1,6 +1,3 @@ -#ifndef MATIOCPP_MULTIDIMENSIONALARRAY_TPP -#define MATIOCPP_MULTIDIMENSIONALARRAY_TPP - /* * Copyright (C) 2020 Fondazione Istituto Italiano di Tecnologia * @@ -8,6 +5,9 @@ * BSD-2-Clause license (https://opensource.org/licenses/BSD-2-Clause). */ +#ifndef MATIOCPP_MULTIDIMENSIONALARRAY_TPP +#define MATIOCPP_MULTIDIMENSIONALARRAY_TPP + template bool matioCpp::MultiDimensionalArray::checkCompatibility(const matvar_t* inputPtr, matioCpp::VariableType variableType, matioCpp::ValueType valueType) const { diff --git a/include/matioCpp/impl/StructArrayElement.tpp b/include/matioCpp/impl/StructArrayElement.tpp index 115c7e50..5456e5bb 100644 --- a/include/matioCpp/impl/StructArrayElement.tpp +++ b/include/matioCpp/impl/StructArrayElement.tpp @@ -1,6 +1,3 @@ -#ifndef MATIOCPP_STRUCTARRAYELEMENT_TPP -#define MATIOCPP_STRUCTARRAYELEMENT_TPP - /* * Copyright (C) 2020 Fondazione Istituto Italiano di Tecnologia * @@ -8,6 +5,9 @@ * BSD-2-Clause license (https://opensource.org/licenses/BSD-2-Clause). */ +#ifndef MATIOCPP_STRUCTARRAYELEMENT_TPP +#define MATIOCPP_STRUCTARRAYELEMENT_TPP + template matioCpp::StructArrayElement::StructArrayElement(index_type index, input_vector_type *array) : m_innerIndex(index) diff --git a/include/matioCpp/impl/Vector.tpp b/include/matioCpp/impl/Vector.tpp index 47b7eb27..6c30de55 100644 --- a/include/matioCpp/impl/Vector.tpp +++ b/include/matioCpp/impl/Vector.tpp @@ -1,6 +1,3 @@ -#ifndef MATIOCPP_VECTOR_TPP -#define MATIOCPP_VECTOR_TPP - /* * Copyright (C) 2020 Fondazione Istituto Italiano di Tecnologia * @@ -8,6 +5,9 @@ * BSD-2-Clause license (https://opensource.org/licenses/BSD-2-Clause). */ +#ifndef MATIOCPP_VECTOR_TPP +#define MATIOCPP_VECTOR_TPP + template bool matioCpp::Vector::initializeVector(const std::string& name, Span::element_type> inputVector) { diff --git a/src/ExogenousConversions.cpp b/src/ExogenousConversions.cpp new file mode 100644 index 00000000..9fc0cc45 --- /dev/null +++ b/src/ExogenousConversions.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2022 Fondazione Istituto Italiano di Tecnologia + * + * This software may be modified and distributed under the terms of the + * BSD-2-Clause license (https://opensource.org/licenses/BSD-2-Clause). + */ + +#include + + +matioCpp::Element matioCpp::make_variable(const std::string& name, bool input) +{ + return matioCpp::Element(name, input); +} + +matioCpp::String matioCpp::make_variable(const std::string& name, const std::string& input) +{ + return matioCpp::String(name, input); +} + +matioCpp::Vector matioCpp::make_variable(const std::string& name, const std::vector& input) +{ + return matioCpp::Vector(name, input); +} + +matioCpp::CellArray matioCpp::make_variable(const std::string& name, const std::vector& input) +{ + matioCpp::CellArray stringsArray(name, {input.size(), 1}); + for (size_t i = 0; i < input.size(); ++i) + { + stringsArray.setElement(i, make_variable(input[i], input[i])); + } + + return stringsArray; +} diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 9aee7e46..335dc3cb 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -49,3 +49,7 @@ add_unit_test(NAME StructArrayElement SOURCES StructArrayElementUnitTest.cpp LINKS matioCpp::matioCpp) +add_unit_test(NAME ExogenousConversions + SOURCES ExogenousConversionsUnitTest.cpp + LINKS matioCpp::matioCpp) + diff --git a/test/ElementUnitTest.cpp b/test/ElementUnitTest.cpp index c56e8f4e..74069937 100644 --- a/test/ElementUnitTest.cpp +++ b/test/ElementUnitTest.cpp @@ -92,6 +92,10 @@ TEST_CASE("Constructors") REQUIRE(i64.isValid()); REQUIRE(i64.valueType() == matioCpp::ValueType::INT64); + matioCpp::Element ui64; + REQUIRE(ui64.isValid()); + REQUIRE(ui64.valueType() == matioCpp::ValueType::UINT64); + matioCpp::Element c; REQUIRE(c.isValid()); REQUIRE(c.valueType() == matioCpp::ValueType::UTF8); @@ -198,6 +202,19 @@ TEST_CASE("Assignements") } } +TEST_CASE("Span") +{ + matioCpp::Element i("hello_element", 7); + + auto span = i.toSpan(); + REQUIRE(span.size() == 1); + REQUIRE(span[0] == 7); + + const matioCpp::Span constSpan = i.toSpan(); + REQUIRE(constSpan.size() == 1); + REQUIRE(constSpan[0] == 7); +} + TEST_CASE("Set name") { matioCpp::Element i("hello_element", 7); diff --git a/test/ExogenousConversionsUnitTest.cpp b/test/ExogenousConversionsUnitTest.cpp new file mode 100644 index 00000000..0636d005 --- /dev/null +++ b/test/ExogenousConversionsUnitTest.cpp @@ -0,0 +1,251 @@ +/* + * Copyright (C) 2020 Fondazione Istituto Italiano di Tecnologia + * + * This software may be modified and distributed under the terms of the + * BSD-2-Clause license (https://opensource.org/licenses/BSD-2-Clause). + */ + +//#define MATIOCPP_NO_EIGEN +#include +#include +#include +#include +#include + +template +void checkSameVectors(const Vector1& a, const Vector2& b) +{ + REQUIRE(static_cast(a.size()) == static_cast(b.size())); + + for (size_t i = 0; i < static_cast(a.size()); ++i) + { + REQUIRE(a[i] == b[i]); + } +} + +template +void checkSameMatrix(const Matrix& a, const matioCpp::MultiDimensionalArray& b) +{ + REQUIRE(b.isValid()); + REQUIRE(b.dimensions().size() == 2); + REQUIRE(static_cast(a.rows()) == b.dimensions()(0)); + REQUIRE(static_cast(a.cols()) == b.dimensions()(1)); + + for (size_t i = 0; i < b.dimensions()(0); ++i) + { + for (size_t j = 0; j < b.dimensions()(1); ++j) + { + REQUIRE(a(i,j) == b({i,j})); + } + } +} + +struct testStruct +{ + int i{1}; + double d{2.0}; + std::string s{"test"}; + std::vector stdVec = {1.0, 2.0, 3.0, 4.0, 5.0}; + int* notSupported = nullptr; + std::vector stringVector = {"Huey", "Dewey", "Louie"}; + std::vector vecOfBool = {true, false, true}; +}; +VISITABLE_STRUCT(testStruct, i, d, s, stdVec, vecOfBool, stringVector); + + +struct nestedStruct +{ + using array_3f = std::array; + BEGIN_VISITABLES(nestedStruct); + VISITABLE_DIRECT_INIT(array_3f, array, {1.0, 2.0, 3.0}); + VISITABLE(testStruct, s); + END_VISITABLES; + int* notSupported = nullptr; +}; + +#ifdef MATIOCPP_HAS_EIGEN + +TEST_CASE("Eigen Conversions") +{ + + SECTION("To Eigen") + { + std::vector stdVec = {1.0, 2.0, 3.0, 4.0, 5.0}; + + matioCpp::Vector vector("vector", 5); + vector = stdVec; + + Eigen::VectorXd eigenVec = matioCpp::to_eigen(vector); + checkSameVectors(eigenVec, vector); + + Eigen::Matrix3f eigenMatrix; + eigenMatrix << 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0; + + matioCpp::MultiDimensionalArray matioCppMatrix("matrix", {3,3}, eigenMatrix.data()); + + Eigen::MatrixXf toEigenMatrix = matioCpp::to_eigen(matioCppMatrix); + checkSameMatrix(toEigenMatrix, matioCppMatrix); + } + + SECTION("From Eigen") + { + Eigen::Matrix3f eigenMatrix; + eigenMatrix << 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0; + auto toMatioMatrix = matioCpp::make_variable("testMatrix", eigenMatrix); + checkSameMatrix(eigenMatrix, toMatioMatrix); + + Eigen::Vector3i eigenVec; + eigenVec << 2, 4, 6; + auto toMatioEigenVec = matioCpp::make_variable("testEigen", eigenVec); + checkSameVectors(eigenVec, toMatioEigenVec); + } + +} +#endif + +TEST_CASE("Exogenous conversions") +{ + SECTION("make_variable_callable") + { + REQUIRE(matioCpp::is_make_variable_callable>::value); + REQUIRE(matioCpp::is_make_variable_callable>::value); + REQUIRE(matioCpp::is_make_variable_callable::value); + REQUIRE(matioCpp::is_make_variable_callable>::value); + REQUIRE(matioCpp::is_make_variable_callable::value); + REQUIRE(matioCpp::is_make_variable_callable::value); + REQUIRE(matioCpp::is_make_variable_callable::value); + REQUIRE(matioCpp::is_make_variable_callable::value); + REQUIRE(matioCpp::is_make_variable_callable>::value); + REQUIRE(matioCpp::is_make_variable_callable::value); + } + + SECTION("make_variable_callable") + { + REQUIRE(std::is_same>::type, matioCpp::Vector>::value); + REQUIRE(std::is_same>::type, matioCpp::Vector>::value); + REQUIRE(std::is_same::type, matioCpp::String>::value); + REQUIRE(std::is_same>::type, matioCpp::Vector>::value); + REQUIRE(std::is_same::type, matioCpp::Element>::value); + REQUIRE(std::is_same::type, matioCpp::Element>::value); + REQUIRE(std::is_same::type, matioCpp::Element>::value); + REQUIRE(std::is_same::type, matioCpp::Element>::value); + REQUIRE(std::is_same>::type, matioCpp::CellArray>::value); + REQUIRE(std::is_same::type, matioCpp::Struct>::value); + } + + SECTION("Vector") + { + std::vector stdVec = {1.0, 2.0, 3.0, 4.0, 5.0}; + auto toMatioVec = matioCpp::make_variable("test", stdVec); + checkSameVectors(stdVec, toMatioVec); + + std::array array = {1.0, 2.0, 3.0}; + auto toMatioArray = matioCpp::make_variable("test", array); + checkSameVectors(array, toMatioArray); + + int classicalArray[] = {1, 2, 3}; + auto toMatioClassic = matioCpp::make_variable("test", matioCpp::make_span(classicalArray, 3)); + checkSameVectors(matioCpp::make_span(classicalArray, 3), toMatioClassic); + + std::string string("something"); + auto toMatioString = matioCpp::make_variable("name", string); + REQUIRE(string == toMatioString()); + + std::vector vecOfBool = {true, false, true}; + auto toVecofBool = matioCpp::make_variable("vecOfBool", vecOfBool); + checkSameVectors(vecOfBool, toVecofBool); + + auto matioDouble = matioCpp::make_variable("double", 5.0); + REQUIRE(matioDouble == 5.0); + + auto matioBool = matioCpp::make_variable("bool", true); + REQUIRE(matioBool); + + auto matioInt = matioCpp::make_variable("int", 2); + REQUIRE(matioInt == 2); + + auto matioChar = matioCpp::make_variable("char", 'f'); + REQUIRE(matioChar == 'f'); + + std::vector stringVector = {"Huey", "Dewey", "Louie"}; + auto matioCell = matioCpp::make_variable("stringVector", stringVector); + for (size_t i = 0; i < stringVector.size(); ++i) + { + REQUIRE(matioCell[i].asString()() == stringVector[i]); + } + } + + SECTION("Struct") + { + std::unordered_map unordered_map; + unordered_map["one"] = 1; + unordered_map["two"] = 2; + unordered_map["three"] = 3; + + matioCpp::Struct toMatio = matioCpp::make_struct("map", unordered_map.begin(), unordered_map.end()); + + REQUIRE(toMatio["one"].asElement() == 1); + REQUIRE(toMatio["two"].asElement() == 2); + REQUIRE(toMatio["three"].asElement() == 3); + + testStruct s; + matioCpp::Struct automaticStruct = matioCpp::make_variable("testStruct", s); + REQUIRE(automaticStruct["i"].asElement() == s.i); + REQUIRE(automaticStruct["d"].asElement() == s.d); + REQUIRE(automaticStruct["s"].asString()() == s.s); + checkSameVectors(automaticStruct["stdVec"].asVector(), s.stdVec); + checkSameVectors(automaticStruct["vecOfBool"].asVector(), s.vecOfBool); + + for (size_t i = 0; i < s.stringVector.size(); ++i) + { + REQUIRE(automaticStruct["stringVector"].asCellArray()[i].asString()() == s.stringVector[i]); + } + + auto fields = automaticStruct.fields(); + bool ok = std::find(fields.begin(), fields.end(), "notSupported") == fields.end(); + REQUIRE(ok); + + nestedStruct s2; + matioCpp::Struct automaticNestedStruct = matioCpp::make_variable("testStruct2", s2); + checkSameVectors(s2.array, automaticNestedStruct["array"].asVector()); + REQUIRE(automaticNestedStruct["s"].asStruct()["i"].asElement() == s2.s.i); + REQUIRE(automaticNestedStruct["s"].asStruct()["d"].asElement() == s2.s.d); + REQUIRE(automaticNestedStruct["s"].asStruct()["s"].asString()() == s2.s.s); + checkSameVectors(automaticNestedStruct["s"].asStruct()["stdVec"].asVector(), s2.s.stdVec); + checkSameVectors(automaticNestedStruct["s"].asStruct()["vecOfBool"].asVector(), s2.s.vecOfBool); + + for (size_t i = 0; i < s2.s.stringVector.size(); ++i) + { + REQUIRE(automaticNestedStruct["s"].asStruct()["stringVector"].asCellArray()[i].asString()() == s2.s.stringVector[i]); + } + + auto fields2 = automaticNestedStruct.fields(); + ok = std::find(fields2.begin(), fields2.end(), "notSupported") == fields2.end(); + REQUIRE(ok); + } + + SECTION("Cell Array") + { + std::map map; + map["a"] = 'a'; + map["b"] = 'b'; + map["c"] = 'c'; + + matioCpp::CellArray toMatioCell = matioCpp::make_cell_array("mapAsCell", map.begin(), map.end()); + REQUIRE(toMatioCell[0].asElement() == 'a'); + REQUIRE(toMatioCell[0].name() == "a"); + REQUIRE(toMatioCell[1].asElement() == 'b'); + REQUIRE(toMatioCell[1].name() == "b"); + REQUIRE(toMatioCell[2].asElement() == 'c'); + REQUIRE(toMatioCell[2].name() == "c"); + + std::set set({"Huey", "Dewey", "Louie"}); + auto toMatioCellFromSet = matioCpp::make_cell_array("setAsCell", set.begin(), set.end()); + REQUIRE(toMatioCellFromSet[0].asString()() == "Dewey"); + REQUIRE(toMatioCellFromSet[1].asString()() == "Huey"); + REQUIRE(toMatioCellFromSet[2].asString()() == "Louie"); + } +} + + + diff --git a/test/SpanUnitTest.cpp b/test/SpanUnitTest.cpp index 138a8163..d59b2402 100644 --- a/test/SpanUnitTest.cpp +++ b/test/SpanUnitTest.cpp @@ -376,11 +376,20 @@ TEST_CASE("from_std_array_const_constructor") } } +size_t foo(Span input) +{ + return input.size(); +} + TEST_CASE("from_container_constructor") { std::vector v = {1, 2, 3}; const std::vector cv = v; + size_t sizeAfterDeducton = foo(v); + + REQUIRE(sizeAfterDeducton == v.size()); + { Span s{v}; REQUIRE((s.size() == static_cast(v.size()) && s.data() == v.data())); @@ -428,6 +437,14 @@ TEST_CASE("from_container_constructor") auto cs = make_span(cv); REQUIRE((cs.size() == static_cast(cv.size()) && cs.data() == cv.data())); } + + { + auto lambdaSize = [](Span input){return input.size();}; + + size_t sizeAfterDeducton = lambdaSize(cv); + + REQUIRE(sizeAfterDeducton == v.size()); + } } TEST_CASE("from_convertible_span_constructor")