# Constructing an n_container

## Description

An N-dimensional (multi-dimensional) container must be constructed before it can be used. The data type to be contained must first be declared as a `SDLT_PRIMITIVE`, then a data layout is chosen, and finally the shape of the container is determined describing the extents of each dimension.

Specifying Data Layout

Rather than defining different containers for different data layouts, the data layout to use is specified as a template parameter to the container.

Available layouts are summarized in table below. Full details can found on the table in the topic n_container.

Layout | Description
--- | ---
    layout::soa<>
| Structure of Arrays (SOA). Each data member of the Primitive will have its own N-dimensional array.
    layout::soa_per_row<>
| Structure of Arrays Per Row. Each data member of the Primitive will have its own 1-dimensional array per row. Layout repeats for remaining N-1 dimensions.
    layout::aos_by_struct
| Array of Structures (AOS) Accessed by Struct. Native AOS layout and data access.
    layout::aos_by_stride
| Array of Structures Accessed by Stride. Native SOA data access through pointers to the built in types of members using a stride to account for the size of the Primitive.

Numbers and Constants

In order to define shape, integer values can be provided in three different forms, each successively providing less information to compiler. It is advised to use as precise specification as possible. The compiler may optimize better with more information.

Integer Value Specification | Description
--- | ---
    fixed<int NumberT>
| Known at compile time. `foo(fixed<1080>(), fixed<1920>());` The suffix `_fixed` will declare an equivalent literal. For example, `(1080_fixed` is equivalent to `fixed<1080>`. `foo(1080_fixed, 1920_fixed); )`
    aligned<int AlignmentT>(number)
| Programmer guarantees the number is a multiple of the `AlignmentT`. `foo(aligned<8>(height), aligned<128>(width));`
    “int”
| Arbitrary integer value. `foo(width, height);`

Specifying Container Shape

`n_extent_t<…>` is a variadic template that accepts any number of arguments defining dimensions. Because construction using this type may look unclear, a generator object, `n_extent` , is provided to construct extents for all dimensions using a familiar array-definition-like syntax. Extent values may be specified using the most precise representation possible, as described above, to allow the compiler to better prove any potential data alignments.

    n_extent[height][width];                 // OK
    n_extent[height][aligned<128>(width)];  // Better
    n_extent[1080_fixed][1920_fixed];       // Best

Defining an n_container

Using a previously declared primitive (same as SDLT v1),

    struct RGBAs { float red, green, blue, alpha; };
    SDLT_PRIMITIVE(RGBAs, red, green, blue, alpha)

A two-dimensional container of RGBAs with HD image size 1920x0180 can be declared and instantiated as in the below example.

    typedef n_container<RGBAs, layout::soa,
                        n_extent_t<fixed<1080>, fixed<1920>>> HdImage;
    HdImage image1;

If sizes are not known, a container may be defined with extents unknown to the compiler but known at run-time when an instance of the container is created.

    typedef n_container<RGBAs, layout::soa, n_extent_t<int, int>> Image;
    Image image2(n_extent[height][width]);

Additionally, the templated factory function make_n_container<PrimitiveT, LayoutT> may be used to create containers.

    auto image1 = make_n_container<RGBAs,
                              layout::soa>(n_extent[1080_fixed][1920_fixed]);
    auto image2 = make_n_container<RGBAs,
                               layout::soa>(n_extent[height][width]);

Accessing Cells

Containers own data. To get to the data inside, use an "accessor."

    auto ca = image1.const_access();
    auto a = image2.access();

Specify the index for each dimension with a series of calls to the array subscript operator [], similar to a multi-dimensional array in C.

    RGBAs pixel = ca[y][x];
    float greyscale = (pixel.red + pixel.green + pixel.blue)/3;
    a[y][x] = RGBAs(greyscale, greyscale, greyscale);

Discovering Extents

Accessors know their extents.

Use template function `extent_d<int DimensionT>(object)`.

    for (int y = 0; y < extent_d<0>(ca); ++y)
        for (int x = 0; x < extent_d<1>(ca); ++x) {
              RGBAs pixel = ca[y][x];
    		 // …
        }

For convenience, non-template methods are also provided.

    for (int y = 0; y < ca.extent_d0(); ++y)
        for (int x = 0; x < ca.extent_d1(); ++x) {
              RGBAs pixel = ca[y][x];
    		 // …
        }

Lowering Dimensions

The result of not specifying all the dimensions required by an accessor is a new accessor with a lower rank that can then be accessed.

    auto cay = ca[y];
    RGBAs pixel = cay[x];

Parent topic: Usage Guidelines: Function Calls and Containers