4

Is it possible to write a preprocessor macro that automatically iterates for all members of a structure?

I have such a structure (automatically generated from a Simulink model):

typedef struct {
  real_T driveStatusword;
  real_T posSensor[2];
  real_T softAbortDemand;
} ExtU_motionCtrlRTOS_T;

And a similar one:

struct CoreInputOffsets
{
    uint32_t driveStatusword;
    uint32_t posSensor;
    uint32_t softAbortDemand;
};

And I would like to do such an operation:

void getCoreInputOffsets(CoreInputOffsets* pCoreInputOffsets)
{
    pCoreInputOffsets->driveStatusword = offsetof(ExtU_motionCtrlRTOS_T, driveStatusword);
    pCoreInputOffsets->posSensor = offsetof(ExtU_motionCtrlRTOS_T, posSensor);
    pCoreInputOffsets->softAbortDemand = offsetof(ExtU_motionCtrlRTOS_T, softAbortDemand);
}

But without having to edit this function each time the structure changes, by iterating for all members of CoreInputOffsets.

Axel Williot
  • 485
  • 6
  • 13

2 Answers2

3

from c++14, yes we do have compile time reflection of (almost all) aggregate types, see Antony Polukhin's magic get library (and this cppcon presentation to see how it works). I think you can also make it work in c++11 with some ABI support.

For example, to assign to an ExtU_motionCtrlRTOS_T x; you'd simply write

boost::pfr::flat_structure_tie(x) = boost::pfr::flat_structure_tie(some_unrelated_pod);

where I assumed that members are assigned in order. Note that I used the flat tie version, to assign nested arrays element-wise.


Now, in light of the above, it would be wiser to avoid relying on offsetof() as you're doing now and just exploit all compile time info for related operations (this will also probably give you faster code).

Anyway, if you still want to get offsets, a verbatim transcription of your code may look like:

#include <boost/pfr/flat/core.hpp>

struct CoreInputOffsets
{
    uint32_t driveStatusword;
    uint32_t posSensor[2];
    uint32_t softAbortDemand;
};

template <typename T,std::size_t... Is>
void assignOffsets( CoreInputOffsets& offsets, std::index_sequence<Is...> )
{
  T t;
  (( boost::pfr::flat_get<Is>(offsets) = reinterpret_cast<char*>(&boost::pfr::flat_get<Is>(t)) - reinterpret_cast<char*>(&boost::pfr::flat_get<0>(t)) ), ...);
}

template <typename T>
void assignOffsets( CoreInputOffsets& offsets )
{
  assignOffsets<T>( offsets, std::make_index_sequence< boost::pfr::flat_tuple_size<T>::value >{} );
}

void getCoreInputOffsets(CoreInputOffsets* pCoreInputOffsets)
{
  assignOffsets<ExtU_motionCtrlRTOS_T>( *pCoreInputOffsets );
}

with the caveats:

  • this is c++17 (you can make it c++14 compliant though)
  • the code taking the actual offsets needs a dummy ExtU_motionCtrlRTOS_T; this is not a big deal given that you'll assign it just once, I suppose
  • the code taking the actual offsets via pointer substraction gives undefined behavior standard-wise, you'll need to verify it's legal for your platform
  • CoreInputOffsets::posSensor shall be an array and will get two offsets now
Massimiliano Janes
  • 5,524
  • 1
  • 10
  • 22
1

There are no "automatic" means, no.

And unfortunately your structures are automatically generated. If they were under your full control, I'd recommend the REFLECTABLE macro like it is described here.

Please read that answer, maybe you can restructure your code and/or work flow to make it work?

Yamakuzure
  • 367
  • 2
  • 9