Collections » CnxArray module

CnxArray(T, N) is a struct template for a type-safe fixed-capacity, dynamic-size, stack allocated array type. CnxArray(T, N) is bounds safe, allocator aware, and provides Cnx compatible random access iterators. It supports user-defined default-constructors, copy-constructors, and destructors for its elements. It's design is similar to C++'s std::array, but with expanded semantics and functionality.

Because of its size, N, template parameter, instantiations of CnxArray(T, N) for builtins or any Cnx types are not provided, and will have to be instantiated by the user. The requirements are similar to CnxVector(T), and requires minimal boiler-plate:

  1. a typedef of your type to provide an alphanumeric name for it. (for template and macro parameters)
  2. a typedef for pointer to your type as Ref(YourType), for use with the iterators
  3. atypedeffor pointer to const your type asConstRef(YourType)`, for use with the iterators
  4. Instantiations for Cnx iterators for the typedefs provided in (2) and (3) (already instantiated for builtins and CnxString)
  5. Instantiation of CnxOption(T) for your type (already instantiated for builtins and CnxString)

For CnxArray(T, N) we recommend you provide the instantiation for user-defined types in a separate "template instantiation" .h/.c file pair, but you can also provide them inline with your type like discussed in CnxVector(T)'s documentation.

Example:

// in `YourType.h`
typedef struct YourType {
    // members
} YourType;

typedef YourType* Ref(YourType);
typedef const YourType* ConstRef(YourType);

DeclCnxIterators(Ref(YourType));
DeclCnxIterators(ConstRef(YourType));
// the rest of your public interface

// in `CnxOptionYourType.h`
#include <Cnx/Def.h>
#include "YourType.h"
#define OPTION_T YourType
#define OPTION_DECL TRUE
#include <Cnx/Option.h>
#undef OPTION_T
#undef OPTION_DECL

// in `CnxOptionYourType.c`
#include "CnxOptionYourType.h"
#define OPTION_T YourType
#define OPTION_IMPL TRUE
#include <Cnx/Option.h>
#undef OPTION_T
#undef OPTION_IMPL

// in `CnxArrayYourTypeYourN.h"
#include "YourType.h"
#include "CnxOptionYourType.h"

#define ARRAY_T YourType
#define ARRAY_N YourN
#define ARRAY_DECL TRUE
#include <Cnx/Array.h>
#undef ARRAY_T
#undef ARRAY_N
#undef ARRAY_DECL

// in `CnxArrayYourTypeYourN.c`
#include "CnxArrayYourTypeYourN.h"

#define ARRAY_T YourType
#define ARRAY_N YourN
#define ARRAY_IMPL TRUE
#include <Cnx/Array.h>
#undef ARRAY_T
#undef ARRAY_N
#undef ARRAY_IMPL

The, somewhere else in your codebase, you can easily use the instantiation:

#include "YourType.h"
#include "CnxArrayYourTypeYourN.h"

CnxArray(YourType, YourN) create_and_fill_your_type_array(void) {
    let_mut array = cnx_array_new(YourType, YourN);

    cnx_array_resize(array, YourN);

    return array;
}

CnxArray(T, N) implements CnxFormat, but because of its generic type, can't be automatically converted to its CnxFormat implementation, so we have to cast to it explicitly:

