Skip to content

Commit

Permalink
[Schema] Add support for std::map
Browse files Browse the repository at this point in the history
  • Loading branch information
arntanguy committed Nov 29, 2023
1 parent 6bf72a7 commit 7486fcf
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 6 deletions.
40 changes: 36 additions & 4 deletions doc/_i18n/en/tutorials/usage/schema.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,22 +30,52 @@ struct SimpleSchema
MEMBER(bool, useFeature, "Use magic feature")
MEMBER(double, weight, "Task weight")
MEMBER(std::vector<std::string>, names, "Some names")
using MapType = std::map<std::string, double>
MEMBER(MapType, jointValues, "Map of joint names and values")
MEMBER(sva::ForceVecd, wrench, "Target wrench")
MEMBER(sva::PTransformd, pt, "Some transform")
#undef MEMBER
};
```
<h5 class="no_toc">Note for MSVC users</h5>
<h5 class="no_toc">Note on the MEMBER macro</h5>
If you intend to use the `MEMBER` macro, beware of the following pre-processor limitations.
<h6 class="no_toc">Templated types</h6>
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<std::string, double>, jointValues, "Map of joint names and values")
```

This occurs because the pre-processor splits the arguments as follows:

- `std::map<std::string`
- `double>`
- `jointValues`
- `"Map of joint names and values"`

To avoid this you can explicitly alias the type

```cpp
using MapType = std::map<std::string, double>
MEMBER(MapType, jointValues, "Map of joint names and values")
```
If this is not an option, you may use `BOOST_IDENTIY_TYPE` instead.
<h6 class="no_toc">Note for MSVC users</h6>
If you intend the above code to be used with the Microsoft Visual C++ Compiler (MSVC), the `MEMBER` definition should be changed to:
```cpp
#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
Expand Down Expand Up @@ -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:

Expand Down Expand Up @@ -196,6 +226,8 @@ If you are using a `_DEFAULT_` macro, this is `mc_rtc::Default<T>` 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<T>`
- Empty map for `std::map<K, V>`
- `mc_rtc::Default<T>` for `std::variant<T, Others...>`
- Default values for a schema-based structure

Expand Down
33 changes: 32 additions & 1 deletion include/mc_rtc/Schema.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,28 @@ inline constexpr bool is_std_vector_schema_v = []()
else { return false; }
}();

/** Type-trait to detect an std::map */
template<typename T>
struct is_std_map : std::false_type
{
};

template<typename... Args>
struct is_std_map<std::map<Args...>> : std::true_type
{
};

template<typename T>
inline constexpr bool is_std_map_v = is_std_map<T>::value;

/** Type-trait to detect an std::map<Key, ValueT> where ValueT is a Schema-based type */
template<typename T>
inline constexpr bool is_std_map_schema_v = []()
{
if constexpr(is_std_map_v<T>) { return is_schema_v<typename T::value_type>; }
else { return false; }
}();

template<typename T, bool IsRequired, bool IsInteractive, bool HasChoices = false, bool IsStatic = false>
void addValueToForm(const T & value,
const std::string & description,
Expand Down Expand Up @@ -119,6 +141,10 @@ void addValueToForm(const T & value,
addValueToForm<value_type, true, IsInteractive>(default_, description, {}, input);
form.addElement(input);
}
else if constexpr(details::is_std_map_v<T>)
{
// We currently do not support map in forms
}
else if constexpr(gui::details::is_variant_v<T>)
{
auto input = mc_rtc::gui::FormOneOfInput(description, IsRequired, get_value);
Expand Down Expand Up @@ -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<T>)
else if constexpr(details::is_std_vector_schema_v<T> or details::is_std_map_schema_v<T>)
{
using SchemaT = typename T::value_type;
std::vector<Configuration> in_ = in(description);
Expand Down Expand Up @@ -383,6 +409,11 @@ struct Default<T, std::enable_if_t<schema::details::is_std_vector_v<T>>>
inline static const T value = {};
};

template<typename T>
struct Default<T, typename std::enable_if_t<schema::details::is_std_map_v<T>>>
{
inline static const T value = {};
};
} // namespace mc_rtc

#include <mc_rtc/SchemaMacros.h>
2 changes: 2 additions & 0 deletions tests/samples_Schema.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ struct SimpleSchema
MEMBER(bool, useFeature, "Use magic feature")
MEMBER(double, weight, "Task weight")
MEMBER(std::vector<std::string>, names, "Some names")
using MapType = std::map<std::string, double>;
MEMBER(MapType, jointValues, "Some joint values")
MEMBER(sva::ForceVecd, wrench, "Target wrench")
MEMBER(sva::PTransformd, pt, "Some transform")
#undef MEMBER
Expand Down
7 changes: 6 additions & 1 deletion tests/testSchema.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@ BOOST_AUTO_TEST_CASE(TestDefault)
static_assert(Default<std::variant<double, Eigen::Vector3d>>::value == 0.0);
using test_variant_t = std::variant<Eigen::Vector3d, double>;
BOOST_REQUIRE(Default<test_variant_t>::value == Eigen::Vector3d::Zero());
BOOST_REQUIRE(Default<std::vector<double>>::value == std::vector<double>{});
auto map_value = Default<std::map<std::string, double>>::value;
auto expected_map_value = std::map<std::string, double>{};
BOOST_REQUIRE(map_value == expected_map_value);
}

BOOST_AUTO_TEST_CASE(TestSimpleSchema)
Expand All @@ -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;
Expand All @@ -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_);
}
{
Expand Down

0 comments on commit 7486fcf

Please sign in to comment.