1

Suppos I need to hold some data consisting of n Foo's, n Bars and n Bazs. The elements of corresponding indices are related, so you can also think of this data as n triplets of Foo, Bar andBaz`. The first description of the data is a "structure of arrays", the second is an "array of structures"; you can find a discussion of these two layouts here on StackOverflow.

Now, if we know n at compile time, it's pretty easy on your fiegers to express both perceptions in C++.

Array of Structures (AoS):

struct {
    Foo foo;
    Bar bar;
    Baz baz;
} aos_form[n];

Structure of Arrays (SoA):

struct {
    Foo foo[n];
    Bar bar[n];
    Baz baz[n];
} soa_form;

but note we are already breaking the DRY principle when we say n 3 times. This is greatly exacerbated if we now want to allocate memory for our data dynamically - say with std::unique_ptrs:

Array of Structures (AoS):

struct {
    Foo foo;
    Bar bar;
    Baz baz;
} data_tuple_t;
auto aos_form = std::make_unique<data_tuple_t[]>(n);

reasonable enough, although we seem to need a type definition now. But how about SoA? Well,

Structure of Arrays (SoA):

struct {
    std::unique_ptr<Foo[]> foo;
    std::unique_ptr<Bar[]> bar;
    std::unique_ptr<Bas[]> baz;
} soa_form = {
    std::make_unique<Foo[]>(n),
    std::make_unique<Bar[]>(n),
    std::make_unique<Baz[]>(n)
}

Now that's just terrible! We're repeating ourselves in 3 different ways:

  • Specifying the field type twice - in type definition and in assignment
  • Having to be "verbose" rather than "opaque" or "implicit" both in definition and in initialization.
  • Having to specify the length thrice.

Other than writing a specially-customized class, what can I do more simply and generally to make the Soa FORM "easier on the eyes" (and the fingers)?

einpoklum
  • 118,144
  • 57
  • 340
  • 684
  • You could use a variadic template class to capture this SOA pattern in a generic way, for as many types as you want. The implementation itself would avoid the duplication you don't like, and use would be even simpler: your `soa_form` could just be declared as `Soa`. Essentially it would be something like `std::tuple` except that instead of storing 1 instance of each type, you store N (you could have versions where N is a compile-time quantity and another that is dynamic). – BeeOnRope Jun 15 '18 at 02:47
  • @BeeOnRope: But wouldn't that mean my members would then be unnamed? – einpoklum Jun 15 '18 at 09:10
  • Yes, for something like `tuple` (in fact, you could actually just _use_ `std::tuple` along with a template function helper to create it with the right array types), you'd end up with generic indexed accessors like `get<0>`. To name them, you could use a subclass with accessors like `getFoo() { return get<0>(); }` - but maybe that's just trading one type of boilerplate for another. – BeeOnRope Jun 15 '18 at 16:50
  • 1
    Another option is to reduce the boilerplate with various helper classes, e.g., a builder class that takes `n` and then exposes a template method to do the `make_unique` part with the correct `n`. You could also declare a class which only has a typedef or two allowing you to inherit from that class, or declare a typedef of that class, to use the typedef to get rid of the `std::unique_ptr` part, with `array_ptr` which isn't that much better... A lot of the redundancy results from the fact that fundamentally there are more degrees of freedom in SoA. – BeeOnRope Jun 15 '18 at 16:59
  • That is, SoA is kind of just a _convention_ that "ok, these arrays will have the same length and the combination of 1 element from each array at the same index will logically represent an element", but nothing enforces that syntactically. The AoS on the other hand really binds the the types in a 1:1:1 relationship. In principle, you could declare arrays of different lengths with SoA or use values from different indexes to represent an object: so you are looking for things that remove these degrees of freedom, by e.g. tying all arrays to the same element count. – BeeOnRope Jun 15 '18 at 17:02
  • @BeeOnRope: I wouldn't mind something with stronger guarantees, but I still want simple syntax for it. – einpoklum Jun 15 '18 at 17:03
  • I mean there are always macros (the feature-of-which-we-dare-not-speak-its-name) which would probably give you whatever syntax you want. – BeeOnRope Jun 15 '18 at 17:04
  • @BeeOnRope: If I could use something like Antony Polukihn's magic_get voodoo to get an arbitrary struct and a number, and create the unique_ptr's, maybe that would do the trick. – einpoklum Jun 15 '18 at 19:47

0 Answers0