#include "Cnx/IO.h"
void print_array(const CnxArray(YourType, YourN)* array) {
    println("{}", as_format_t(CnxArray(YourType, YourN), *array);
}

You can provide user-defined default-constructor, copy-constructor, and destructor for elements of your type, and custom allocator for memory allocations they might need to perform. (CnxArray(T, N) is always stack allocated, so the memory allocator is only for constructor and destructor functions for your type)

YourType your_type_constructor(CnxAllocator allocator) {
    let_mut t = (YourType){0};
    // whatever you need to do to init your type
    return t;
}

YourType your_type_copy_constructor(const YourType* restrict elem, CnxAllocator allocator) {
    let_mut t = (YourType){0};
    // whatever you need to do to copy `elem` to `t`
    return t;
}

void your_type_destructor(YourType* restrict t, CnxAllocator allocator) {
    // whatever you need to do to cleanup `t`
}

CnxArray(YourType, YourN) create_and_fill_your_type_array(void) {
    let_mut array = cnx_array_new_with_allocator_and_collection_data(YourType,
                        YourN,
                        // This `CnxAllocator` will only be associated with this specific array,
                        // not all `CnxArray(YourType, YourN)`s
                        cnx_allocator_from_custom_stateless_allocator(your_malloc,
                                                                      your_realloc,
                                                                      your_free),
                        // This `CnxCollectionData(CollectionType)` will only be
                        // associated with this specific array, not all
                        // `CnxArray(YourType, YourN)`s
                        ((CnxCollectionData(CnxArray(YourType, YourN))) {
                            // can be left NULL, will be defaulted
                            .m_constructor = your_type_constructor,
                            // can be left NULL, will disable `cnx_array_clone(self)` for
                            // this array
                            .m_copy_constructor = your_type_copy_constructor,
                            // can be left NULL, will be defaulted
                            .m_destructor = your_type_destructor
                        })
                    );

    // fill the array with default constructed elements
    cnx_array_resize(array, YourN);

    return array;
}

CnxArray(T, N) also provides Cnx random access iterators and optional scoped destruction:

void example(void) {
    cnx_array_scoped(u32, 10) array = cnx_array_new(u32, 10);
    ranged_for(i, 0, 9) {
        cnx_array_push_back(array, i);
    }

    // loop over the elements in the array by value, as an iteration
    foreach(elem, vec) {
        println("{}", elem);
    }
    // array goes out of scope here and `cnx_array_free(array)` is called on it
    // because we declared it as scoped
}

Like other Cnx collections, CnxArray(T, N) provides its type-agnostic usage through a vtable pointer contained in the struct, and provides macros which wrap the usage of the vtable, making access simpler. If you prefer to not use this method of access, you can call the typed functions directly by in-fixing the contained type in the associated function name. IE: for CnxArray(i32, 10), and an array array, the equivalent function call for cnx_array_push_back(array, element) would be cnx_array_i32_10_push_back(&array, element)

Defines

#define CnxArray(T, N)
Macro alias for a CnxArray(T, N) containing up to N Ts.
#define cnx_array_new(T, N)
Creates a new CnxArray(T, N) with defaulted associated functions.
#define cnx_array_new_with_allocator(T, N, allocator)
Creates a new CnxArray(T, N) with defaulted associated functions.
#define cnx_array_new_with_collection_data(T, N, collection_data_ptr)
Creates a new CnxArray(T, N) with provided associated functions.
#define cnx_array_new_with_allocator_and_collection_data(T, N, allocator, collection_data_ptr)
Creates a new CnxArray(T, N) with provided associated functions.
#define cnx_array_clone(self)
Clones the given CnxArray(T, N)
#define cnx_array_at_mut(self, index)
Returns a mutable reference to the element at the given index into the given CnxArray(T, N)
#define cnx_array_at(self, index)
Returns a const reference to the element at the given index into the given CnxArray(T, N)
#define cnx_array_front_mut(self)
Returns a mutable reference to the first element in the given CnxArray(T, N)
#define cnx_array_front(self)
Returns a const reference to the first element in the given CnxArray(T, N)
#define cnx_array_back_mut(self)
Returns a mutable reference to the last element in the given CnxArray(T, N)
#define cnx_array_back(self)
Returns a const reference to the last element in the given CnxArray(T, N)
#define cnx_array_data_mut(self)
Returns a mutable reference to the internal array in the given CnxArray(T, N)
#define cnx_array_data(self)
Returns a const reference to the internal array in the given CnxArray(T, N)
#define cnx_array_is_empty(self)
Returns whether the given CnxArray(T, N) is empty.
#define cnx_array_is_full(self)
Returns whether the given CnxArray(T, N) is full.
#define cnx_array_size(self)
Returns the current size of the given CnxArray(T, N)
#define cnx_array_capacity(self)
Returns the capacity of the given CnxArray(T, N) (ie the N)
#define cnx_array_resize(self, new_size)
Resizes the given CnxArray(T, N) to new_size
#define cnx_array_clear(self)
Resets the given CnxArray(T, N) to a size of zero, destroying any active elements in the array.
#define cnx_array_push_back(self, element)
Pushes a new element into the given CnxArray(T, N), at the end.
#define cnx_array_pop_back(self)
Removes the last element from the given CnxArray(T, N) and returns it.
#define cnx_array_insert(self, element, index)
Inserts the given element at the given index in the CnxArray(T, N)
#define cnx_array_erase(self, index)
Erases the element at index from the CnxArray(T, N)
#define cnx_array_erase_n(self, index, num_elements)
Erases num_elements elements from the CnxArray(T, N), starting at index
#define cnx_array_free(self)
Frees the given CnxArray(T, N), destroying its elements.
#define cnx_array_begin(self)
Returns a CnxRandomAccessIterator into the mutable iteration of the given CnxArray(T, N), starting at the beginning of the iteration (pointing at the beginning of the array)
#define cnx_array_end(self)
Returns a CnxRandomAccessIterator into the mutable iteration of the given CnxArray(T, N), starting at the end of the iteration (pointing at the end of the array)
#define cnx_array_rbegin(self)
Returns a CnxRandomAccessIterator into the mutable iteration of the given CnxArray(T, N), starting at the beginning of the reversed iteration (pointing at the end of the array)
#define cnx_array_rend(self)
Returns a CnxRandomAccessIterator into the mutable iteration of the given CnxArray(T, N), starting at the end of the reversed iteration (pointing at the beginning of the array)
#define cnx_array_iterator_equals(first, second)
Returns whether the given pair of iterators are equal (they belong to the same collection and point to the same element), IE: if first == second
#define cnx_array_cbegin(self)
Returns a CnxRandomAccessIterator into the const iteration of the given CnxArray(T, N), starting at the beginning of the iteration (pointing at the beginning of the array)
#define cnx_array_cend(self)
Returns a CnxRandomAccessIterator into the const iteration of the given CnxArray(T, N), starting at the end of the iteration (pointing at the end of the array)
#define cnx_array_crbegin(self)
Returns a CnxRandomAccessIterator into the const iteration of the given CnxArray(T, N), starting at the beginning of the reversed iteration (pointing at the end of the array)
#define cnx_array_crend(self)
Returns a CnxRandomAccessIterator into the const iteration of the given CnxArray(T, N), starting at the end of the reversed iteration (pointing at the beginning of the array)
#define cnx_array_const_iterator_equals(first, second)
Returns whether the given pair of const iterators are equal (they belong to the same collection and point to the same element), IE: if first == second
#define CnxScopedArray(T, N)
declare a CnxArray(T, N) variable with this attribute to have cnx_array_free automatically called on it at scope end

Define documentation

#define CnxArray(T, N)

Macro alias for a CnxArray(T, N) containing up to N Ts.

CnxArray(T, N) is a generic, type-safe, fixed-capacity, dynamic-size, stack allocated array type. CnxArray(T, N) is bounds safe and allocator aware. It's implemented as a "struct template", which enables 100% type safety while also providing abstractions that enable type agnostic use. It has greatly increased ergonomics over manually managing an array and provides many useful features such as random access iterators and optional scoped destruction.

Example:

#include "Cnx/Array.h"
#include "Cnx/IO.h"
#include "Array_i32_10.h"

// create a `CnxArray(i32, 10)` with default allocator, element default-constructor, and
element
// destructor
let_mut array = cnx_array_new(i32, 10);
// fill the array with 10 elements
ranged_for(i, 0, 9) {
    cnx_array_push_back(array, i);
}

prints information about `array` to `stdout`
because `CnxArray(T, N)` is generic, and thus can't be used in a `_Generic` match arm prior
to the type's instantiation, we have to explicitly cast to the `CnxFormat` Trait to get
`CnxArray(T, N)`'s implementation of the Trait println("{}", cnx_as_format_t(CnxArray(i32,
10), array));

