Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Automaticaly convert array of structures to structure of arrays in C++

I have a struct Widget which has several fields inside:

struct Widget
{
    Some field1;
    Another second_field;
    ...
};

Conceptually I need an array or vector of these Widgets, i.e. std::vector<Widget>. But for performance reasons all those fields should be in it's own array, i.e.:

struct Widgets
{
    vector<Some> field1;
    vector<Another> second_field;
    ...
};

So, given that Widget what are possible approaches in C++ to scatter it's fields on several vectors, i.e. to convert array of structures to struct of arrays of fields? Manual definition of some reasonable metadata is OK (inside or outside Widget), but solution without additional metadata is preferred.

To clarify, I am looking for a automatic or semi-automatic way of converting Widget struct to Widgets struct, without manually coding all boilerplate glue.

like image 923
mechanical Avatar asked May 02 '18 05:05

mechanical


2 Answers

The core functionality can be implemented using std::transform and a few lambda functions.

To get a std::vector<Some> from a std::vector<Widget>, use:

 std::vector<Widget> v1 = { ... };
 std::vector<Some> v2(v1.size());
 std::transform(v1.begin(),
                v1.end(),
                v2.begin(),
                [](Widget const& w) -> Some { return w.field1; });

I'll let you figure out the rest.

I am looking for a automatic or semi-automatic way of converting Widget struct to Widgets struct, without manually coding all boilerplate glue.

You will have to write some boilerplate code for each member variable of Widget. You will be able to reduce the amount of boiler plate code a bit by using a helper function of your own.

E.g.

template <typename FieldType, typename Lambda>
void scatter(std::vector<Widget> const& widgets,
             std::vector<FieldType>& fields,
             Lmabda l)
{
   fields.resize(widgets.size());
   std::transform(widgets.begin(), widgets.end(), fields.begin(), l);
}

and use it as:

 std::vector<Widget> v1 = { ... };
 Widgets w;
 scatter(v1, w.field1, [](Widget const& w) -> Some { return w.field1; });
 scatter(v1, w.second_field, [](Widget const& w) -> Another { return w.second_field; });
 ...
like image 100
R Sahu Avatar answered Nov 20 '22 16:11

R Sahu


You could overload the [] operator so that the members adjacent in memory (as the second example) but you can behave as if there was a vector of structs:

struct Widget { 
  Some &field1; Another &second_field; ... 
  Widget(Some &f1, Another &f2 ...) : field1(f1), second_field(f2) ... {};
};

struct Widgets
{
   Widgets(int size) : field1(size), second_field(size) ... {};
   vector<Some> field1;
   vector<Another> second_field;
   Widget &operator[](int i) { return { field1[i], second_field[i] }; }
};

Now you can treat Widgets as a regular array of structs:

Widgets w(5);
w[3].field1 = Some(42);

The compiler is good at optimizing functions but not the way you store data. Thus you may expect operator[] not to initialize a new struct if you want to extract an element or similar, while the data are still vectorized.

like image 26
Kostas Avatar answered Nov 20 '22 17:11

Kostas