I am attempting to translate my Python program to C++, but because I am new to C++ I am encountering some problems. The input file is first parsed (works, not shown) to create the INITIAL_VALUES dict/map, which I then want to use to assign the Parameters class/struct attributes using the DEST_DICT_PARAMS dict/map.
I was able to achieve this in Python code with:
import dataclasses
INITIAL_VALUES = {
"BULK": {
"MAGMA": {
"M0": 1.0,
"T0": 1320.0,
},
"ASSIM": {
"M0": 0.0,
"T0": 600.0,
},
}
}
DEST_DICT_PARAMS = {
'M0': {"MAGMA": 'Mm0', "ASSIM": 'Ma0'},
'T0': {"MAGMA": 'Tm0', "ASSIM": 'Ta0'},
}
#dataclasses.dataclass
class Parameters:
Mm0: float = None
Ma0: float = None
Ta0: float = None
Tm0: float = None
class ParametersReader:
def __init__(self):
self.parameters = Parameters()
self._assignParameters()
def _assignParameters(self):
for param_fam, dest in DEST_DICT_PARAMS.items():
for component, param in dest.items():
value = INITIAL_VALUES["BULK"][component][param_fam]
setattr(self.parameters, param, value)
params = ParametersReader()
print(params.parameters)
Output:
Parameters(Mm0=1.0, Ma0=0.0, Ta0=600.0, Tm0=1320.0)
So I wrote the corresponding C++ code:
#include <iostream>
#include <map>
using std::map;
using std::string;
map<string, map<string, map<string, float> > > INITIAL_VALUES = {{
"BULK", {
{"MAGMA", {
{"M0", 1.0},
{"T0", 1320.0},
}},
{"ASSIM", {
{"M0", 0.0},
{"T0", 600.0},
}},
}
}};
map<string, map<string, string> > DEST_DICT_PARAMS = {{
{"M0", {{"MAGMA", "Mm0"}, {"ASSIM", "Ma0"}}},
{"T0", {{"MAGMA", "Tm0"}, {"ASSIM", "Ta0"}}},
}};
struct Parameters {
float Mm0;
float Ma0;
float Ta0;
float Tm0;
} parameters;
class ParametersReader {
public:
void assignParameters_() {
map<string, map<string, string> >::iterator itr0;
map<string, string>::iterator itr1;
for (itr0 = DEST_DICT_PARAMS.begin(); itr0 != DEST_DICT_PARAMS.end(); itr0++) {
for (itr1 = itr0->second.begin(); itr1 != itr0->second.end(); itr1++) {
parameters.itr1->second = INITIAL_VALUES["BULK"][itr1->first];
}
}
}
};
int main() {
ParametersReader params;
params.assignParameters_();
}
But I'm getting an error at the line
parameters.itr1->second = INITIAL_VALUES['BULK'][itr1->first] saying "no member named 'itr1' in 'Parameters'". That error makes total sense because the code is literally trying to interpret 'itr1' as an attribute name and not the whole 'itr1->second' as the name. I think this comes down to the fact that I can't seem to find a C++ equivalent to Python's setattr(obj, name, val) function that takes an object and its attribute name and assigns it a value. Is there a C++ solution to what I am attempting?
Perhaps my entire approach is incompatible with C++. If so, would you kindly suggest an alternative approach? I would like to keep the input file format the same between the Python and C++ versions.
C++ does not have runtime reflection like Python. You cannot look up a class member by name using a runtime string because class member names do not exist at runtime.
What you can do is look up a class member via a pointer to member. This is an offset into the object calculated at compile time by the compiler:
std::map<std::string, std::map<std::string, float Parameters::*> > DEST_DICT_PARAMS = {{
{"M0", {{"MAGMA", &Parameters::Mm0}, {"ASSIM", &Parameters::Ma0}}},
{"T0", {{"MAGMA", &Parameters::Tm0}, {"ASSIM", &Parameters::Ta0}}},
}};
class ParametersReader {
public:
void assignParameters_() {
for (auto& [param_fam, dest] : DEST_DICT_PARAMS) {
for (auto& [component, param] : dest) {
parameters.*param = INITIAL_VALUES["BULK"][component][param_fam];
}
}
}
};
Demo
Note I've also used range-based for loops and structured bindings to clean up your assignParameters_ function.
C++ has no equivalent to Pythons setattr(self.parameters, param, value). If you want to have a mapping between strings and members you need to write it yourself.
You can use pointers to members to do something along the line of:
#include <map>
#include <iostream>
struct foo {
float a = 0.0f;
float b = 0.0f;
};
// list all members and their string represenation
std::map<std::string,float foo::*> mmap {
{ "a", &foo::a },
{ "b", &foo::b }
};
int main() {
foo f;
std::map<std::string,float> values {
{"a", 0.1},
{"b", 0.2}
};
for (const auto& val : values) {
// lookup the member pointers via the strings from mmap
// use the found function pointer to assing to corresponding member in f
f.*mmap[val.first] = val.second;
}
std::cout << f.a << " " << f.b << "\n";
}
Note that the code assumes that all strings present in values are also present in mmap. If not, it will fail horribly. To fix that mmap.find should be used instead and the case of not found string handleted appropriately.
This works, though there is no way to get that mapping implicitly from the class definition only. On the other, hand I can imagine libraries to exist that can help with that.
Before you ask, I've looked and looked for this on SO, and cannot find a solid answer.
I need to be able to dynamically iterate over an enum that has non-incremental values, as an example:
typedef enum {
CAPI_SUBTYPE_NULL = 0, /* Null subtype. */
CAPI_SUBTYPE_DIAG_DFD = 1, /* Data Flow diag. */
CAPI_SUBTYPE_DIAG_ERD = 2, /* Entity-Relationship diag. */
CAPI_SUBTYPE_DIAG_STD = 3, /* State Transition diag. */
CAPI_SUBTYPE_DIAG_STC = 4, /* Structure Chart diag. */
CAPI_SUBTYPE_DIAG_DSD = 5, /* Data Structure diag. */
CAPI_SUBTYPE_SPEC_PROCESS = 6, /* Process spec. */
CAPI_SUBTYPE_SPEC_MODULE = 7, /* Module spec. */
CAPI_SUBTYPE_SPEC_TERMINATOR = 8, /* Terminator spec. */
CAPI_SUBTYPE_DD_ALL = 13, /* DD Entries (All). */
CAPI_SUBTYPE_DD_COUPLE = 14, /* DD Entries (Couples). */
CAPI_SUBTYPE_DD_DATA_AREA = 15, /* DD Entries (Data Areas). */
CAPI_SUBTYPE_DD_DATA_OBJECT = 16, /* DD Entries (Data Objects). */
CAPI_SUBTYPE_DD_FLOW = 17, /* DD Entries (Flows). */
CAPI_SUBTYPE_DD_RELATIONSHIP = 18, /* DD Entries (Relationships). */
CAPI_SUBTYPE_DD_STORE = 19, /* DD Entries (Stores). */
CAPI_SUBTYPE_DIAG_PAD = 35, /* Physical architecture diagram. */
CAPI_SUBTYPE_DIAG_BD = 36, /* Behaviour diagram. */
CAPI_SUBTYPE_DIAG_UCD = 37, /* UML Use case diagram. */
CAPI_SUBTYPE_DIAG_PD = 38, /* UML Package diagram. */
CAPI_SUBTYPE_DIAG_COD = 39, /* UML Collaboration diagram. */
CAPI_SUBTYPE_DIAG_SQD = 40, /* UML Sequence diagram. */
CAPI_SUBTYPE_DIAG_CD = 41, /* UML Class diagram. */
CAPI_SUBTYPE_DIAG_SCD = 42, /* UML State chart. */
CAPI_SUBTYPE_DIAG_ACD = 43, /* UML Activity chart. */
CAPI_SUBTYPE_DIAG_CPD = 44, /* UML Component diagram. */
CAPI_SUBTYPE_DIAG_DPD = 45, /* UML Deployment diagram. */
CAPI_SUBTYPE_DIAG_PFD = 47, /* Process flow diagram. */
CAPI_SUBTYPE_DIAG_HIER = 48, /* Hierarchy diagram. */
CAPI_SUBTYPE_DIAG_IDEF0 = 49, /* IDEF0 diagram. */
CAPI_SUBTYPE_DIAG_AID = 50, /* AID diagram. */
CAPI_SUBTYPE_DIAG_SAD = 51, /* SAD diagram. */
CAPI_SUBTYPE_DIAG_ASG = 59 /* ASG diagram. */
} CAPI_SUBTYPE_E ;
The reason I'd like to be able to do this is because the enum is given in an API (which I cannot change, obviously) and would prefer to be able to, regardless of the API version, be able to iterate over these values.
Any direction is appreciated.
With C++, the only way to iterate through enums is store them in an array and iterate through the same. The main challenge is how to track the same order in the enum declaration and the array declaration?
You can automate the way you order them in the enum as well as array. I feel that this is a decent way:
// CAPI_SUBTYPE_E_list.h
// This header file contains all the enum in the order
// Whatever order is set will be followed everywhere
NAME_VALUE(CAPI_SUBTYPE_NULL, 0), /* Null subtype. */
NAME_VALUE(CAPI_SUBTYPE_DIAG_DFD, 1), /* Data Flow diag. */
NAME_VALUE(CAPI_SUBTYPE_DIAG_ERD, 2), /* Entity-Relationship diag. */
...
NAME_VALUE(CAPI_SUBTYPE_DD_ALL, 13), /* DD Entries (All). */
NAME_VALUE(CAPI_SUBTYPE_DD_COUPLE, 14), /* DD Entries (Couples). */
...
NAME_VALUE(CAPI_SUBTYPE_DIAG_ASG, 59) /* ASG diagram. */
Now you #include this file in your enum declaration and array declaration both places with macro redefinition:
// Enum.h
typedef enum {
#define NAME_VALUE(NAME,VALUE) NAME = VALUE
#include"CAPI_SUBTYPE_E_list.h"
#undef NAME_VALUE
}CAPI_SUBTYPE_E;
And put the same file for array with other macro definition:
// array file
// Either this array can be declared `static` or inside unnamed `namespace` to make
// ... it visible through a header file; Or it should be declared `extern` and keep ...
// ... the record of its size; declare a getter method for both array and the size
unsigned int CAPI_SUBTYPE_E_Array [] = {
#define NAME_VALUE(NAME,VALUE) NAME
#include"CAPI_SUBTYPE_E_list.h"
#undef NAME_VALUE
};
Now iterate in C++03 as:
for(unsigned int i = 0, size = sizeof(CAPI_SUBTYPE_E_Array)/sizeof(CAPI_SUBTYPE_E_Array[0]);
i < size; ++i)
or yet simple in C++11:
for(auto i : CAPI_SUBTYPE_E_Array)
It is about tricky and more C than C++ practice, but you can use X macros. It is very ugly and you need to keep TABLE in right order. In C++ I believe we don't need to iterate over enumerations and more we don't need to assign values to enumeration (ostensibly enumeration value is random in every compilation). So think of it as a joke :)
#include <iostream>
#define CAPI_SUBTYPE_TABLE \
CAPI_SUBTYPE_X(CAPI_SUBTYPE_NULL, 0 ) \
CAPI_SUBTYPE_X(CAPI_SUBTYPE_DIAG_DFD, 1 ) \
CAPI_SUBTYPE_X(CAPI_SUBTYPE_DD_ALL, 13)
#define CAPI_SUBTYPE_X(name, value) name = value,
enum CAPI_SUBTYPE
{
CAPI_SUBTYPE_TABLE
CAPI_SUBTYPE_END
};
#undef CAPI_SUBTYPE_X
#define CAPI_SUBTYPE_X(name, value) name,
CAPI_SUBTYPE subtype_iteratable[] =
{
CAPI_SUBTYPE_TABLE
CAPI_SUBTYPE_END
};
#undef CAPI_SUBTYPE_X
#define CAPI_SUBTYPE_SIZE (sizeof(subtype_iteratable) / sizeof(subtype_iteratable[0]) - 1)
int main()
{
for (unsigned i = 0; i < CAPI_SUBTYPE_SIZE; ++i)
std::cout << subtype_iteratable[i] << std::endl; // 0, 1, 13
}
I agree with the already given statements that this isn't possible without either altering or copying the definitions of the enum. However, in C++11 (maybe even C++03?) you can go as far as providing a syntax where all you have to do (literally) is to copy and paste the enumerator definitions from the enum into a macro. This works as long as every enumerator has an explicit definition (using =).
Edit: You can expand this to work even if not every enumerator has an explicit definition, but this shouldn't be required in this case.
I've once developed this for some physicists, so the example is about particles.
Usage example:
// required for this example
#include <iostream>
enum ParticleEnum
{
PROTON = 11,
ELECTRON = 42,
MUON = 43
};
// define macro (see below)
MAKE_ENUM(
ParticleEnum, // name of enum type
particle_enum_detail, // some namespace to place some types in
all_particles, // name of array to list all enumerators
// paste the enumerator definitions of your enum here
PROTON = 11,
ELECTRON = 42,
MUON = 43
) // don't forget the macro's closing paranthesis
int main()
{
for(ParticleEnum p : all_particles)
{
std::cout << p << ", ";
}
}
The macro yields to (effectively):
namespace particle_enum_detail
{
// definition of a type and some constants
constexpr ParticleEnum all_particles[] = {
PROTON,
ELECTRON,
MUON
};
}
using particle_enum_detail::all_particles;
macro definition
#define MAKE_ENUM(ENUM_TYPE, NAMESPACE, ARRAY_NAME, ...) \
namespace NAMESPACE \
{ \
struct iterable_enum_ \
{ \
using storage_type = ENUM_TYPE; \
template < typename T > \
constexpr iterable_enum_(T p) \
: m{ static_cast<storage_type>(p) } \
{} \
constexpr operator storage_type() \
{ return m; } \
template < typename T > \
constexpr iterable_enum_ operator= (T p) \
{ return { static_cast<storage_type>(p) }; } \
private: \
storage_type m; \
}; \
\
/* the "enumeration" */ \
constexpr iterable_enum_ __VA_ARGS__; \
/* the array to store all "enumerators" */ \
constexpr ENUM_TYPE ARRAY_NAME[] = { __VA_ARGS__ }; \
} \
using NAMESPACE::ARRAY_NAME; // macro end
Note: the type iterable_enum_ could as well be defined once outside the macro.
macro explanation
The idea is to allow a syntax like proton = 11, electron = 12 within the macro invocation. This works very easy for any kind of declaration, yet it makes problems for storing the names:
#define MAKE_ENUM(ASSIGNMEN1, ASSIGNMENT2) \
enum my_enum { ASSIGNMENT1, ASSIGNMENT2 }; \
my_enum all[] = { ASSIGNMENT1, ASSIGNMENT2 };
MAKE_ENUM(proton = 11, electron = 22);
yields to:
enum my_enum { proton = 11, electron = 22 }; // would be OK
my_enum all[] = { proton = 11, electron = 22 }; // cannot assign to enumerator
As with many syntactical tricks, operator overloading provides a way to overcome this problem; but the assignment operator has to be a member functions - and enums are not classes.
So why not use some constant objects instead of an enum?
enum my_enum { proton = 11, electron = 22 };
// alternatively
constexpr int proton = 11, electron = 12;
// the `constexpr` here is equivalent to a `const`
This does not yet solve our problem, it just demonstrates we can easily replace enums by a list of constants if we don't need the auto-increment feature of enumerators.
Now, the syntactical trick with operator overloading:
struct iterable_enum_
{
// the trick: a constexpr assignment operator
constexpr iterable_enum_ operator= (int p) // (op)
{ return {p}; }
// we need a ctor for the syntax `object = init`
constexpr iterable_enum_(int p) // (ctor)
: m{ static_cast<ParticleEnum>(p) }
{}
private:
ParticleEnum m;
};
constexpr iterable_enum_ proton = 11, electron = 22; // (1)
iterable_enum_ all_particles[] = { proton = 11, electron = 22 }; // (2)
The trick is, in line (1) the = designates a copy-initialisation, which is done by converting the number (11, 22) to a temporary of type particle by using the (ctor) and copying/moving the temporary via an implicitly-defined ctor to the destination object (proton, electron).
In contrast, the = in line (2) is resolved to an operator call to (op), which effectively returns a copy of the object on which it has been called (*this). The constexpr stuff allows to use these variables at compile time, e.g. in a template declaration. Due to restrictions on constexpr functions, we cannot simply return *this in the (op) function. Additionally, constexpr implies all restrictions of const.
By providing an implicit conversion operator, you can create the array in line (2) of type ParticleEnum:
// in struct particle
constexpr operator ParticleEnum() { return m; }
// in namespace particle_enum_detail
ParticleEnum all_particles[] = { proton = 11, electron = 22 };
Based on the articles given at the begin of the question, I derived a solution that is based in the assumption that you know the invalids ranges.
I really wanna knows if this is a good solution.
First, end you enum with something like that: CAPI_END = 60. It will helps to interates. So my code is:
typedef enum {
CAPI_SUBTYPE_NULL = 0, /* Null subtype. */
CAPI_SUBTYPE_DIAG_DFD = 1, /* Data Flow diag. */
CAPI_SUBTYPE_DIAG_ERD = 2, /* Entity-Relationship diag. */
CAPI_SUBTYPE_DIAG_STD = 3, /* State Transition diag. */
CAPI_SUBTYPE_DIAG_STC = 4, /* Structure Chart diag. */
CAPI_SUBTYPE_DIAG_DSD = 5, /* Data Structure diag. */
CAPI_SUBTYPE_SPEC_PROCESS = 6, /* Process spec. */
CAPI_SUBTYPE_SPEC_MODULE = 7, /* Module spec. */
CAPI_SUBTYPE_SPEC_TERMINATOR = 8, /* Terminator spec. */
CAPI_SUBTYPE_DD_ALL = 13, /* DD Entries (All). */
CAPI_SUBTYPE_DD_COUPLE = 14, /* DD Entries (Couples). */
CAPI_SUBTYPE_DD_DATA_AREA = 15, /* DD Entries (Data Areas). */
CAPI_SUBTYPE_DD_DATA_OBJECT = 16, /* DD Entries (Data Objects). */
CAPI_SUBTYPE_DD_FLOW = 17, /* DD Entries (Flows). */
CAPI_SUBTYPE_DD_RELATIONSHIP = 18, /* DD Entries (Relationships). */
CAPI_SUBTYPE_DD_STORE = 19, /* DD Entries (Stores). */
CAPI_SUBTYPE_DIAG_PAD = 35, /* Physical architecture diagram. */
CAPI_SUBTYPE_DIAG_BD = 36, /* Behaviour diagram. */
CAPI_SUBTYPE_DIAG_UCD = 37, /* UML Use case diagram. */
CAPI_SUBTYPE_DIAG_PD = 38, /* UML Package diagram. */
CAPI_SUBTYPE_DIAG_COD = 39, /* UML Collaboration diagram. */
CAPI_SUBTYPE_DIAG_SQD = 40, /* UML Sequence diagram. */
CAPI_SUBTYPE_DIAG_CD = 41, /* UML Class diagram. */
CAPI_SUBTYPE_DIAG_SCD = 42, /* UML State chart. */
CAPI_SUBTYPE_DIAG_ACD = 43, /* UML Activity chart. */
CAPI_SUBTYPE_DIAG_CPD = 44, /* UML Component diagram. */
CAPI_SUBTYPE_DIAG_DPD = 45, /* UML Deployment diagram. */
CAPI_SUBTYPE_DIAG_PFD = 47, /* Process flow diagram. */
CAPI_SUBTYPE_DIAG_HIER = 48, /* Hierarchy diagram. */
CAPI_SUBTYPE_DIAG_IDEF0 = 49, /* IDEF0 diagram. */
CAPI_SUBTYPE_DIAG_AID = 50, /* AID diagram. */
CAPI_SUBTYPE_DIAG_SAD = 51, /* SAD diagram. */
CAPI_SUBTYPE_DIAG_ASG = 59, /* ASG diagram. */
CAPI_END = 60 /* just to mark the end of your enum */
} CAPI_SUBTYPE_E ;
CAPI_SUBTYPE_E& operator++(CAPI_SUBTYPE_E& capi)
{
const int ranges = 2; // you have 2 invalid ranges in your example
int invalid[ranges][2] = {{8, 12}, {19, 34}}; // {min, max} (inclusive, exclusive)
CAPI_SUBTYPE_E next = CAPI_SUBTYPE_NULL;
for (int i = 0; i < ranges; i++)
if ( capi >= invalid[i][0] && capi < invalid[i][1] ) {
next = static_cast<CAPI_SUBTYPE_E>(invalid[i][1] + 1);
break;
} else {
next = static_cast<CAPI_SUBTYPE_E>(capi + 1);
}
// if ( next > CAPI_END )
// throw an exception
return capi = next;
}
int main()
{
for(CAPI_SUBTYPE_E i = CAPI_SUBTYPE_NULL; i < CAPI_END; ++i)
cout << i << endl;
cout << endl;
}
I'm providing only a pre increment operator. A post increment operator is let to be implemanted later.
The answer is "no, you cannot iterate over the elements of an enum in C++03 or C++11".
Now, you can describe the set of values of an enum in a way that can be understood at compile time.
template<typename E, E... Es>
struct TypedEnumList {};
typedef TypedEnumList<
CAPI_SUBTYPE_E,
CAPI_SUBTYPE_NULL, // etc
// ...
CAPI_SUBTYPE_DIAG_ASG
> CAPI_SUBTYPE_E_LIST;
which gives you a type CAPI_SUBTYPE_E_LIST which encapsulates the list of enum values.
We can then populate an array with these easily:
template<typename T, T... Es>
std::array<T, sizeof...(Es)> GetRuntimeArray( TypedEnumList<T, Es... > ) {
return { Es... };
}
auto Capis = GetRuntimeArray( CAPI_SUBTYPE_E_LIST() );
if you really need it. But this is just a special case of the more general case of being able to generate code for each element of your enum CAPI_SUBTYPE_E -- directly building a for loop isn't needed.
Amusingly, with a compliant C++11 compiler, we could write code that would generate our CAPI_SUBTYPE_E_LIST with particular enum elements iff those elements are actually in CAPI_SUBTYPE_E using SFINAE. This would be useful because we can take the most recent version of the API we can support, and have it auto-degrade (at compile time) if the API we compile against is more primitive.
To demonstrate the technique, I'll start with a toy enum
enum Foo { A = 0, /* B = 1 */ };
Imagine that B=1 is uncommented in the most modern version of the API, but is not there in the more primitive.
template<int index, typename EnumList, typename=void>
struct AddElementN: AddElementN<index-1, EnumList> {};
template<typename EnumList>
struct AddElementN<-1, EnumList, void> {
typedef EnumList type;
};
template<typename Enum, Enum... Es>
struct AddElementN<0, TypedEnumList<Enum, Es...>, typename std::enable_if< Enum::A == Enum::A >::type >:
AddElement<-1, TypedEnumList<Enum, A, Es...>>
{};
template<typename Enum, Enum... Es>
struct AddElementN<1, TypedEnumList<Enum, Es...>, typename std::enable_if< Enum::B == Enum::B >::type >:
AddElement<0, TypedEnumList<Enum, B, Es...>>
{};
// specialize this for your enum to call AddElementN:
template<typename Enum>
struct BuildTypedList;
template<>
struct BuildTypedList<CAPI_SUBTYPE_E>:
AddElementN<1, TypedEnumList<CAPI_SUBTYPE_E>>
{};
template<typename Enum>
using TypedList = typename BuildTypedList<Enum>::type;
now, if I wrote that right, TypedList<CAPI_SUBTYPE_E> contains B iff B is defined as an element of CAPI_SUBTYPE_E. This lets you compile against more than one version of the library, and get a different set of elements in your enum element list depending on what is in the library. You do have to maintain that annoying boilerplate (which could probably be made easier with macros or code generation) against the "final" version of the enums elements, but it should automatically handle previous versions at compile time.
This sadly requires lots of maintenance to work.
Finally, your requirement that this be dynamic: the only practical way for this to be dynamic is to wrap the 3rd party API in code that knows what the version of the API is, and exposes a different buffer of enum values (I'd put it in a std::vector) depending on what the version the API is. Then when you load the API, you also load this helper wrapper, which then uses the above techniques to build the set of elements of the enum, which you iterate over.
Some of this boilerplate can be made easier to write with some horrible macros, like ones that build the various AddElementN type SFINAE code by using the __LINE__ to index the recursive types. But that would be horrible.
Somewhat clearer (???) with a bit of boost preprocessing.
You define your enums by a sequence
#define CAPI_SUBTYPE_E_Sequence \
(CAPI_SUBTYPE_NULL)(0) \
(CAPI_SUBTYPE_DIAG_DFD)(1) ...
then you can automate (through macros) the declaration of the enum,
DECL_ENUM(CAPI_SUBTYPE_E) ;
the table that indexes it
DECL_ENUM_TABLE(CAPI_SUBTYPE_E);
the number of enums / size of the table
ENUM_SIZE(CAPI_SUBTYPE_E)
and access to it:
ITER_ENUM_i(i,CAPI_SUBTYPE_E)
Here is the full text.
#include <boost/preprocessor.hpp>
// define your enum as (name)(value) sequence
#define CAPI_SUBTYPE_E_Sequence \
(CAPI_SUBTYPE_NULL)(0) /* Null subtype. */ \
(CAPI_SUBTYPE_DIAG_DFD)(1) /* Data Flow diag. */ \
(CAPI_SUBTYPE_DIAG_ERD)(2) /* Entity-Relationship diag. */ \
(CAPI_SUBTYPE_DIAG_DSD)(5) /* Data Structure diag. */ \
(CAPI_SUBTYPE_DD_ALL)(13) /* DD Entries (All). */
// # enums
#define ENUM_SIZE(name) \
BOOST_PP_DIV(BOOST_PP_SEQ_SIZE(BOOST_PP_CAT(name,_Sequence)),2)
#define ENUM_NAME_N(N,seq) BOOST_PP_SEQ_ELEM(BOOST_PP_MUL(N,2),seq)
#define ENUM_VALUE_N(N,seq) BOOST_PP_SEQ_ELEM(BOOST_PP_INC(BOOST_PP_MUL(N,2)),seq)
// declare Nth enum
#define DECL_ENUM_N(Z,N,seq) \
BOOST_PP_COMMA_IF(N) ENUM_NAME_N(N,seq) = ENUM_VALUE_N(N,seq)
// declare whole enum
#define DECL_ENUM(name) \
typedef enum { \
BOOST_PP_REPEAT( ENUM_SIZE(name) , DECL_ENUM_N , BOOST_PP_CAT(name,_Sequence) ) \
} name
DECL_ENUM(CAPI_SUBTYPE_E) ;
// declare Nth enum value
#define DECL_ENUM_TABLE_N(Z,N,seq) \
BOOST_PP_COMMA_IF(N) ENUM_NAME_N(N,seq)
// declare table
#define DECL_ENUM_TABLE(name) \
static const name BOOST_PP_CAT(name,_Table) [ENUM_SIZE(name)] = { \
BOOST_PP_REPEAT( ENUM_SIZE(name) , DECL_ENUM_TABLE_N , BOOST_PP_CAT(name,_Sequence) ) \
}
DECL_ENUM_TABLE(CAPI_SUBTYPE_E);
#define ITER_ENUM_i(i,name) BOOST_PP_CAT(name,_Table) [i]
// demo
// outputs : [0:0] [1:1] [2:2] [3:5] [4:13]
#include <iostream>
int main() {
for (int i=0; i<ENUM_SIZE(CAPI_SUBTYPE_E) ; i++)
std::cout << "[" << i << ":" << ITER_ENUM_i(i,CAPI_SUBTYPE_E) << "] ";
return 0;
}
// bonus : check enums are unique and in-order
#include <boost/preprocessor/stringize.hpp>
#include <boost/static_assert.hpp>
#define CHECK_ENUM_N(Z,N,seq) \
BOOST_PP_IF( N , \
BOOST_STATIC_ASSERT_MSG( \
ENUM_VALUE_N(BOOST_PP_DEC(N),seq) < ENUM_VALUE_N(N,seq) , \
BOOST_PP_STRINGIZE( ENUM_NAME_N(BOOST_PP_DEC(N),seq) ) " not < " BOOST_PP_STRINGIZE( ENUM_NAME_N(N,seq) ) ) \
, ) ;
#define CHECK_ENUM(name) \
namespace { void BOOST_PP_CAT(check_enum_,name) () { \
BOOST_PP_REPEAT( ENUM_SIZE(name) , CHECK_ENUM_N , BOOST_PP_CAT(name,_Sequence) ) } }
// enum OK
CHECK_ENUM(CAPI_SUBTYPE_E)
#define Bad_Enum_Sequence \
(one)(1)\
(five)(5)\
(seven)(7)\
(three)(3)
// enum not OK : enum_iter.cpp(81): error C2338: seven not < three
CHECK_ENUM(Bad_Enum)
You cannot iterate over arbitrary enum in C++. For iterating, values should be put in some container. You can automate maintaining such a container using 'enum classes' as described here: http://www.drdobbs.com/when-enum-just-isnt-enough-enumeration-c/184403955http://www.drdobbs.com/when-enum-just-isnt-enough-enumeration-c/184403955
Use Higher Order macros
Here's the technique we've been using in our projects.
Concept:
The idea is to generate a macro called LISTING which contains the definition of name-value pairs and it takes another macro as an argument. In the example below I defined two such helper macros. 'GENERATE_ENUM' to generate the enum and 'GENERATE_ARRAY' to generate an iteratable array. Of course this can be extended as necessary. I think this solution gives you the most bang for the buck.
Conceptually it's very similar to iammilind's solution.
Example:
// helper macros
#define GENERATE_ENUM(key,value) \
key = value \
#define GENERATE_ARRAY(name,value) \
name \
// Since this is C++, I took the liberty to wrap everthing in a namespace.
// This done mostly for aesthetic reasons, you don't have to if you don't want.
namespace CAPI_SUBTYPES
{
// I define a macro containing the key value pairs
#define LISTING(m) \
m(NONE, 0), /* Note: I can't use NULL here because it conflicts */
m(DIAG_DFD, 1), \
m(DIAG_ERD, 2), \
...
m(DD_ALL, 13), \
m(DD_COUPLE, 14), \
...
m(DIAG_SAD, 51), \
m(DIAG_ASG, 59), \
typedef enum {
LISTING(GENERATE_ENUM)
} Enum;
const Enum At[] = {
LISTING(GENERATE_ARRAY)
};
const unsigned int Count = sizeof(At)/sizeof(At[0]);
}
Usage:
Now in code you can refer to the enum like this:
CAPI_SUBTYPES::Enum eVariable = CAPI_SUBTYPES::DIAG_STD;
You can iterate over the enumeration like this:
for (unsigned int i=0; i<CAPI_SUBTYPES::Count; i++) {
...
CAPI_SUBTYPES::Enum eVariable = CAPI_SUBTYPES::At[i];
...
}
Note:
If memory serves me right, C++11 enums live in their own namespaces (like in Java or C#) , therefore the above usage wouldn't work. You'd have to refer to the enum values like this CAPI_SUBTYPES::Enum::FooBar.
Put them into an array or other container and iterate over that. If you modify the enum, you will have to update the code that puts them in the container.
the beginnings of a solution involving no macros and (almost) no runtime overhead:
#include <iostream>
#include <utility>
#include <boost/mpl/vector.hpp>
#include <boost/mpl/find.hpp>
template<int v> using has_value = std::integral_constant<int, v>;
template<class...EnumValues>
struct better_enum
{
static constexpr size_t size = sizeof...(EnumValues);
using value_array = int[size];
static const value_array& values() {
static const value_array _values = { EnumValues::value... };
return _values;
}
using name_array = const char*[size];
static const name_array& names() {
static const name_array _names = { EnumValues::name()... };
return _names;
}
using enum_values = boost::mpl::vector<EnumValues...>;
struct iterator {
explicit iterator(size_t i) : index(i) {}
const char* name() const {
return names()[index];
}
int value() const {
return values()[index];
}
operator int() const {
return value();
}
void operator++() {
++index;
}
bool operator==(const iterator& it) const {
return index == it.index;
}
bool operator!=(const iterator& it) const {
return index != it.index;
}
const iterator& operator*() const {
return *this;
}
private:
size_t index;
};
friend std::ostream& operator<<(std::ostream& os, const iterator& iter)
{
os << "{ " << iter.name() << ", " << iter.value() << " }";
return os;
}
template<class EnumValue>
static iterator find() {
using iter = typename boost::mpl::find<enum_values, EnumValue>::type;
static_assert(iter::pos::value < size, "attempt to find a value which is not part of this enum");
return iterator { iter::pos::value };
}
static iterator begin() {
return iterator { 0 };
}
static iterator end() {
return iterator { size };
}
};
struct Pig : has_value<0> { static const char* name() { return "Pig";} };
struct Dog : has_value<7> { static const char* name() { return "Dog";} };
struct Cat : has_value<100> { static const char* name() { return "Cat";} };
struct Horse : has_value<90> { static const char* name() { return "Horse";} };
struct Monkey : has_value<1000> { static const char* name() { return "Monkey";} };
using animals = better_enum<
Pig,
Dog,
Cat,
Horse
>;
using namespace std;
auto main() -> int
{
cout << "size : " << animals::size << endl;
for (auto v : animals::values())
cout << v << endl;
for (auto v : animals::names())
cout << v << endl;
cout << "full iteration:" << endl;
for (const auto& i : animals())
{
cout << i << endl;
}
cout << "individials" << endl;
auto animal = animals::find<Dog>();
cout << "found : " << animal << endl;
while (animal != animals::find<Horse>()) {
cout << animal << endl;
++animal;
}
// will trigger the static_assert auto xx = animals::find<Monkey>();
return 0;
}
output:
size : 4
0
7
100
90
Pig
Dog
Cat
Horse
full iteration:
{ Pig, 0 }
{ Dog, 7 }
{ Cat, 100 }
{ Horse, 90 }
individials
found : { Dog, 7 }
{ Dog, 7 }
{ Cat, 100 }
Since an enum does not allow iteration you have to create an alternative representation of the enum and its range of values.
The approach that I would take would be a simple table lookup embedded in a class. The problem is that as the API modifies its enum with new entries, you would also need to update the constructor for this class.
The simple class that I would use would be comprised of a constructor to build the table along with a couple of methods to iterate over the table. Since you also want to know if there is a problem with table size when adding items, you could use an assert () macro that would issue an assert() in debug mode. In the source example below I use the preprocessor to test for debug compile or not and whether assert has been included or not so as to provide a mechanism for basic consistency checking.
I have borrowed an idea I saw from P. J. Plauger in his book Standard C Library of using a simple lookup table for ANSI character operations in which the character is used to index into a table.
To use this class you would do something like the following which uses a for loop to iterate over the set of values in the table. Within the body of the loop you would do whatever you want to do with the enum values.
CapiEnum myEnum;
for (CAPI_SUBTYPE_E jj = myEnum.Begin(); !myEnum.End(); jj = myEnum.Next()) {
// do stuff with the jj enum value
}
Since this class enumerates over the values, I have arbitrarily chosen to return the value of CAPI_SUBTYPE_NULL in those cases where we have reached the end of the enumeration. So the return value in the case of a table lookup error is in the valid range however it can not be depended upon. There fore a check on the End() method should be done to see if the end of an iteration has been reached. Also after doing the construction of the object one can check the m_bTableError data member to see if there was an error during construction.
The source example for the class follows. You would need to update the constructor with the enum values for the API as they change. Unfortunately there is not much that can be done to automate the check on an update enum however we do have tests in place in a debug compile to check that the table is large enough and that the value of the enum being put into the table is within range of the table size.
class CapiEnum {
public:
CapiEnum (void); // constructor
CAPI_SUBTYPE_E Begin (void); // method to call to begin an iteration
CAPI_SUBTYPE_E Next (void); // method to get the next in the series of an iteration
bool End (void); // method to indicate if we have reached the end or not
bool Check (CAPI_SUBTYPE_E value); // method to see if value specified is in the table
bool m_TableError;
private:
static const int m_TableSize = 256; // set the lookup table size
static const int m_UnusedTableEntry = -1;
int m_iIterate;
bool m_bEndReached;
CAPI_SUBTYPE_E m_CapiTable[m_TableSize];
};
#if defined(_DEBUG)
#if defined(assert)
#define ADD_CAPI_ENUM_ENTRY(capi) (((capi) < m_TableSize && (capi) > m_UnusedTableEntry) ? (m_CapiTable[(capi)] = (capi)) : assert(((capi) < m_TableSize) && ((capi) > m_UnusedTableEntry)))
#else
#define ADD_CAPI_ENUM_ENTRY(capi) (((capi) < m_TableSize && (capi) > m_UnusedTableEntry) ? (m_CapiTable[(capi)] = (capi)) : (m_TableError = true))
#endif
#else
#define ADD_CAPI_ENUM_ENTRY(capi) (m_CapiTable[(capi)] = (capi))
#endif
CapiEnum::CapiEnum (void) : m_bEndReached(true), m_iIterate(0), m_TableError(false)
{
for (int iLoop = 0; iLoop < m_TableSize; iLoop++) m_CapiTable[iLoop] = static_cast <CAPI_SUBTYPE_E> (m_UnusedTableEntry);
ADD_CAPI_ENUM_ENTRY(CAPI_SUBTYPE_NULL);
// .....
ADD_CAPI_ENUM_ENTRY(CAPI_SUBTYPE_DIAG_ASG);
}
CAPI_SUBTYPE_E CapiEnum::Begin (void)
{
m_bEndReached = false;
for (m_iIterate = 0; m_iIterate < m_TableSize; m_iIterate++) {
if (m_CapiTable[m_iIterate] > m_UnusedTableEntry) return m_CapiTable[m_iIterate];
}
m_bEndReached = true;
return CAPI_SUBTYPE_NULL;
}
CAPI_SUBTYPE_E CapiEnum::Next (void)
{
if (!m_bEndReached) {
for (m_iIterate++; m_iIterate < m_TableSize; m_iIterate++) {
if (m_CapiTable[m_iIterate] > m_UnusedTableEntry) return m_CapiTable[m_iIterate];
}
}
m_bEndReached = true;
return CAPI_SUBTYPE_NULL;
}
bool CapiEnum::End (void)
{
return m_bEndReached;
}
bool CapiEnum::Check (CAPI_SUBTYPE_E value)
{
return (value >= 0 && value < m_TableSize && m_CapiTable[value] > m_UnusedTableEntry);
}
And if you like you could add an additional method to retrieve the current value of the iteration. Notice that rather than incrementing to the next, the Current() method uses whatever the iteration index is currently at and starts searching from the current position. So if the current position is a valid value it just returns it otherwise it will find the first valid value. Alternatively, you could make this just return the current table value pointed to by the index and if the value is invalid, set an error indicator.
CAPI_SUBTYPE_E CapiEnum::Current (void)
{
if (!m_bEndReached) {
for (m_iIterate; m_iIterate < m_TableSize; m_iIterate++) {
if (m_CapiTable[m_iIterate] > m_UnusedTableEntry) return m_CapiTable[m_iIterate];
}
}
m_bEndReached = true;
return CAPI_SUBTYPE_NULL;
}
Here's one more approach. One bonus is that your compiler may warn you if you omit an enum value in a switch:
template<typename T>
void IMP_Apply(const int& pSubtype, T& pApply) {
switch (pSubtype) {
case CAPI_SUBTYPE_NULL :
case CAPI_SUBTYPE_DIAG_DFD :
case CAPI_SUBTYPE_DIAG_ERD :
case CAPI_SUBTYPE_DIAG_STD :
case CAPI_SUBTYPE_DIAG_STC :
case CAPI_SUBTYPE_DIAG_DSD :
case CAPI_SUBTYPE_SPEC_PROCESS :
case CAPI_SUBTYPE_SPEC_MODULE :
case CAPI_SUBTYPE_SPEC_TERMINATOR :
case CAPI_SUBTYPE_DD_ALL :
case CAPI_SUBTYPE_DD_COUPLE :
case CAPI_SUBTYPE_DD_DATA_AREA :
case CAPI_SUBTYPE_DD_DATA_OBJECT :
case CAPI_SUBTYPE_DD_FLOW :
case CAPI_SUBTYPE_DD_RELATIONSHIP :
case CAPI_SUBTYPE_DD_STORE :
case CAPI_SUBTYPE_DIAG_PAD :
case CAPI_SUBTYPE_DIAG_BD :
case CAPI_SUBTYPE_DIAG_UCD :
case CAPI_SUBTYPE_DIAG_PD :
case CAPI_SUBTYPE_DIAG_COD :
case CAPI_SUBTYPE_DIAG_SQD :
case CAPI_SUBTYPE_DIAG_CD :
case CAPI_SUBTYPE_DIAG_SCD :
case CAPI_SUBTYPE_DIAG_ACD :
case CAPI_SUBTYPE_DIAG_CPD :
case CAPI_SUBTYPE_DIAG_DPD :
case CAPI_SUBTYPE_DIAG_PFD :
case CAPI_SUBTYPE_DIAG_HIER :
case CAPI_SUBTYPE_DIAG_IDEF0 :
case CAPI_SUBTYPE_DIAG_AID :
case CAPI_SUBTYPE_DIAG_SAD :
case CAPI_SUBTYPE_DIAG_ASG :
/* do something. just `applying`: */
pApply(static_cast<CAPI_SUBTYPE_E>(pSubtype));
return;
}
std::cout << "Skipped: " << pSubtype << '\n';
}
template<typename T>
void Apply(T& pApply) {
const CAPI_SUBTYPE_E First(CAPI_SUBTYPE_NULL);
const CAPI_SUBTYPE_E Last(CAPI_SUBTYPE_DIAG_ASG);
for (int idx(static_cast<int>(First)); idx <= static_cast<int>(Last); ++idx) {
IMP_Apply(idx, pApply);
}
}
int main(int argc, const char* argv[]) {
class t_apply {
public:
void operator()(const CAPI_SUBTYPE_E& pSubtype) const {
std::cout << "Apply: " << static_cast<int>(pSubtype) << '\n';
}
};
t_apply apply;
Apply(apply);
return 0;
}
I'm using this type of constructions to define my own enums:
#include <boost/unordered_map.hpp>
namespace enumeration
{
struct enumerator_base : boost::noncopyable
{
typedef
boost::unordered_map<int, std::string>
kv_storage_t;
typedef
kv_storage_t::value_type
kv_type;
typedef
std::set<int>
entries_t;
typedef
entries_t::const_iterator
iterator;
typedef
entries_t::const_iterator
const_iterator;
kv_storage_t const & kv() const
{
return storage_;
}
const char * name(int i) const
{
kv_storage_t::const_iterator it = storage_.find(i);
if(it != storage_.end())
return it->second.c_str();
return "empty";
}
iterator begin() const
{
return entries_.begin();
}
iterator end() const
{
return entries_.end();
}
iterator begin()
{
return entries_.begin();
}
iterator end()
{
return entries_.end();
}
void register_e(int val, std::string const & desc)
{
storage_.insert(std::make_pair(val, desc));
entries_.insert(val);
}
protected:
kv_storage_t storage_;
entries_t entries_;
};
template<class T>
struct enumerator;
template<class D>
struct enum_singleton : enumerator_base
{
static enumerator_base const & instance()
{
static D inst;
return inst;
}
};
}
#define QENUM_ENTRY(K, V, N) K, N register_e((int)K, V);
#define QENUM_ENTRY_I(K, I, V, N) K = I, N register_e((int)K, V);
#define QBEGIN_ENUM(NAME, C) \
enum NAME \
{ \
C \
} \
}; \
} \
#define QEND_ENUM(NAME) \
}; \
namespace enumeration \
{ \
template<> \
struct enumerator<NAME>\
: enum_singleton< enumerator<NAME> >\
{ \
enumerator() \
{
QBEGIN_ENUM(test_t,
QENUM_ENTRY(test_entry_1, "number uno",
QENUM_ENTRY_I(test_entry_2, 10, "number dos",
QENUM_ENTRY(test_entry_3, "number tres",
QEND_ENUM(test_t)))))
int _tmain(int argc, _TCHAR* argv[])
{
BOOST_FOREACH(int x, enumeration::enumerator<test_t>::instance())
std::cout << enumeration::enumerator<test_t>::instance().name(x) << "=" << x << std::endl;
return 0;
}
Also you can replace storage_ type to boost::bimap to have bidirectional correspondance int <==> string
There are a lot of answers to this question already, but most of them are either very complicated or inefficient in that they don't directly address the requirement of iterating over an enum with gaps. Everyone so far has said that this is not possible, and they are sort of correct in that there is no language feature to allow you to do this. That certainly does not mean you can't, and as we can see by all the answers so far, there are many different ways to do it. Here is my way, based on the enum you have provided and the assumption that it's structure won't change much. Of course this method can be adapted as needed.
typedef enum {
CAPI_SUBTYPE_NULL = 0, /* Null subtype. */
CAPI_SUBTYPE_DIAG_DFD = 1, /* Data Flow diag. */
CAPI_SUBTYPE_DIAG_ERD = 2, /* Entity-Relationship diag. */
CAPI_SUBTYPE_DIAG_STD = 3, /* State Transition diag. */
CAPI_SUBTYPE_DIAG_STC = 4, /* Structure Chart diag. */
CAPI_SUBTYPE_DIAG_DSD = 5, /* Data Structure diag. */
CAPI_SUBTYPE_SPEC_PROCESS = 6, /* Process spec. */
CAPI_SUBTYPE_SPEC_MODULE = 7, /* Module spec. */
CAPI_SUBTYPE_SPEC_TERMINATOR = 8, /* Terminator spec. */
CAPI_SUBTYPE_DD_ALL = 13, /* DD Entries (All). */
CAPI_SUBTYPE_DD_COUPLE = 14, /* DD Entries (Couples). */
CAPI_SUBTYPE_DD_DATA_AREA = 15, /* DD Entries (Data Areas). */
CAPI_SUBTYPE_DD_DATA_OBJECT = 16, /* DD Entries (Data Objects). */
CAPI_SUBTYPE_DD_FLOW = 17, /* DD Entries (Flows). */
CAPI_SUBTYPE_DD_RELATIONSHIP = 18, /* DD Entries (Relationships). */
CAPI_SUBTYPE_DD_STORE = 19, /* DD Entries (Stores). */
CAPI_SUBTYPE_DIAG_PAD = 35, /* Physical architecture diagram. */
CAPI_SUBTYPE_DIAG_BD = 36, /* Behaviour diagram. */
CAPI_SUBTYPE_DIAG_UCD = 37, /* UML Use case diagram. */
CAPI_SUBTYPE_DIAG_PD = 38, /* UML Package diagram. */
CAPI_SUBTYPE_DIAG_COD = 39, /* UML Collaboration diagram. */
CAPI_SUBTYPE_DIAG_SQD = 40, /* UML Sequence diagram. */
CAPI_SUBTYPE_DIAG_CD = 41, /* UML Class diagram. */
CAPI_SUBTYPE_DIAG_SCD = 42, /* UML State chart. */
CAPI_SUBTYPE_DIAG_ACD = 43, /* UML Activity chart. */
CAPI_SUBTYPE_DIAG_CPD = 44, /* UML Component diagram. */
CAPI_SUBTYPE_DIAG_DPD = 45, /* UML Deployment diagram. */
CAPI_SUBTYPE_DIAG_PFD = 47, /* Process flow diagram. */
CAPI_SUBTYPE_DIAG_HIER = 48, /* Hierarchy diagram. */
CAPI_SUBTYPE_DIAG_IDEF0 = 49, /* IDEF0 diagram. */
CAPI_SUBTYPE_DIAG_AID = 50, /* AID diagram. */
CAPI_SUBTYPE_DIAG_SAD = 51, /* SAD diagram. */
CAPI_SUBTYPE_DIAG_ASG = 59 /* ASG diagram. */
} CAPI_SUBTYPE_E;
struct ranges_t
{
int start;
int end;
};
ranges_t ranges[] =
{
{CAPI_SUBTYPE_NULL, CAPI_SUBTYPE_NULL},
{CAPI_SUBTYPE_DIAG_DFD, CAPI_SUBTYPE_DIAG_DSD},
{CAPI_SUBTYPE_SPEC_PROCESS, CAPI_SUBTYPE_SPEC_TERMINATOR},
{CAPI_SUBTYPE_DD_ALL, CAPI_SUBTYPE_DD_STORE},
{CAPI_SUBTYPE_DIAG_PAD, CAPI_SUBTYPE_DIAG_SAD},
{CAPI_SUBTYPE_DIAG_ASG, CAPI_SUBTYPE_DIAG_ASG},
};
int numRanges = sizeof(ranges) / sizeof(*ranges);
for( int rangeIdx = 0; rangeIdx < numRanges; ++rangeIdx )
{
for( int enumValue = ranges[rangeIdx].start; enumValue <= ranges[rangeIdx].end; ++enumValue )
{
processEnumValue( enumValue );
}
}
Or something along those lines.
The only real 'solution' I finally came up with to solve this problem is to create a pre-run script that reads the c/c++ file(s) containing the enums and generates a class file that has a list of all the enums as vectors. This is very much the same way Visual Studio supports T4 Templates. In the .Net world it's pretty common practice but since I can't work in that environment I was forced to do it this way.
The script I wrote is in Ruby, but you could do it in whatever language. If anyone wants the source script, I uploaded it here. It's by no means a perfect script but it fit the bill for my project. I encourage anyone to improve on it and give tips here.