// prints `array`'s elements to stdout
foreach(elem, array) {
    println("{}", elem);
}

{
    let_mut CnxScopedArray(i32, 10) array2 = cnx_array_new(i32, 10);
    foreach(elem, array) {
        cnx_array_push_back(array2, elem);
    }
}
// `array2` is destroyed here because it was scoped
// `array` is not destroyed

The above example assumes that the CnxArray(T, N) template has been instantiated for CnxArray(i32, 10) in "Array_i32_10.h" and an associated ".c" file. See the module-level page for details

Like other Cnx collections, CnxArray(T, N) provides its type-agnostic usage through a vtable pointer contained in the struct, and provides macros which wrap the usage of the vtable, making access simpler. If you prefer to not use this method of access, you can call the typed functions directly by in-fixing the contained type in the associated function name. IE: for CnxArray(i32, 10), and an array array, the equivalent function call for cnx_array_push_back(array, element) would be cnx_array_i32_10_push_back(&array, element)

#define cnx_array_new(T, N)

Creates a new CnxArray(T, N) with defaulted associated functions.

Parameters
T - The element type of the CnxArray(T, N) instantiation to create
N - The capacity of the CnxArray(T, N) instantiation to create
Returns a new CnxArray(T, N)

Creates a new CnxArray(T, N) with:

  1. default associated element default-constructor,
  2. default associated element destructor
  3. default associated memory allocator (potentially used in element constructor and destructor, memory for the CnxArray(T, N) itself is stack allocated)

