Enumerations¶
Enumerations are widely used by Tudat to define the type of settings objects. In this section, it will be explained how to implement support for the enumeration EphemerisType
. The same process can be followed to add support for other enumerations in the future.
...
namespace tudat
{
namespace simulation_setup
{
...
enum EphemerisType
{
approximate_planet_positions,
direct_spice_ephemeris,
tabulated_ephemeris,
interpolated_spice,
constant_ephemeris,
kepler_ephemeris,
custom_ephemeris
};
...
Built-in implementation¶
The JSON for modern C++ library has built-in support for enumerations. For instance:
nlohmann::json j = 1;
EphemerisType ephemerisType = j; // direct_spice_ephemeris
EphemerisType ephemerisType = interpolated_spice;
nlohmann::json j = ephemerisType;
std::cout << ephemerisType << std::endl; // 3
However, the built-in implementation uses the value of the enumeration rather than its name. Clearly, it is not convenient for the user having to specify the type of the ephemeris based on how they are listed in the C++ code. Additionally, if the order of the values in the enumeration list changes in the future, converting 1
to EphemerisType
may not result in direct_spice_ephemeris
anymore. Thus, a custom implementation is needed, in which the JSON representation of an enumeration is the name of its possible values. For instance:
nlohmann::json j = "direct_spice_ephemeris";
EphemerisType ephemerisType = j;
Without the custom implementation, this leads to a run-time error.
Name-based implementation¶
Custom implementations for the to_json
and from_json
functions of all the supported enumerations are provided in the JSON Interface library. In this way, it is possible to convert nlohmann::json
objects of value type string to enum
and vice versa. However, the string representation for each enum value has to be manually provided. Although it is possible to replace the enumeration value names by identical strings during compile time, this was deemed too complex and, additionally, the enumeration names used in Tudat are not always optimal. For instance, consider this JSON file:
{
"Earth": {
"ephemeris": {
"type": "tabulated_ephemeris"
}
}
}
In this case, the _ephemeris
part is redundant. In Tudat this is necessary when other enumerations declared in the same namespace can share the same names (e.g. tabulated_atmosphere
). However, in a JSON file, the string tabulated_ephemeris
will only be used inside ephemeris
objects of body objects, so the string tabulated
is unambiguous. Thus, when defining the string representation for existing Tudat enumerations, the redundant information is removed. Additionally, the naming convention is to use lowerCamelCase
strings.
The definition of the string representation of the enum values is done in a file in the JSON Interface directory but in the enumeration’s namespace (not in the json_interface
namespace). This decision was made taking into account that these variables are only used inside the to_json
and from_json
functions of the enumeration, which must be declared in the enumeration’s namespace. A map is used to define the string representation of each enumeration type:
#include <Tudat/SimulationSetup/EnvironmentSetup/createEphemeris.h>
#include "Tudat/JsonInterface/Support/valueAccess.h"
#include "Tudat/JsonInterface/Support/valueConversions.h"
...
namespace tudat
{
namespace simulation_setup
{
...
//! Map of `EphemerisType`s string representations.
static std::map< EphemerisType, std::string > ephemerisTypes =
{
{ approximate_planet_positions, "approximatePlanetPositions" },
{ direct_spice_ephemeris, "directSpice" },
{ tabulated_ephemeris, "tabulated" },
{ interpolated_spice, "interpolatedSpice" },
{ constant_ephemeris, "constant" },
{ kepler_ephemeris, "kepler" },
{ custom_ephemeris, "custom" }
};
//! `EphemerisType` not supported by `json_interface`.
static std::vector< EphemerisType > unsupportedEphemerisTypes =
{
custom_ephemeris
};
...
As you can see, the string representations are provided for all the enumeration values, even those that are not supported by the JSON interface. For instance, custom_ephemeris
is not supported by the JSON interface, because a boost::function
cannot be provided using JSON files. Thus, this enum value is marked as unsupported by adding it to the variable unsupportedEphemerisTypes
. In this way, when the user provides the value "custom"
, for the key ephemeris, rather than getting an IllevalValueError
, an EphemerisType
with value custom_ephemeris
will be created without printing any warning. Then, when the actual EphemerisSettings
are created, in its from_json
function, the user will get an error in which it is said that custom ephemeris is not supported by the JSON interface but it does exist in Tudat, so if they want to use it they have to build their own custom JSON-based C++ application, in which the ephemeris function is defined manually (after reading the JSON input file containing the remainder of the settings).
Although a to_json
and from_json
function has to be written for each enumeration, they each are just a single line in which the functions stringFromEnum
and enumFromString
defined in Tudat/JsonInterface/Support/errorHandling.h
are called:
#include <Tudat/SimulationSetup/EnvironmentSetup/createEphemeris.h>
#include "Tudat/JsonInterface/Support/valueAccess.h"
#include "Tudat/JsonInterface/Support/valueConversions.h"
...
namespace tudat
{
namespace simulation_setup
{
...
//! Convert `EphemerisType` to `nlohmann::json`.
inline void to_json( nlohmann::json& jsonObject, const EphemerisType& ephemerisType )
{
jsonObject = json_interface::stringFromEnum( ephemerisType, ephemerisTypes );
}
//! Convert `nlohmann::json` to `EphemerisType`.
inline void from_json( const nlohmann::json& jsonObject, EphemerisType& ephemerisType )
{
ephemerisType = json_interface::enumFromString( jsonObject, ephemerisTypes );
}
...
If the string representation of ephemerisType
is not known, the recognised strings will be printed and an IllegalValueError
will be thrown:
Unknown string "constatn" for enum tudat::simulation_setup::EphemerisType
Recognized strings:
approximatePlanetPositions
directSpice
tabulated
interpolatedSpice
constant
kepler
custom
libc++abi.dylib: terminating with uncaught exception of type tudat::json_interface::IllegalValueError:
Illegal value for key: bodies.Earth.ephemeris.type
Could not convert value to expected type tudat::simulation_setup::EphemerisType
Note
All the files in which the to_json
and from_json
functions of enumerations and settings file are defined must include the files Tudat/JsonInterface/Support/valueAccess.h
and Tudat/JsonInterface/Support/valueConversions.h
. The former includes Tudat/JsonInterface/Support/errorHandling.h
, exposing the functions json_interface::stringFromEnum
and json_interface::enumFromString
.
Caution
When converting an enumeration to or from a nlohmann::json
object, the file in which its custom to_json
and from_json
functions are defined must be included (if the conversion takes place in a different file), or the functions have to be declared before being used if defined in the same file. If one forgets to include this file, the code will compile without giving any errors or warnings and the default implementation will be used, leading to a run-time error in which it is said that an int
was expected when converting to the enumeration type.