Variants provide a mechanism to store several types of data in one construct and send them via Ice. The Variant class offers a unified interface for storing and accessing values of several data types. It is one of the essential data types of ArmarX and can be used in Ice communication. Variants can be extended and several Variant-based implementations of simple or complex data types like 6D poses are available in ArmarX.

Complex data types in the variant context are all that are not int, float, bool, double or string.

Basic Variant Usage

Variants can store instances of all classes that implement the VariantDataClass Ice interface. For the basic data types int, bool, float, double and std::string there are three main ways for creating a Variant.

In the following code three int-Variant instances are created using the different ways of instantiation:

Variant varInt;
Variant varInt(2);
Variant varInt;

This works analogously for the other basic data types. The setting of complex datatypes work similarly:

Variant varComplex(new TestComplexFloat(1, 2));
The TestComplexFloat is from the HowTo How to Create Custom Variant Types.

The stored values can be retrieved using the respective getter methods:

int value = varInt.getInt();

Or for complex types:

BOOST_CHECK(varComplex.getType() == VariantType::TestComplexFloat);
BOOST_CHECK(varComplex.get<TestComplexFloat>()->getReal() == 1);
BOOST_CHECK(varComplex.get<TestComplexFloat>()->getImag() == 2);

Note that after assigning a value to a variant and therefore after initially determining the Variant's type, changing the type is not possible anymore. Any assignment of data of a different than the set type will result in a LocalException.

There exist several implementations of Variant-types apart from the elementary ones:

List of all Variants: Variant Group

There are further classes that realize flexible containers of Variant-values, which are Variants themselves:

These containers are needed for the same reason as the Variant themselves: We need to be able to send different data via the same Ice interface. Since Ice interfaces always have a specific type, we need these classes with a common base class (VariantDataClass and VariantContainerBase).

Take a look at How to Create Custom Variant Types if you want to implement your own specialized Variant classes.

Serialization of Variants as JSON

It is easy to serialize a Variant into a JSON format. Every Variant type has this feature built in. You only need the armarx::JSONObject for the serialization and deserialization:

// Needed for deserialization - the communicator can be retrieved
// in any ManagedIceObject with getCommunicator()
// The next line is already done in a standard ArmarX app.
JSONObjectPtr jsonSerialize(new JSONObject(iceCommunicator));
// create Variant - every variant data type can be passed to the constructor
VariantPtr var = new Variant(3.0f);
jsonSerialize->setVariant("value", var); // key and value
std::string prettyJSONstring = jsonSerialize->asString(true); // true -> pretty JSON
// Now create the variant back from the string
JSONObjectPtr jsonDeserialize(new JSONObject(iceCommunicator));
jsonDeserialize->fromString(prettyJSONstring); // parse the JSON string
ARMARX_INFO << prettyJSONstring;
VariantPtr newVariant = jsonDeserialize->getVariant("value");
ARMARX_INFO << "float value: " << newVariant->getFloat();

The JSON then looks as follows:

   "value" : {
      "typeName" : "::armarx::FloatVariantData",
      "value" : 3.0
IceInternal::Handle< Variant > VariantPtr
Definition: Variant.h:42
std::shared_ptr< Value > value()
Definition: cxxopts.hpp:926
IceInternal::Handle< JSONObject > JSONObjectPtr
Definition: JSONObject.h:34
Definition: Logging.h:174
static void RegisterKnownObjectFactoriesWithIce(const Ice::CommunicatorPtr &ic)
Registers all object factories that are known with Ice.
Definition: ArmarXManager.cpp:1204