#define cnx_array_new_with_allocator(T, N, allocator)

Creates a new CnxArray(T, N) with defaulted associated functions.

Parameters
T - The element type of the CnxArray(T, N) instantiation to create
N - The capacity of the CnxArray(T, N) instantiation to create
allocator - The memory allocator to pass to the element default-constructor and destructor
Returns a new CnxArray(T, N)

Creates a new CnxArray(T, N) with:

  1. default associated element default-constructor,
  2. default associated element destructor
  3. given associated memory allocator (potentially used in element constructor and destructor, memory for the CnxArray(T, N) itself is stack allocated)

#define cnx_array_new_with_collection_data(T, N, collection_data_ptr)

Creates a new CnxArray(T, N) with provided associated functions.

Parameters
T - The element type of the CnxArray(T, N) instantiation to create
N - The capacity of the CnxArray(T, N) instantiation to create
collection_data_ptr - Pointer to the CnxCollectionData containing the element default-constructor, element destructor, and element copy-constructor to use
Returns a new CnxArray(T, N)

Creates a new CnxArray(T, N) with:

  1. possibly user-provided element default-constructor
  2. possibly user-provided element destructor
  3. default memory allocator (potentially used in element constructor and destructor, memory for the CnxArray(T, N) itself is stack allocated)

#define cnx_array_new_with_allocator_and_collection_data(T, N, allocator, collection_data_ptr)

Creates a new CnxArray(T, N) with provided associated functions.

Parameters
T - The element type of the CnxArray(T, N) instantiation to create
N - The capacity of the CnxArray(T, N) instantiation to create
allocator - The memory allocator to pass to the element default-constructor and destructor
collection_data_ptr - Pointer to the CnxCollectionData containing the element default-constructor, element destructor, and element copy-constructor to use
Returns a new CnxArray(T, N)

Creates a new CnxArray(T, N) with:

  1. possibly user-provided element default-constructor
  2. possibly user-provided element destructor
  3. user-provided memory allocator (potentially used in element constructor and destructor, memory for the CnxArray(T, N) itself is stack allocated)

#define cnx_array_clone(self)

Clones the given CnxArray(T, N)

Parameters
self - The CnxArray(T, N) to clone
Returns a clone of the given array

Creates a deep copy of the given CnxArray(T, N) calling the associated copy constructor for each element stored in it.

