From a0db95823a983825f0f8d1e1203778a7cbc2393a Mon Sep 17 00:00:00 2001
From: Rainer Kartmann <rainer.kartmann@kit.edu>
Date: Tue, 27 Apr 2021 09:23:20 +0200
Subject: [PATCH] Add stl / template conversions and tests

---
 .../libraries/aron/common/CMakeLists.txt      |   6 +-
 .../libraries/aron/common/aron_conversions.h  |   3 +
 .../aron/common/aron_conversions/core.cpp     |   2 +
 .../aron/common/aron_conversions/core.h       |  81 ++++++
 .../aron/common/aron_conversions/stl.cpp      |   1 +
 .../aron/common/aron_conversions/stl.h        | 187 +++++++++++++
 .../libraries/aron/common/test/CMakeLists.txt |  14 +
 .../aron/common/test/MyCustomType.cpp         |  67 +++++
 .../libraries/aron/common/test/MyCustomType.h | 117 ++++++++
 .../aron/common/test/aron_common_test.cpp     | 261 ++++++++++++++++++
 10 files changed, 738 insertions(+), 1 deletion(-)
 create mode 100644 source/RobotAPI/libraries/aron/common/aron_conversions/core.cpp
 create mode 100644 source/RobotAPI/libraries/aron/common/aron_conversions/core.h
 create mode 100644 source/RobotAPI/libraries/aron/common/aron_conversions/stl.cpp
 create mode 100644 source/RobotAPI/libraries/aron/common/aron_conversions/stl.h
 create mode 100644 source/RobotAPI/libraries/aron/common/test/CMakeLists.txt
 create mode 100644 source/RobotAPI/libraries/aron/common/test/MyCustomType.cpp
 create mode 100644 source/RobotAPI/libraries/aron/common/test/MyCustomType.h
 create mode 100644 source/RobotAPI/libraries/aron/common/test/aron_common_test.cpp

diff --git a/source/RobotAPI/libraries/aron/common/CMakeLists.txt b/source/RobotAPI/libraries/aron/common/CMakeLists.txt
index 292095e5d..7a4f383eb 100644
--- a/source/RobotAPI/libraries/aron/common/CMakeLists.txt
+++ b/source/RobotAPI/libraries/aron/common/CMakeLists.txt
@@ -13,12 +13,16 @@ armarx_add_library(
 
     HEADERS
         aron_conversions.h
+        aron_conversions/core.h
         aron_conversions/armarx.h
         aron_conversions/simox.h
+        aron_conversions/stl.h
 
     SOURCES
+        aron_conversions/core.cpp
         aron_conversions/armarx.cpp
         aron_conversions/simox.cpp
+        aron_conversions/stl.cpp
 )
 
 
