make fields and skins convenient to use, support zipping together views to form new views (basic composability)
The code is mostly backward compatible to the old way of doing things, but now has vastly improved support for user-friendly fields and skins. A practical upshot of this is that with this new convenient way of defining fields and skins, one can now create views of a part of the container (just some fields), and one can zip views together to create a new view. Better documentation is to follow over the next couple of days (I hope). Meanwhile, here are two simple examples:
Let's start with something trivial, i.e. the fields just have standard getters/setters, and the skin just supports the getters and setters of its fields.
#include "SOAContainer.h"
/***********************************************************************
* "simple" fields and skins example
**********************************************************************/
namespace MyPoint {
// a field struct x: type float, accessors: (const) float& x() (const)
SOAFIELD_TRIVIAL(x, x, float);
SOAFIELD_TRIVIAL(y, y, float); // same for y, z
SOAFIELD_TRIVIAL(z, z, float);
// make a skin Point, with fields x, y, z
SOASKIN_TRIVIAL(PointSkin, x, y, z);
}
void foo()
{
using namespace MyPoint;
SOA::Container<std::vector, PointSkin> c;
// fill the container with sth.
// test extraction of some fields into a new view
auto v1 = c.view<x, z>();
auto v2 = c.view<y>();
auto y = v2.front().y();
// zip several views into a common one ("join" the fields)
auto v4 = zip(v1, v2);
auto x = v4.front().x();
// avoid compiler warning about unused x, y
x += y;
}
The example below is somewhat more complex in that both fields and skin get extra functionality (in addition to the standard getters/setters from the previous example):
#include "SOAContainer.h"
/***********************************************************************
* "complex" fields and skins example
**********************************************************************/
namespace MyVeloPixel {
SOAFIELD(x, float,
SOAFIELD_ACCESSORS(x) // generate getter/setter x()
// assume hot area at small |x|, provide test
bool isInHotAreaX() const noexcept
{ return std::abs(this->x()) < 6.; }
);
SOAFIELD(y, float,
SOAFIELD_ACCESSORS(y)
// assume hot area at small |y|, provide test
bool isInHotAreaY() const noexcept
{ return std::abs(this->y()) < 6.; }
);
SOASKIN(MyVeloPixelSkin, x, y) {
// use constructors/assignment operators from underlying tuple
SOASKIN_INHERIT_DEFAULT_METHODS(MyVeloPixelSkin);
// any kind of special constructors, assignment operators go here...
// we have our own methods
float r2() const noexcept
{ return this->x() * this->x() + this->y() * this->y(); }
float r() const noexcept
{ return std::sqrt(r2()); }
bool isHot() const noexcept
{ return this->isInHotAreaX() || this->isInHotAreaY(); }
};
}
void bar()
{
using namespace MyVeloPixel;
SOA::Container<std::vector, MyVeloPixelSkin> c;
// fill c somehow...
// remove hits that are too far away, or in one of the hot areas...
c.erase(std::remove_if(c.begin(), c.end(),
[] (decltype(c)::const_reference hit)
{ return hit.r2() > 42.f || hit.isHot(); }), c.end());
// view and zip will still work, but it'll generate trivial skins that are
// just the sum of accessors in the fields. In this case, extracting fields
// from cc means that the r2(), r(), isHot() methods would be lost in the
// autogenerated skin...
auto v = c.view<x, y>();
v.front().isInHotAreaX(); // will keep working
// v.front().r() - doesn't work (yet) - complex skin lost
}