#define cnx_array_at_mut(self, index)

Returns a mutable reference to the element at the given index into the given CnxArray(T, N)

Parameters
self - The CnxArray(T, N) to get an element from
index - The index of the desired element
Returns a mutable reference to the element at the given index

#define cnx_array_at(self, index)

Returns a const reference to the element at the given index into the given CnxArray(T, N)

Parameters
self - The CnxArray(T, N) to get an element from
index - The index of the desired element
Returns a const reference to the element at the given index

#define cnx_array_front_mut(self)

Returns a mutable reference to the first element in the given CnxArray(T, N)

Parameters
self - The CnxArray(T, N) to get the first element from
Returns a mutable reference to the first element

#define cnx_array_front(self)

Returns a const reference to the first element in the given CnxArray(T, N)

Parameters
self - The CnxArray(T, N) to get the first element from
Returns a const reference to the first element

#define cnx_array_back_mut(self)

Returns a mutable reference to the last element in the given CnxArray(T, N)

Parameters
self - The CnxArray(T, N) to get the last element from
Returns a mutable reference to the last element

#define cnx_array_back(self)

Returns a const reference to the last element in the given CnxArray(T, N)

Parameters
self - The CnxArray(T, N) to get the last element from
Returns a const reference to the last element

#define cnx_array_data_mut(self)

Returns a mutable reference to the internal array in the given CnxArray(T, N)

Parameters
self - The CnxArray(T, N) to get the internal array from
Returns a mutable reference to the internal array

#define cnx_array_data(self)

Returns a const reference to the internal array in the given CnxArray(T, N)

Parameters
self - The CnxArray(T, N) to get the internal array from
Returns a const reference to the internal array

#define cnx_array_is_empty(self)

Returns whether the given CnxArray(T, N) is empty.

Parameters
self - The CnxArray(T, N) to check for emptiness
Returns true if empty, false otherwise

#define cnx_array_is_full(self)

Returns whether the given CnxArray(T, N) is full.

Parameters
self - The CnxArray(T, N) to check for fullness
Returns true if full, false otherwise

#define cnx_array_size(self)

Returns the current size of the given CnxArray(T, N)

Parameters
self - The CnxArray(T, N) to get the size of
Returns the size of the CnxArray(T, N)

#define cnx_array_capacity(self)

Returns the capacity of the given CnxArray(T, N) (ie the N)

Parameters
self - The CnxArray(T, N) to get the capacity of
Returns the capacity of the CnxArray(T, N)

#define cnx_array_resize(self, new_size)

Resizes the given CnxArray(T, N) to new_size

Parameters
self - The CnxArray(T, N) to resize
new_size - The new size for the given array

new_size must be strictly <= N. If new_size is greater than the current size, elements will be default constructed until self contains new_size elements. If new_size is less than the current size, elements will be destroyed.

#define cnx_array_clear(self)

Resets the given CnxArray(T, N) to a size of zero, destroying any active elements in the array.

Parameters
self - The CnxArray(T, N) to clear

#define cnx_array_push_back(self, element)

Pushes a new element into the given CnxArray(T, N), at the end.

Parameters
self - The CnxArray(T, N) to add an element to
element - The element to add to the array

The current size must be strictly less than N.

#define cnx_array_pop_back(self)

Removes the last element from the given CnxArray(T, N) and returns it.

Parameters
self - The CnxArray(T, N) to pop the last element from
Returns Some(T) if size greater than 0, otherwise None(T)

#define cnx_array_insert(self, element, index)

Inserts the given element at the given index in the CnxArray(T, N)

Parameters
self - The CnxArray(T, N) to insert in to
element - The element to insert into the array
index - The index to insert element at