@@ -33,4 +37,4 @@ armarx_enable_aron_file_generation_for_target(
 
 
 # add unit tests
-# add_subdirectory(test)
+add_subdirectory(test)
diff --git a/source/RobotAPI/libraries/aron/common/aron_conversions.h b/source/RobotAPI/libraries/aron/common/aron_conversions.h
index 17f784d13..2f96d0285 100644
--- a/source/RobotAPI/libraries/aron/common/aron_conversions.h
+++ b/source/RobotAPI/libraries/aron/common/aron_conversions.h
@@ -1,4 +1,7 @@
 #pragma once
 
+#include "aron_conversions/core.h"
+
 #include "aron_conversions/armarx.h"
 #include "aron_conversions/simox.h"
+#include "aron_conversions/stl.h"
diff --git a/source/RobotAPI/libraries/aron/common/aron_conversions/core.cpp b/source/RobotAPI/libraries/aron/common/aron_conversions/core.cpp
new file mode 100644
index 000000000..790714d9c
--- /dev/null
+++ b/source/RobotAPI/libraries/aron/common/aron_conversions/core.cpp
@@ -0,0 +1,2 @@
+#include "core.h"
+
diff --git a/source/RobotAPI/libraries/aron/common/aron_conversions/core.h b/source/RobotAPI/libraries/aron/common/aron_conversions/core.h
new file mode 100644
index 000000000..bcc537a28
--- /dev/null
+++ b/source/RobotAPI/libraries/aron/common/aron_conversions/core.h
@@ -0,0 +1,81 @@
+#pragma once
+
+
+
+namespace armarx::aron
+{
+
+    /**
+     * Framework for converting ARON DTOs (Data Transfer Objects) to C++ BOs
+     * (Business Objects) and back.
+     *
+     * To allow conversion between custom ARON and C++ types, declare two
+     * functions in the namespace of the BO:
+     *
+     * @code
+     * // aron_conversions.h
+     *
+     * namespace bo_namespace
+     * {
+     *     void toAron(arondto::MyObject& dto, const MyObject& bo);
+     *     void fromAron(const arondto::MyObject& dto, MyObject& bo);
+     * }
+     * @endcode
+     *
+     * Note that the DTO always comes first, and the target object is
+     * non-const.
+     *
+     * In the implementation,
+     *
+     *
+     * @code
+     * // aron_conversions.cpp
+     *
+     * #include "aron_conversions.h"
+     * #include <Path/to/MyValue/aron_conversions.h>
+     *
+     * void bo_namespace::toAron(arondto::MyObject& dto, const MyObject& bo)
+     * {
+     *     dto.name = bo.name;
+     *     toAron(dto.myValue, bo.myValue);
+     * }
+     *
+     * void bo_namespace::fromAron(const arondto::MyObject& dto, MyObject& bo)
+     * {
+     *     bo.name = dto.name;
+     *     fromAron(dto.myValue, bo.myValue);
+     * }
+     * @endcode
+     */
+
+    // Same type
+    template <class T>
+    void toAron(T& dto, const T& bo)
+    {
+        dto = bo;
+    }
+    template <class T>
+    void fromAron(const T& dto, T& bo)
+    {
+        bo = dto;
+    }
+
+
+    // Generic return version
+
+    template <class DtoT, class BoT>
+    DtoT toAron(const BoT& bo)
+    {
+        DtoT dto;
+        toAron(dto, bo);
+        return dto;
+    }
+    template <class BoT, class DtoT>
+    BoT fromAron(const DtoT& dto)
+    {
+        BoT bo;
+        fromAron(dto, bo);
+        return bo;
+    }
+
+}
diff --git a/source/RobotAPI/libraries/aron/common/aron_conversions/stl.cpp b/source/RobotAPI/libraries/aron/common/aron_conversions/stl.cpp
new file mode 100644
index 000000000..857d161ec
--- /dev/null
+++ b/source/RobotAPI/libraries/aron/common/aron_conversions/stl.cpp
@@ -0,0 +1 @@
+#include "stl.h"
diff --git a/source/RobotAPI/libraries/aron/common/aron_conversions/stl.h b/source/RobotAPI/libraries/aron/common/aron_conversions/stl.h
new file mode 100644
index 000000000..12c353e15
--- /dev/null
+++ b/source/RobotAPI/libraries/aron/common/aron_conversions/stl.h
@@ -0,0 +1,187 @@
+#pragma once
+
+#include <map>
+#include <memory>
+#include <optional>
+#include <vector>
+
+#include "core.h"
+
+
+namespace armarx::aron
+{
+
+    // std::unique_ptr
+
+    template <class DtoT, class BoT>
+    void toAron(DtoT& dto, const std::unique_ptr<BoT>& bo)
+    {
+        if (bo)
+        {
+            toAron(dto, *bo);
+        }
+    }
+    template <class DtoT, class BoT>
+    void fromAron(const DtoT& dto, std::unique_ptr<BoT>& bo)
+    {
+        bo = std::make_unique<BoT>();
+        fromAron(dto, *bo);
+    }
+
+
+    // std::optional
+
+    template <class DtoT, class BoT>
+    void toAron(std::optional<DtoT>& dto, const std::optional<BoT>& bo)
+    {
+        if (bo.has_value())
+        {
+            dto = DtoT{};
+            toAron(*dto, *bo);
+        }
+        else
+        {
+            dto = std::nullopt;
+        }
+    }
+    template <class DtoT, class BoT>
+    void fromAron(const std::optional<DtoT>& dto, std::optional<BoT>& bo)
+    {
+        if (dto.has_value())
+        {
+            bo = BoT{};
+            fromAron(*dto, *bo);
+        }
+        else
+        {
+            bo = std::nullopt;
+        }
+    }
+
+    // Flag-controlled optional
+    template <class DtoT, class BoT>
+    void toAron(DtoT& dto, bool& dtoValid, const BoT& bo, bool boValid)
+    {
+        dtoValid = boValid;
+        if (boValid)
+        {
+            toAron(dto, bo);
+        }
+        else
+        {
+            dto = {};
+        }
+    }
+    template <class DtoT, class BoT>
+    void fromAron(const DtoT& dto, bool dtoValid, BoT& bo, bool& boValid)
+    {
+        boValid = dtoValid;
+        if (dtoValid)
+        {
+            fromAron(dto, bo);
+        }
+        else
+        {
+            bo = {};
+        }
+    }
+
+    template <class DtoT, class BoT>
+    void toAron(DtoT& dto, bool& dtoValid, const std::optional<BoT>& bo)
+    {
+        dtoValid = bo.has_value();
+        if (dtoValid)
+        {
+            toAron(dto, *bo);
+        }
+        else
+        {
+            dto = {};
+        }
+    }
+    template <class DtoT, class BoT>
+    void fromAron(const DtoT& dto, bool dtoValid, std::optional<BoT>& bo)
+    {
+        if (dtoValid)
+        {
+            bo = BoT{};
+            fromAron(dto, *bo);
+        }
+        else
+        {
+            bo = std::nullopt;
+        }
+    }
+
+
+    // std::vector
+
+    template <class DtoT, class BoT>
+    void toAron(std::vector<DtoT>& dtos, const std::vector<BoT>& bos)
+    {
+        dtos.clear();
+        dtos.reserve(bos.size());
+        for (const auto& bo : bos)
+        {
+            toAron(dtos.emplace_back(), bo);
+        }
+    }
+    template <class DtoT, class BoT>
+    void fromAron(const std::vector<DtoT>& dtos, std::vector<BoT>& bos)
+    {
+        bos.clear();
+        bos.reserve(dtos.size());
+        for (const auto& dto : dtos)
+        {
+            fromAron(dto, bos.emplace_back());
+        }
+    }
+
+    template <class DtoT, class BoT>
+    std::vector<DtoT> toAron(const std::vector<BoT>& bos)
+    {
+        std::vector<DtoT> dtos;
+        toAron(dtos, bos);
+        return dtos;
+    }
+
+
+    // std::map
+
+    template <class DtoKeyT, class DtoValueT, class BoKeyT, class BoValueT>
+    void toAron(std::map<DtoKeyT, DtoValueT>& dtoMap,
+                const std::map<BoKeyT, BoValueT>& boMap)
+    {
+        dtoMap.clear();
+        for (const auto& [boKey, boValue] : boMap)
+        {
+            DtoKeyT dtoKey;
+            toAron(dtoKey, boKey);
+            auto [it, _] = dtoMap.emplace(std::move(dtoKey), DtoValueT{});
+            toAron(it->second, boValue);
+        }
+    }
+    template <class DtoKeyT, class DtoValueT, class BoKeyT, class BoValueT>
+    void fromAron(const std::map<DtoKeyT, DtoValueT>& dtoMap,
+                  std::map<BoKeyT, BoValueT>& boMap)
+    {
+        boMap.clear();
+        for (const auto& [dtoKey, dtoValue] : dtoMap)
+        {
+            BoKeyT boKey;
+            fromAron(dtoKey, boKey);
+            auto [it, _] = boMap.emplace(boKey, BoValueT{});
+            fromAron(dtoValue, it->second);
+        }
+    }
+
+
+    template <class DtoKeyT, class DtoValueT, class BoKeyT, class BoValueT>
+    std::map<DtoKeyT, DtoValueT> toAron(const std::map<BoKeyT, BoValueT>& boMap)
+    {
+        std::map<DtoKeyT, DtoValueT> dtoMap;
+        toAron(dtoMap, boMap);
+        return dtoMap;
+    }
+
+}
diff --git a/source/RobotAPI/libraries/aron/common/test/CMakeLists.txt b/source/RobotAPI/libraries/aron/common/test/CMakeLists.txt
new file mode 100644
index 000000000..856234779
--- /dev/null
+++ b/source/RobotAPI/libraries/aron/common/test/CMakeLists.txt
@@ -0,0 +1,14 @@
+
+add_library(aroncommon_test_files SHARED
+    MyCustomType.h
+    MyCustomType.cpp
+)
+target_link_libraries(aroncommon_test_files PRIVATE aroncommon)
+
+
+# Libs required for the tests
+SET(LIBS
+    aroncommon
+    aroncommon_test_files
+)
+armarx_add_test(aron_common_test aron_common_test.cpp "${LIBS}")
diff --git a/source/RobotAPI/libraries/aron/common/test/MyCustomType.cpp b/source/RobotAPI/libraries/aron/common/test/MyCustomType.cpp
new file mode 100644
index 000000000..6431732ee
--- /dev/null
+++ b/source/RobotAPI/libraries/aron/common/test/MyCustomType.cpp
@@ -0,0 +1,67 @@
+/*
+ * This file is part of ArmarX.
+ *
+ * ArmarX is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * ArmarX is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @package    RobotAPI::aron_common::aron_common
+ * @author     Rainer Kartmann ( rainer dot kartmann at kit dot edu )
+ * @date       2021
+ * @copyright  http://www.gnu.org/licenses/gpl-2.0.txt
+ *             GNU General Public License
+ */
+#include "MyCustomType.h"
+
+#include <ostream>
+
+#include <RobotAPI/libraries/aron/common/aron_conversions/core.h>
+
+
+template <class CustomTypeT>
+std::ostream& ostreamop(std::ostream& os, const CustomTypeT& rhs)
+{
+    return os << "(name='" << rhs.name << "'"
+              << " index=" << rhs.index
+              << " value=" << rhs.value
+              << ")";
+}
+
+std::ostream& my::operator<<(std::ostream& os, const CustomType& rhs)
+{
+    return ostreamop(os, rhs);
+}
+
+std::ostream& my::arondto::operator<<(std::ostream& os, const CustomType& rhs)
+{
+    return ostreamop(os, rhs);
+}
+
+bool my::operator==(const CustomType& lhs, const arondto::CustomType& rhs)
+{
+    return lhs.name == rhs.name
+            && lhs.index == rhs.index
+            && lhs.value == rhs.value;
+}
+
+
+void my::toAron(arondto::CustomType& dto, const CustomType& bo)
+{
+    dto.name = bo.name;
+    armarx::aron::toAron(dto.index, bo.index);
+    armarx::aron::toAron(dto.value, bo.value);
+}
+void my::fromAron(const arondto::CustomType& dto, CustomType& bo)
+{
+    bo.name = dto.name;
+    armarx::aron::fromAron(dto.index, bo.index);
+    armarx::aron::fromAron(dto.value, bo.value);
+}
diff --git a/source/RobotAPI/libraries/aron/common/test/MyCustomType.h b/source/RobotAPI/libraries/aron/common/test/MyCustomType.h
new file mode 100644
index 000000000..31a4507db
--- /dev/null
+++ b/source/RobotAPI/libraries/aron/common/test/MyCustomType.h
@@ -0,0 +1,117 @@
+/*
+ * This file is part of ArmarX.
+ *
+ * ArmarX is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * ArmarX is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @package    RobotAPI::aron_common::aron_common
+ * @author     Rainer Kartmann ( rainer dot kartmann at kit dot edu )
+ * @date       2021
+ * @copyright  http://www.gnu.org/licenses/gpl-2.0.txt
+ *             GNU General Public License
+ */
+
+#include <ostream>
+#include <string>
+#include <vector>
+
+
+namespace my
+{
+    struct CustomType
+    {
+        std::string name;
+        int index = 0;
+        float value = 0.0;
+    };
+
+    std::ostream& operator<<(std::ostream& os, const CustomType& rhs);
+
+
+    namespace arondto
+    {
+        struct CustomType
+        {
+            std::string name;
+            int index;
+            float value;
+        };
+
+        std::ostream& operator<<(std::ostream& os, const arondto::CustomType& rhs);
+    }
+
+    bool operator==(const CustomType& lhs, const arondto::CustomType& rhs);
+    bool operator!=(const CustomType& lhs, const arondto::CustomType& rhs)
+    {
+        return !(lhs == rhs);
+    }
+    bool operator==(const arondto::CustomType& lhs, const CustomType& rhs)
+    {
+        return rhs == lhs;
+    }
+    bool operator!=(const arondto::CustomType& lhs, const CustomType& rhs)
+    {
+        return !(rhs == lhs);
+    }
+
+    void toAron(arondto::CustomType& dto, const CustomType& bo);
+    void fromAron(const arondto::CustomType& dto, CustomType& bo);
+}
+
+
+namespace std
+{
+
+    template <class L1, class L2, class R1, class R2>
+    bool operator==(const std::pair<L1, L2>& lhs,
+                    const std::pair<R1, R2>& rhs)
+    {
+        return lhs.first == rhs.first && lhs.second == rhs.second;
+    }
+    template <class L1, class L2, class R1, class R2>
+    bool operator!=(const std::pair<L1, L2>& lhs,
+                    const std::pair<R1, R2>& rhs)
+    {
+        return !(lhs == rhs);
+    }
+
+    template <class L1, class L2>
+    std::ostream& operator<<(std::ostream& os, const std::pair<L1, L2>& pair)
+    {
+        return os << "(" << pair.first << " | " << pair.second << ")";
+    }
+
+
+    template <class L, class R>
+    bool operator==(const std::vector<L>& lhs, const std::vector<R>& rhs)
+    {
+        if (lhs.size() != rhs.size())
+        {
+            return false;
+        };
+        for (size_t i = 0; i < lhs.size(); ++i)
+        {
+            if (lhs[i] != rhs[i])
+            {
+                return false;
+            }
+        }
+        return true;
+    }
+    template <class L, class R>
+    bool operator!=(const std::vector<L>& lhs, const std::vector<R>& rhs)
+    {
+        return !(lhs == rhs);
+    }
+
+}
+
diff --git a/source/RobotAPI/libraries/aron/common/test/aron_common_test.cpp b/source/RobotAPI/libraries/aron/common/test/aron_common_test.cpp
new file mode 100644
index 000000000..0503f0132
--- /dev/null
+++ b/source/RobotAPI/libraries/aron/common/test/aron_common_test.cpp
@@ -0,0 +1,261 @@
+/*
+ * This file is part of ArmarX.
+ *
+ * ArmarX is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * ArmarX is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * @package    RobotAPI::ArmarXObjects::ArmarXObjects
+ * @author     Rainer Kartmann ( rainer dot kartmann at kit dot edu )
+ * @date       2020
+ * @copyright  http://www.gnu.org/licenses/gpl-2.0.txt
+ *             GNU General Public License
+ */
+
+#define BOOST_TEST_MODULE RobotAPI::ArmarXLibraries::aron_common
+
+#define ARMARX_BOOST_TEST
+
+#include <RobotAPI/Test.h>
+
+#include <RobotAPI/libraries/aron/common/aron_conversions/stl.h>
+#include <RobotAPI/libraries/aron/common/aron_conversions/core.h>
+
+#include <iostream>
+
+#include "MyCustomType.h"
+
+
+BOOST_AUTO_TEST_CASE(test_direct)
+{
+    const my::CustomType bo { "name", -10, 42.42f };
+
+    my::arondto::CustomType dto;
+    toAron(dto, bo);
+    BOOST_CHECK_EQUAL(dto, bo);
+
+    my::CustomType boOut;
+    fromAron(dto, boOut);
+    BOOST_CHECK_EQUAL(boOut, dto);
+}
+
+
+template <class BOs, class DTOs>
+void test_complex(const BOs bos, DTOs& dtos)
+{
+    armarx::aron::toAron(dtos, bos);
+    BOOST_CHECK_EQUAL_COLLECTIONS(dtos.begin(), dtos.end(),
+                                  bos.begin(), bos.end());
+
+    BOs bosOut;
+    armarx::aron::fromAron(dtos, bosOut);
+    BOOST_CHECK_EQUAL_COLLECTIONS(bosOut.begin(), bosOut.end(),
+                                  dtos.begin(), dtos.end());
+}
+
+
+BOOST_AUTO_TEST_CASE(test_stl_vector)
+{
+    const std::vector<my::CustomType> bos
+    {
+        { "name", -10, 42.42f },
+        { "name2", 20, -128.128f },
+    };
+    std::vector<my::arondto::CustomType> dtos;
+
+    test_complex(bos, dtos);
+}
+
+
+BOOST_AUTO_TEST_CASE(test_stl_map)
+{
+    const std::map<std::string, my::CustomType> bos
+    {
+        { "key1", { "name", -10,   42.42f  } },
+        { "key2", { "name2", 20, -128.128f } },
+    };
+    std::map<std::string, my::arondto::CustomType> dtos;
+
+    test_complex(bos, dtos);
+}
+
+
+BOOST_AUTO_TEST_CASE(test_stl_vector_of_vector)
+{
+    const std::vector<std::vector<my::CustomType>> bos
+    {
+        { { "name", -10,   42.42f  } },
+        { },
+        { { "name2", 20, -128.128f }, { "name2.1", 40, -64.64f } },
+    };
+    std::vector<std::vector<my::arondto::CustomType>> dtos;
+
+    test_complex(bos, dtos);
+}
+
+
+BOOST_AUTO_TEST_CASE(test_stl_map_of_vector)
+{
+    const std::map<std::string, std::vector<my::CustomType>> bos
+    {
+        { "key1", { { "name", -10,   42.42f  } } },
+        { "key2", { { "name2", 20, -128.128f }, { "name2.1", 40, -64.64f } } },
+        { "key3", {  } },
+    };
+    std::map<std::string, std::vector<my::arondto::CustomType>> dtos;
+
+    test_complex(bos, dtos);
+}
+
+
+BOOST_AUTO_TEST_CASE(test_optional)
+{
+    using std::nullopt;
+
+    std::optional<my::CustomType> bo;
+    std::optional<my::arondto::CustomType> dto;
+
+    auto reset = [&](
+            std::optional<my::CustomType> theBo,
+            std::optional<my::arondto::CustomType> theDto)
+    {
+        bo = theBo;
+        dto = theDto;
+    };
+
+    reset(nullopt, nullopt);
+    {
+        armarx::aron::toAron(dto, bo);
+        BOOST_CHECK(!dto.has_value());
+    }
+    reset(nullopt, nullopt);
+    {
+        armarx::aron::fromAron(dto, bo);
+        BOOST_CHECK(!bo.has_value());
+    }
+
+    reset(my::CustomType{ "bo", 30, 16.16f }, nullopt);
+    {
+        armarx::aron::toAron(dto, bo);
+        BOOST_CHECK(dto.has_value());
+        BOOST_CHECK_EQUAL(dto.value(), bo.value());
+    }
+    reset(my::CustomType{ "bo", 30, 16.16f }, nullopt);
+    {
+        armarx::aron::fromAron(dto, bo);
+        BOOST_CHECK(!bo.has_value());
+    }
+
+    reset(nullopt, my::arondto::CustomType{ "dto", 30, 16.16f });
+    {
+        armarx::aron::toAron(dto, bo);
+        BOOST_CHECK(!dto.has_value());
+    }
+    reset(nullopt, my::arondto::CustomType{ "dto", 30, 16.16f });
+    {
+        armarx::aron::fromAron(dto, bo);
+        BOOST_CHECK(bo.has_value());
+        BOOST_CHECK_EQUAL(bo.value(), dto.value());
+    }
+
+    reset(my::CustomType{ "bo", -30, -16.16f }, my::arondto::CustomType{ "dto", 30, 16.16f });
+    {
+        armarx::aron::toAron(dto, bo);
+        BOOST_CHECK(dto.has_value());
+        BOOST_CHECK_EQUAL(dto.value(), bo.value());
+        BOOST_CHECK_EQUAL(dto->name, "bo");
+    }
+    reset(my::CustomType{ "bo", -30, -16.16f }, my::arondto::CustomType{ "dto", 30, 16.16f });
+    {
+        armarx::aron::fromAron(dto, bo);
+        BOOST_CHECK(bo.has_value());
+        BOOST_CHECK_EQUAL(bo.value(), dto.value());
+        BOOST_CHECK_EQUAL(bo->name, "dto");
+    }
+}
+
+
+
+BOOST_AUTO_TEST_CASE(test_optional_value_flagged)
+{
+    using std::nullopt;
+
+    std::optional<my::CustomType> bo;
+    my::arondto::CustomType dto;
+    bool dtoValid;
+
+    auto reset = [&](
+            std::optional<my::CustomType> theBo,
+            std::optional<my::arondto::CustomType> theDto)
+    {
+        bo = theBo;
+        if (theDto)
+        {
+            dto = *theDto;
+            dtoValid = true;
+        }
+        else
+        {
+            dto = {};
+            dtoValid = false;
+        }
+    };
+
+    reset(nullopt, nullopt);
+    {
+        armarx::aron::toAron(dto, dtoValid, bo);
+        BOOST_CHECK(!dtoValid);
+    }
+    reset(nullopt, nullopt);
+    {
+        armarx::aron::fromAron(dto, dtoValid, bo);
+        BOOST_CHECK(!bo.has_value());
+    }
+
+    reset(my::CustomType{ "bo", 30, 16.16f }, nullopt);
+    {
+        armarx::aron::toAron(dto, dtoValid, bo);
+        BOOST_CHECK(dtoValid);
+        BOOST_CHECK_EQUAL(dto, bo.value());
+    }
+    reset(my::CustomType{ "bo", 30, 16.16f }, nullopt);
+    {
+        armarx::aron::fromAron(dto, dtoValid, bo);
+        BOOST_CHECK(!bo.has_value());
+    }
+
+    reset(nullopt, my::arondto::CustomType{ "dto", 30, 16.16f });
+    {
+        armarx::aron::toAron(dto, dtoValid, bo);
+        BOOST_CHECK(!dtoValid);
+    }
+    reset(nullopt, my::arondto::CustomType{ "dto", 30, 16.16f });
+    {
+        armarx::aron::fromAron(dto, dtoValid, bo);
+        BOOST_CHECK(bo.has_value());
+        BOOST_CHECK_EQUAL(bo.value(), dto);
+    }
+
+    reset(my::CustomType{ "bo", -30, -16.16f }, my::arondto::CustomType{ "dto", 30, 16.16f });
+    {
+        armarx::aron::toAron(dto, dtoValid, bo);
+        BOOST_CHECK(dtoValid);
+        BOOST_CHECK_EQUAL(dto, bo.value());
+        BOOST_CHECK_EQUAL(dto.name, "bo");
+    }
+    reset(my::CustomType{ "bo", -30, -16.16f }, my::arondto::CustomType{ "dto", 30, 16.16f });
+    {
+        armarx::aron::fromAron(dto, dtoValid, bo);
+        BOOST_CHECK(bo.has_value());
+        BOOST_CHECK_EQUAL(bo.value(), dto);
+        BOOST_CHECK_EQUAL(bo->name, "dto");
+    }
+}
-- 
GitLab