mc_rtc::Configuration
general purpose configurationmc_rtc::Configuration
general purpose configurationThis page provides information about the following topics:
mc_rtc::Configuration
objectmc_rtc::Configuration
objectmc_rtc::Configuration
objectIn every example on this page, we assume that config
is a C++
object of type mc_rtc::Configuration
in C++ examples and a
Python object of type mc_rtc.Configuration
in Python
examples.
mc_rtc::Configuration
provides an abstraction over a JSONor a YAML object to easily manipulate the object and retrieve C++/Python data from it.
To load data into the configuration object you can use the following:
// Load from a file, assume it's JSON data
auto config = mc_rtc::Configuration("myfile.conf");
// Load from a YAML file, the extension must be yml or yaml
auto config = mc_rtc::Configuration("myfile.yaml");
// Load from already loaded JSON data
auto config = mc_rtc::Configuration::fromData(data);
// Load from already loaded YAML data
auto config = mc_rtc::Configuration::fromYAMLData(data);
Several methods are proposed to access the data in the configuration, you can mix them in your program depending on the most appropriate. The three methods are:
The first method we present will throw if the configuration entries you are accessing do not exist in the configuration file that was loaded or if the stored type does not fit the value you are trying to retrieve.
Accessing an entry is then done in this way:
Eigen::Vector3d v = config("MyVector3d");
You can also access a sub-section of the configuration:
Eigen::VectorXd v = config("section")("MyVectorXd");
And those can be nested:
std::vector<std::string> v = config("section")("sub1")("sub2")("SomeStrings");
This is simply done as so:
if(conf.has("MyQuaternion"))
{
Eigen::Quaterniond q = config("MyQuaternion");
}
The above code only throws if the MyQuaternion
entry is part
of the configuration but does not contain the required data to
obtain an Eigen::Quaterniond
object (see the previous
section).
This method can be used safely to retrieve configuration entries. It will not throw if the configuration entry does not exist or does not match the required type.
Accessing an entry is done in this way:
bool b = false;
config("MyBool", b);
You can also simplify the code above like so:
bool b = config("MyBool", false);
If the entry does not exist or does not match the required type, the provided variable is simply not modified. Otherwise, the value stored in the configuration is copied into the provided variable. This is particularly important to understand when retrieving a vector:
std::vector<double> v = {1., 2., 3.};
config("MyVector", v); // if MyVector is a valid entry, the initial content of v is lost
Similarly to the previous method, you can access sub-sections of the configuration:
double d = 1.0;
config("section")("sub1")("sub2")("MyDouble", d);
However, the previous code will throw if any of the section/sub-section does not exist in the configuration file.
The strict/default access methods presented above can be applied to access a sub-section of an mc_rtc::Configuration
object, e.g.
// throws if conf is not an object or section does not exist in conf
auto sub = config("section");
// returns an empty object if the call above would fail
auto sub = config("section", mc_rtc::Configuration{});
Note that copies are shallow so any changes you make to a sub-section can be seen from the parent.
In Python, we cannot deduce the type of the requested element so one has to provide a Python type or value to retrieve data:
# c is an mc_rtc.Configuration object
c = config("key")
# retrieve a bool
b = config("b", bool)
# v is an eigen.Vector3d object
v = config("v", eigen.Vector3d)
# retrieve a bool with a default value of False
b = config("b", False)
# retrieve a list of eigen.Vector3d objects
vlist = config("vlist", [eigen.Vector3d])
# retrieve a list of float with a default value
flist = config("flist", [0., 1., 2.])
mc_rtc::Configuration
objectHere are some writing examples that demonstrate the difference in object or array APIs.
// -- Objects --
// Create a new empty object with the section key
auto section = conf.add("section");
// Add data to the section
section.add("v1", Eigen::Vector3d{1,2,3});
section.add("isFixed", true);
// Here data is of a type supported by mc_rtc::Configuration
section.add("data", data);
// -- Arrays --
// Creates a new empty array
auto array = conf.array("array");
// Creates a new empty array and reserves memory for 10 elements
auto array10 = conf.array("array10", 10);
// Add data to the array
array.push(Eigen::Vector3d{1,2,3});
array.push(true);
array.push(data);
The Python API is identical.
As you saw in the previous examples, the Configuration
object can be used to
retrieve various types of object. The following table shows the supported types
and JSON requirements.
Type | JSON requirement |
---|---|
bool |
Either a boolean (true /false ) or an integer (0 is false, anything else is true) |
int |
An integer |
unsigned int |
An unsigned integer or an integer that is ≥ 0 |
double |
A numeric entry |
std::string |
A string entry |
Eigen::Vector3d |
An array of size 3 composed of numeric elements |
Eigen::Quaterniond |
An array of size 4 composed of numeric elements, the returned quaternion is normalized. |
Eigen::Vector6d |
An array of size 6 composed of numeric elements |
Eigen::VectorXd |
An array of any size composed of numeric elements |
Eigen::Matrix3d |
An array of size 9 composed of numeric elements. Two alternatives representations are supported to support rotation It can also be an array of size 3 representing RPY angles or an array of size 4 representing a quaternion |
std::vector<T> where T is any of the above types |
An array for which each element satisfies the requirements of type T |
std::array<T, N> where T is any of the above types and `N` a fixed size |
An array of size N for which each elements satisfies the requirements of type T |
std::pair<U, V> where U and V are any of the above types |
An array of size 2 whose elements satisfy the requirements of types U and `V` respectively |
std::map<std::string, T> where T is any of the above types |
A map indexed by string whose elements satisfy the requirements of types T |
We support a lot more types, including tasks and constraints, for each object that can be loaded by mc_rtc you can find a documentation on this website.
The generic types supported by the configuration can be composed in any way you can imagine. For example, the following example is perfectly valid:
std::vector<std::pair<std::vector<Eigen::Vector3d>, std::vector<double>> inequalities = config("inequalities");
This feature allows you to specialize mc_rtc::Configuration
to support loading/save to/from your own type. This feature is
a C++ only feature for now.
You can add a specialization to the ConfigurationLoader
template in the mc_rtc
namespace of the following form:
template<>
struct ConfigurationLoader<MyType>
{
static MyType load(const mc_rtc::Configuration & config);
static mc_rtc::Configuration save(const MyType & object);
};
This feature is used extensively in mc_rtc
to provide
support for many SpaceVecAlg
, RBDyn
and mc_rbdyn
types.
All declarations can be found in
mc_rbdyn/configuration_io.h
.
The saving function supports optional arguments so, the following is a valid specialization:
template<>
struct ConfigurationLoader<MyType>
{
static MyType load(const mc_rtc::Configuration & config);
static mc_rtc::Configuration save(const MyType & object, bool verbose = false);
};
You can also implement methods in your object, mc_rtc
will then pick these methods automatically:
struct Foo
{
// ...
// This will be called to load your object from a Configuration object
static Foo fromConfiguration(const mc_rtc::Configuration & in);
// This will be called save your object to a Configuration object
mc_rtc::Configuration toConfiguration() const;
// This also supports arguments
mc_rtc::Configuration toConfiguration(bool verbose) const;
};