Modern C++ API - safe_enum

7 posts / 0 new
Last post
Offline
Last seen: 3 years 11 months ago
Joined: 08/25/2015
Posts: 32
Modern C++ API - safe_enum

Hello,


when using the modern C++ API, enums from IDL are represented with dds::core::safe_enum<T>

but the usage of this class is somehow cumbersome.

- The access to the real enum can only be achieved with dds::core::safe_enum<T>::underlying(), e.g in a switch or when we want to store the values in the database

dds::core::safe_enum<Foo> myState;

...

switch (myState.underlying()) {... }

- When you use templates, underlying() is also needed

std::min(myState, Foo::AnEnum) (bad example, only to show the problem)

- rtiddsgen also has some problems when an enum name has the same name as a struct

enum FooType { Foo = 0, Bar = 1, FooBar = 2 };

struct FooBar { long id; }; // does not work - duplicate symbol 'FooBar

Is there a reason to not use the new c++11 enum instead?

Offline
Last seen: 2 months 1 week ago
Joined: 04/02/2013
Posts: 194

Hi Christian,

I am one of the modern C++ API developers.

The API is designed to work both with C++03 and C++11, for that reason we needed to define our own enum type. We discussed to conditionally use safe_enum or C++11's enum class depending on the compiler, like we do for other C++11 features. However we realized that C++03 client code would not directly compile when switching to C++11, because safe_enum and enum classare not identical.

We can probably improve the user experience by allowing the conversion from safe_enum to integer automatically so you don't have to call underlying. I will file a request for improvement.

The rtiddsgen problem is a bug. I will file it as well. Thanks for reporting it.

I hope this clarifies a bit your doubts and thank you for your feedback.

 

sumant's picture
Offline
Last seen: 6 years 9 months ago
Joined: 02/02/2011
Posts: 7

The safe_enum idiom is designed to avoid the obnoxius behavior of C++98 enumerations. Please see More C++ Idioms/Type-Safe Enum for detailed treatment on the subject. The most important goal of the idiom is to avoid automatic conversion to integral types. A number of subtle enumeration related bugs can be caught at compile-time using the idiom. Java and C++ enum class features are safer to use for the same reasons. 

Direct access to undelrying is rarely needed and should be avoided as much as possible. switch can be generally replaced using if-else-if chain. If the undelerlying value is needed for some reason, C++03/C++11 portable alternatives exists. For example, the following free underlying function can be defined. static_cast<int>(underlying(myState)) shall work portably.  

#ifndef CPP11
template <class Inner>
typename dds::core::safe_enum<Inner>::inner_enum underlying(dds::core::safe_enum<Inner> k)
{
return k.underlying();
}
#endif
#ifdef CPP11

template <class SafeEnum>
SafeEnum underlying(SafeEnum k)
{
return k;
}
#endif

I agree that the std::min example as shown does not compile and it's a pity. Portable alternatives exist for that as well. For example, consider the following program. All the uses of std::min are portable from C++03 to C++11.

ShapeFillKind k1 = ShapeFillKind::SOLID_FILL;
ShapeFillKind k2 = ShapeFillKind::TRANSPARENT_FILL;

std::cout << "fillkind = " << k1 << "value = " << static_cast<int>(underlying(k1)) << "\n";
std::min(k1, k2);
std::min<ShapeFillKind>(k1, ShapeFillKind::TRANSPARENT_FILL);
std::min(k1, ShapeFillKind(ShapeFillKind::TRANSPARENT_FILL));

Finally, section 7.12 in the specification requires the use of C++11 enum classes for C++11 compatibility. It appears not be the case in the current version of rtiddsgen. Perhaps the reason is that a static get_default() function is defined in the generated struct type that encapsulates the native enum. C++11 enum class does not have static functions. I think a portable solution to that would be to include a namespace-level free safe_enum_get_default function and have it return the default value. The function can be called from anywhere in the program using the argument-dependent lookup as follows

// in the namespace of ShapeFillKind
ShapeFillKind safe_enum_get_default(ShapeFillKind)
{  
  r
eturn ShapeFillKind(ShapeFillKind::SOLID_FILL);
}

// Wherever default value is needed
safe_enum_get_default(ShapeFillKind());

Here's live code for this example.

Offline
Last seen: 3 years 11 months ago
Joined: 08/25/2015
Posts: 32

Thx for your explanations and suggestions :)

Offline
Last seen: 2 months 1 week ago
Joined: 04/02/2013
Posts: 194

Christian,

We have further discussed the rtiddsgen error and according to the IDL specification it is in fact illegal, even though the resulting C++ code would compile.

Alex

Offline
Last seen: 3 years 11 months ago
Joined: 08/25/2015
Posts: 32

Thx for the information - then we have to rename our enum values to not match the structures. :)

Offline
Last seen: 4 years 4 months ago
Joined: 08/13/2014
Posts: 55

Hi

I want to know whether it is possible to convert  safe_enum to integer automatically without having to call underlying (except to if-else-if chain). I want to use the enums in switch and if-else-if is not a good alternative in some cases.

Best Regards,

Bonjefir