The current size must be strictly less than N. index must be strictly less than N. `index must be strictly less than the current size.

#define cnx_array_erase(self, index)

Erases the element at index from the CnxArray(T, N)

Parameters
self - The CnxArray(T, N) to erase from
index - The index of the element to erase

index must be strictly less than N. index must be strictly less than the current size.

#define cnx_array_erase_n(self, index, num_elements)

Erases num_elements elements from the CnxArray(T, N), starting at index

Parameters
self - The CnxArray(T, N) to erase from
index - The index to begin erasing at
num_elements - The number of elements to erase

index must be strictly less than N index must be strictly less than the current size. index + num_elements must be strictly less than N index + num_elements must be strictly less than the current size

#define cnx_array_free(self)

Frees the given CnxArray(T, N), destroying its elements.

Parameters
self - The CnxArray(T, N) to free

CnxArray(T, N) does not allocate memory itself, so it does not necessarily need to be freed, but if elements require a provided destructor to run, then cnx_array_free should be called on the array to ensure elements are properly cleaned up.

#define cnx_array_begin(self)

Returns a CnxRandomAccessIterator into the mutable iteration of the given CnxArray(T, N), starting at the beginning of the iteration (pointing at the beginning of the array)

Parameters
self - The CnxArray(T, N) to get an iterator to
Returns a random access iterator at the beginning of the array

#define cnx_array_end(self)

Returns a CnxRandomAccessIterator into the mutable iteration of the given CnxArray(T, N), starting at the end of the iteration (pointing at the end of the array)

Parameters
self - The CnxArray(T, N) to get an iterator to
Returns a random access iterator at the end of the array

#define cnx_array_rbegin(self)

Returns a CnxRandomAccessIterator into the mutable iteration of the given CnxArray(T, N), starting at the beginning of the reversed iteration (pointing at the end of the array)

Parameters
self - The CnxArray(T, N) to get an iterator to
Returns a random access iterator at the beginning of the reversed array

#define cnx_array_rend(self)

Returns a CnxRandomAccessIterator into the mutable iteration of the given CnxArray(T, N), starting at the end of the reversed iteration (pointing at the beginning of the array)

Parameters
self - The CnxArray(T, N) to get an iterator to
Returns a random access iterator at the end of the reversed array

#define cnx_array_iterator_equals(first, second)

Returns whether the given pair of iterators are equal (they belong to the same collection and point to the same element), IE: if first == second

Parameters
first - The LHS iterator of the equality check
second - The RHS iterator of the equality check
Returns true if they are equal, false otherwise

#define cnx_array_cbegin(self)

Returns a CnxRandomAccessIterator into the const iteration of the given CnxArray(T, N), starting at the beginning of the iteration (pointing at the beginning of the array)

Parameters
self - The CnxArray(T, N) to get an iterator to
Returns a random access iterator at the beginning of the array

#define cnx_array_cend(self)

Returns a CnxRandomAccessIterator into the const iteration of the given CnxArray(T, N), starting at the end of the iteration (pointing at the end of the array)

Parameters
self - The CnxArray(T, N) to get an iterator to
Returns a random access iterator at the end of the array

#define cnx_array_crbegin(self)

Returns a CnxRandomAccessIterator into the const iteration of the given CnxArray(T, N), starting at the beginning of the reversed iteration (pointing at the end of the array)

Parameters
self - The CnxArray(T, N) to get an iterator to
Returns a random access iterator at the beginning of the reversed array

#define cnx_array_crend(self)

Returns a CnxRandomAccessIterator into the const iteration of the given CnxArray(T, N), starting at the end of the reversed iteration (pointing at the beginning of the array)

Parameters
self - The CnxArray(T, N) to get an iterator to
Returns a random access iterator at the end of the reversed array

#define cnx_array_const_iterator_equals(first, second)

Returns whether the given pair of const iterators are equal (they belong to the same collection and point to the same element), IE: if first == second

Parameters
first - The LHS iterator of the equality check
second - The RHS iterator of the equality check
Returns true if they are equal, false otherwise

#define CnxScopedArray(T, N)

declare a CnxArray(T, N) variable with this attribute to have cnx_array_free automatically called on it at scope end

Parameters
T - The element type of the CnxArray(T, N) instantiation
N - The capacity of the CnxArray(T, N) instantiation