LLAMA vs. C++

LLAMA tries hard to provide experience and constructs similar to native C++. The following tables compare how various constructs in C++ translate to LLAMA:

Containers and views

Construct

Native C++

LLAMA

LLAMA (alternative)

Defining structs/records

struct VecCpp {
  float x;
  float y;
};
struct ParticleCpp {
  VecCpp pos;
  float mass;
  bool flags[3];
};
struct X{}; struct Y{}; struct Pos{}; struct Mass{}; struct Flags{};
using VecRec = llama::Record<
  llama::Field<X, float>,
  llama::Field<Y, float>
>;
using ParticleRec = llama::Record<
  llama::Field<Pos, VecRec>,
  llama::Field<Mass, float>,
  llama::Field<Flags, bool[3]>
>;

Defining array extents

using size_type = ...;
size_type n = ...;
using ArrayExtents = ...;
ArrayExtents n = ...;

Defining the memory layout

-

using Mapping = ...;
Mapping m(n, ...);

A collection of n things in memory

std::vector<ParticleCpp> view(n);
auto view = llama::allocView(m);
llama::View<ArrayExtents, ParticleRec, ...> view;

Useful for static array dimensions.

Values and references

Construct

Native C++

LLAMA

LLAMA (alternative)

wrong

Declare single local record

ParticleCpp p;
llama::One<ParticleRec> p
ParticleCpp p;

Or any type layout compatible type supporting the tuple interface.

ParticleRec p;

ParticleRec is an empty struct (a type list)!

Copy memory -> local

p = view[i];
p = view[i];
p = view[i];

Assigns field by field using tuple interface.

Copy local -> memory

view[i] = p;
view[i] = p;
view[i] = p;

Assigns field by field using tuple interface.

Copy a single record from memory to local

ParticleCpp p = view[i];
llama::One<ParticleRec> p = view[i];
ParticleCpp p = view[i];

Assigns field by field using tuple interface

auto p = view[i];

p is a reference, not a copy!

Create a reference to a single record in memory

ParticleCpp& p = view[i];
auto p = view[i];
// decltype(p) == llama::RecordRef<...>
auto&& p = view[i];
auto& p = view[i];

Compilation error!

Copy a single sub-record from memory to local

VecCpp v = view[i].pos;
llama::One<VecRec> v = view[i](Pos{});
VecRec v = view[i](Pos{});

Assigns field by field using tuple interface.

auto v = view[i](Pos{});

v is a reference, not a copy!

Create a reference to a single sub-record in memory

VecCpp& v = view[i].pos;
auto v = view[i](Pos{});
// decltype(v) == llama::RecordRef<...>
auto&& v = view[i](Pos{});
auto& p = view[i](Pos{});

Compilation error!

Copy a single record leaf field from memory to local

float y = view[i].pos.y;
float y = view[i](Pos{}, Y{});
float y = view[i](Pos{})(Y{});

Create a reference to a single leaf field in memory

float& y = view[i].pos.y;
float& y = view[i](Pos{});
auto&& y = view[i](Pos{});
auto y = view[i](Pos{});

y is a copy (of type float), not a reference!

Create a copy of a single local record

auto p2 = p;
auto p2 = p;

Create a reference to a single local record

auto& r = p;
auto r = p();

Access with an empty tag list.

Notice that the use of auto to declare a local copy of a value read through a reference, e.g. auto pos = view[i].pos; // copy, does not work as expected in LLAMA. LLAMA makes extensive use of proxy reference types (including llama::RecordRef), where a reference is sometimes represented as a value and sometimes as a real C++ reference. The only consistent way to deal with this duality in LLAMA is the use a forwarding reference auto&& when we want to have a reference (native or proxy) into a LLAMA data structure, and to use a concrete type when we want to make a copy.