Skip to content

make fields and skins convenient to use, support zipping together views to form new views (basic composability)

Manuel Tobias Schiller requested to merge convenient-fields-20170623 into master

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
}

Merge request reports