From 7486fcf53049fdd5917d7c19c4bc4cb247ebba75 Mon Sep 17 00:00:00 2001 From: Arnaud TANGUY Date: Wed, 29 Nov 2023 16:09:51 +0100 Subject: [PATCH] [Schema] Add support for std::map --- doc/_i18n/en/tutorials/usage/schema.md | 40 +++++++++++++++++++++++--- include/mc_rtc/Schema.h | 33 ++++++++++++++++++++- tests/samples_Schema.h | 2 ++ tests/testSchema.cpp | 7 ++++- 4 files changed, 76 insertions(+), 6 deletions(-) diff --git a/doc/_i18n/en/tutorials/usage/schema.md b/doc/_i18n/en/tutorials/usage/schema.md index 5dd4d475b0..1b64924744 100644 --- a/doc/_i18n/en/tutorials/usage/schema.md +++ b/doc/_i18n/en/tutorials/usage/schema.md @@ -30,13 +30,45 @@ struct SimpleSchema MEMBER(bool, useFeature, "Use magic feature") MEMBER(double, weight, "Task weight") MEMBER(std::vector, names, "Some names") + using MapType = std::map + MEMBER(MapType, jointValues, "Map of joint names and values") MEMBER(sva::ForceVecd, wrench, "Target wrench") MEMBER(sva::PTransformd, pt, "Some transform") #undef MEMBER }; ``` -
Note for MSVC users
+
Note on the MEMBER macro
+ +If you intend to use the `MEMBER` macro, beware of the following pre-processor limitations. + +
Templated types
+ +Due to limitations of the pre-processor, you cannot directly pass types depending on multiple template parameters to the macro. For example, the following will give a compilation error. + +```cpp +MEMBER(std::map, jointValues, "Map of joint names and values") +``` + +This occurs because the pre-processor splits the arguments as follows: + +- `std::map` +- `jointValues` +- `"Map of joint names and values"` + +To avoid this you can explicitly alias the type + +```cpp +using MapType = std::map +MEMBER(MapType, jointValues, "Map of joint names and values") +``` + +If this is not an option, you may use `BOOST_IDENTIY_TYPE` instead. + + + +
Note for MSVC users
If you intend the above code to be used with the Microsoft Visual C++ Compiler (MSVC), the `MEMBER` definition should be changed to: @@ -44,8 +76,6 @@ If you intend the above code to be used with the Microsoft Visual C++ Compiler ( #define MEMBER(...) MC_RTC_PP_ID(MC_RTC_SCHEMA_REQUIRED_DEFAULT_MEMBER(SimpleSchema, __VA_ARGS__)) ``` -Or avoid the `MEMBER` helper all-together. - This works around a pre-processor issue in MSVC. ### Usage @@ -117,7 +147,7 @@ The intent is for any `TYPE` to be usable -- at least for the load/save scenario If a given `TYPE` is not supported you will get a compilation error, please report the issue to mc_rtc developpers. -As of now, most types that can be loaded/saved through an `mc_rtc::Configuration` object are supported as well as schema-based types and `std::vector` of such types. +As of now, most types that can be loaded/saved through an `mc_rtc::Configuration` object are supported as well as schema-based types and `std::vector` and `std::map` of such types. The following is an example using such types: @@ -196,6 +226,8 @@ If you are using a `_DEFAULT_` macro, this is `mc_rtc::Default` which is: - Identity for `sva::PTransformd` - Zero for `sva::ForceVecd`, `sva::MotionVecd`, `sva::ImpedanceVecd`, `sva::AdmittanceVecd` - Empty string for `std::string` +- Empty vector for `std::vector` +- Empty map for `std::map` - `mc_rtc::Default` for `std::variant` - Default values for a schema-based structure diff --git a/include/mc_rtc/Schema.h b/include/mc_rtc/Schema.h index f8ed34f39e..21a8b3f22b 100644 --- a/include/mc_rtc/Schema.h +++ b/include/mc_rtc/Schema.h @@ -67,6 +67,28 @@ inline constexpr bool is_std_vector_schema_v = []() else { return false; } }(); +/** Type-trait to detect an std::map */ +template +struct is_std_map : std::false_type +{ +}; + +template +struct is_std_map> : std::true_type +{ +}; + +template +inline constexpr bool is_std_map_v = is_std_map::value; + +/** Type-trait to detect an std::map where ValueT is a Schema-based type */ +template +inline constexpr bool is_std_map_schema_v = []() +{ + if constexpr(is_std_map_v) { return is_schema_v; } + else { return false; } +}(); + template void addValueToForm(const T & value, const std::string & description, @@ -119,6 +141,10 @@ void addValueToForm(const T & value, addValueToForm(default_, description, {}, input); form.addElement(input); } + else if constexpr(details::is_std_map_v) + { + // We currently do not support map in forms + } else if constexpr(gui::details::is_variant_v) { auto input = mc_rtc::gui::FormOneOfInput(description, IsRequired, get_value); @@ -297,7 +323,7 @@ struct Operations auto out_ = out.add(name); T::formToStd(in(description), out_); } - else if constexpr(details::is_std_vector_schema_v) + else if constexpr(details::is_std_vector_schema_v or details::is_std_map_schema_v) { using SchemaT = typename T::value_type; std::vector in_ = in(description); @@ -383,6 +409,11 @@ struct Default>> inline static const T value = {}; }; +template +struct Default>> +{ + inline static const T value = {}; +}; } // namespace mc_rtc #include diff --git a/tests/samples_Schema.h b/tests/samples_Schema.h index 2e9d258e74..87a2e8d2af 100644 --- a/tests/samples_Schema.h +++ b/tests/samples_Schema.h @@ -11,6 +11,8 @@ struct SimpleSchema MEMBER(bool, useFeature, "Use magic feature") MEMBER(double, weight, "Task weight") MEMBER(std::vector, names, "Some names") + using MapType = std::map; + MEMBER(MapType, jointValues, "Some joint values") MEMBER(sva::ForceVecd, wrench, "Target wrench") MEMBER(sva::PTransformd, pt, "Some transform") #undef MEMBER diff --git a/tests/testSchema.cpp b/tests/testSchema.cpp index bcfb8e4591..2a2704b4f8 100644 --- a/tests/testSchema.cpp +++ b/tests/testSchema.cpp @@ -31,6 +31,10 @@ BOOST_AUTO_TEST_CASE(TestDefault) static_assert(Default>::value == 0.0); using test_variant_t = std::variant; BOOST_REQUIRE(Default::value == Eigen::Vector3d::Zero()); + BOOST_REQUIRE(Default>::value == std::vector{}); + auto map_value = Default>::value; + auto expected_map_value = std::map{}; + BOOST_REQUIRE(map_value == expected_map_value); } BOOST_AUTO_TEST_CASE(TestSimpleSchema) @@ -42,6 +46,7 @@ BOOST_AUTO_TEST_CASE(TestSimpleSchema) default_.useFeature = true; default_.weight = 42.42; default_.names = {"a", "b", "c"}; + default_.jointValues = {{"a", 0.0}, {"b", 1.0}, {"c", 2.0}}; default_.wrench = random_fv(); default_.pt = random_pt(); mc_rtc::Configuration cfg; @@ -50,7 +55,7 @@ BOOST_AUTO_TEST_CASE(TestSimpleSchema) BOOST_REQUIRE(default_ == other_); } { - SimpleSchema new_{true, 42.42, default_.names, default_.wrench, default_.pt}; + SimpleSchema new_{true, 42.42, default_.names, default_.jointValues, default_.wrench, default_.pt}; BOOST_REQUIRE(new_ == default_); } {