Generated Documentation (C API)

Contents

Generated Documentation (C API)#

enum FP_MagicNumbers#

Magic numbers used to identify fat pointer types.

These magic numbers are stored in the header of each fat pointer to identify its allocation type and validate that a pointer is indeed a fat pointer.

Note

These were chosen explicitly to be invalid unicode code points.

Values:

enumerator FP_MAGIC_NUMBER#

Base magic number (high byte mask)

enumerator FP_HEAP_MAGIC_NUMBER#

Heap-allocated fat pointer.

enumerator FP_STACK_MAGIC_NUMBER#

Stack-allocated fat pointer.

enumerator FP_DYNARRAY_MAGIC_NUMBER#

Dynamic array fat pointer.

typedef struct __fp_view fp_void_view#

View type for untyped (void) data.

static thread_local struct __FatPointerHeader *__fp_global_header#

Thread local header utilized as an intermediate by fp_alloca and fp_alloca_void.

static inline void *__fp_alloc_default_impl(void *p, size_t size) noexcept#

Default memory allocation function used by the fat pointer library.

This function handles allocation, reallocation, and deallocation:

  • If p is NULL and size > 0: allocates new memory

  • If p is not NULL and size > 0: reallocates and copies data

  • If size is 0: frees memory and returns NULL

  • If size equals current allocation size: may return p unchanged

You can override this by defining FP_ALLOCATION_FUNCTION before including this header.

// Custom allocator example
void* my_allocator(void* p, size_t size) {
    // Your custom allocation logic
    return custom_realloc(p, size);
}
#define FP_ALLOCATION_FUNCTION my_allocator
Parameters:
  • p – Pointer to reallocate (NULL for new allocation)

  • size – Size in bytes to allocate (0 to free)

Returns:

Pointer to allocated memory or NULL if freed

static inline constexpr bool is_fp(const void *p) noexcept#

Check if a pointer is a valid fat pointer.

int* arr = fp_malloc(int, 10);
int* regular = malloc(sizeof(int) * 10);

assert(is_fp(arr) == true);
assert(is_fp(regular) == false);
assert(is_fp(NULL) == false);

fp_free_and_null(arr);
free(regular);

Parameters:

p – Pointer to check

Returns:

true if p is a valid fat pointer, false otherwise

static inline constexpr size_t fp_magic_number(const void *p) noexcept#

Get the magic number of a fat pointer.

See also

FP_MagicNumbers

Parameters:

p – Fat pointer

Returns:

Magic number identifying the allocation type

static inline constexpr bool fp_is_stack_allocated(const void *p) noexcept#

Check if a fat pointer is stack-allocated.

int* heap = fp_malloc(int, 10);
int* stack = fp_alloca(int, 10);

assert(!fp_is_stack_allocated(heap));
assert(fp_is_stack_allocated(stack));

Parameters:

p – Fat pointer

Returns:

true if stack-allocated, false otherwise

static inline constexpr bool fp_is_heap_allocated(const void *p) noexcept#

Check if a fat pointer is heap-allocated.

int* heap = fp_malloc(int, 10);
int* stack = fp_alloca(int, 10);

assert(fp_is_heap_allocated(heap));
assert(!fp_is_heap_allocated(stack));

Parameters:

p – Fat pointer

Returns:

true if heap-allocated, false otherwise

static inline void fp_free(void *p) noexcept#

Free a heap-allocated fat pointer.

int* data = fp_malloc(int, 100);
// ... use data ...
fp_free(data);

Warning

Only use with heap-allocated fat pointers (from fp_malloc/fp_realloc). Stack-allocated pointers are freed automatically.

Parameters:

p – Fat pointer to free

static inline constexpr size_t fp_length(const void *p) noexcept#

Get the number of elements in a fat pointer.

double* arr = fp_malloc(double, 75);
printf("Array has %zu elements\n", fp_length(arr)); // Prints: 75

Parameters:

p – Fat pointer

Returns:

Number of elements (not bytes) in the allocation

static inline constexpr bool fp_empty(const void *p) noexcept#

Check if a fat pointer is empty (zero length)

int* empty = fp_malloc(int, 0);
int* full = fp_malloc(int, 10);
assert(fp_empty(empty));
assert(!fp_empty(full));

Parameters:

p – Fat pointer

Returns:

true if empty, false otherwise

void *memswap(void *a, void *b, size_t n) noexcept#

Swap memory contents of two regions.

int x = 10, y = 20;
memswap(&x, &y, sizeof(int));
// Now x == 20, y == 10

Parameters:
  • a – First memory region

  • b – Second memory region

  • n – Number of bytes to swap

Returns:

Pointer to first region (second before the swap), or NULL if regions are identical

static inline bool fp_swap(void *a, void *b) noexcept#

Swap contents of two fat pointers.

Both pointers must have the same size for the swap to succeed.

int* arr1 = fp_malloc(int, 5);
int* arr2 = fp_malloc(int, 5);

fp_iterate(arr1) { *i = 1; }
fp_iterate(arr2) { *i = 2; }

fp_swap(arr1, arr2);
// Now arr1 contains all 2s, arr2 contains all 1s
Parameters:
  • a – First fat pointer

  • b – Second fat pointer

Returns:

true if swap succeeded, false if sizes don’t match

static inline constexpr int fp_view_compare(const fp_void_view a, const fp_void_view b) noexcept#

Compare two void views for equality.

Parameters:
  • a – First view

  • b – Second view

Returns:

0 if equal, non-zero otherwise

static inline constexpr bool fp_view_equal(const fp_void_view a, const fp_void_view b) noexcept#

Check if two void views are equal.

Parameters:
  • a – First view

  • b – Second view

Returns:

true if views have same size and content, false otherwise

static inline bool fp_view_swap(fp_void_view a, fp_void_view b) noexcept#

Swap contents of two void views.

Parameters:
  • a – First view

  • b – Second view

Returns:

true if swap succeeded, false if sizes don’t match

do_and_assert(action, assertion)#

Executes an action and asserts a condition based on the result in debug mode.

The action is always executed, but the assertion only fires in debug builds. This ensures side effects occur in both debug and release builds.

do_and_assert(fopen("file.txt", "r"), res != NULL);
Parameters:
  • action – Expression to execute (result stored in ‘res’)

  • assertion – Condition to assert (can reference ‘res’)

assert_with_side_effects(assertion)#

Assert macro where the condition itself has side effects that must always execute.

assert_with_side_effects(write(fd, buffer, size) == size);

Parameters:
  • assertion – Expression to evaluate (always executed, assertion checked in debug)

FP_NOEXCEPT#

noexcept in C++, empty in C

FP_CONSTEXPR#

constexpr in C++, empty in C

FP_ALLOCATION_FUNCTION#
FP_MAX(a, b)#

Maximum of two values.

Parameters:
  • a – First value

  • b – Second value

Returns:

Maximum of a and b

FP_MIN(a, b)#

Minimum of two values.

Parameters:
  • a – First value

  • b – Second value

Returns:

Minimum of a and b

fp_alloca_void(_typesize, _size)#

Allocate a fat pointer on the stack (generic version)

Stack-allocated fat pointers are automatically freed when the scope exits. Use fp_alloca() macro for type-safe version.

void process() {
    void* buffer = fp_alloca_void(1, 1024); // 1024 bytes
    // Use buffer...
    // Automatically freed on return
}

Warning

Stack allocations have limited size. Use fp_malloc() for large allocations.

Parameters:
  • _typesize – Size of each element in bytes

  • _size – Number of elements to allocate

Returns:

Void pointer to allocated stack memory with fat pointer header

fp_alloca(type, _size)

Allocate a typed fat pointer on the stack.

Best practices:

  • Use fp_alloca() for temporary buffers < 10KB

  • Use fp_malloc() for anything larger or with dynamic lifetime

  • Never return stack-allocated pointers

  • Be careful with recursion and stack allocations

void compute() {
    int* numbers = fp_alloca(int, 50);
    for(int i = 0; i < 50; i++) {
        numbers[i] = i * i;
    }
    printf("Length: %zu\n", fp_length(numbers));
    // Automatically freed
}

See also

fp_alloca() for type-safe version

See also

fp_malloc() for heap allocation

See also

fp_is_stack_allocated() to check allocation type

Warning

Stack allocations have limited size. Use fp_malloc() for large allocations.

Warning

Stack memory is only valid within the current scope. Do not return stack-allocated pointers from functions.

Parameters:
  • type – Element type

  • _size – Number of elements to allocate

Returns:

Typed pointer to allocated stack memory

fp_malloc(type, _size)#

Allocate a typed fat pointer on the heap.

int* data = fp_malloc(int, 100);
for(size_t i = 0; i < fp_length(data); i++) {
    data[i] = rand();
}
fp_free_and_null(data);

Parameters:
  • type – Element type

  • _size – Number of elements to allocate

Returns:

Typed pointer to allocated heap memory

fp_realloc(type, p, _size)#

Reallocate a fat pointer with a new size.

Existing data is preserved up to the minimum of old and new sizes.

float* values = fp_malloc(float, 50);
// ... use values ...
values = fp_realloc(float, values, 100); // Expand to 100 elements
Parameters:
  • type – Element type

  • p – Existing fat pointer

  • _size – New number of elements

Returns:

Reallocated typed pointer (may be different address)

fp_free_and_null(p)#

Free a fat pointer and set it to NULL.

In general this macro is safer than using fp_free directly

int* data = fp_malloc(int, 50);
fp_free_and_null(data);
assert(data == NULL);
Parameters:
  • p – Fat pointer to free

fp_size#

Alias for fp_length()

fp_front(a)#

Get pointer to the first element.

Parameters:
  • a – Fat pointer

Returns:

Pointer to first element

fp_begin(a)#

Alias for fp_front.

fp_back(a)#

Get pointer to the last element.

int* arr = fp_malloc(int, 10);
*fp_front(arr) = 1;   // First element
*fp_back(arr) = 100;  // Last element

Parameters:
  • a – Fat pointer

Returns:

Typed pointer to last element

fp_end(a)#

Get pointer to one past the last element (end iterator)

int* arr = fp_malloc(int, 5);
for(int* p = fp_begin(arr); p != fp_end(arr); ++p) {
    *p = 0;
}

Parameters:
  • a – Fat pointer

Returns:

Pointer to one past the end

fp_iterate_named(a, iter)#

Iterate over fat pointer with named iterator.

int* arr = fp_malloc(int, 10);
fp_iterate_named(arr, ptr) {
    *ptr = ptr - arr; // Set to index
}

Parameters:
  • a – Fat pointer

  • iter – Iterator variable name

fp_iterate(a)#

Iterate over fat pointer (iterator named ‘i’)

float* values = fp_malloc(float, 20);
fp_iterate(values) {
    *i = 3.14f;
}

Parameters:
  • a – Fat pointer

fp_iterate_calculate_index(a, i)#

Calculate index from iterator and base pointer.

int* arr = fp_malloc(int, 100);
fp_iterate_named(arr, ptr) {
    size_t idx = fp_iterate_calculate_index(arr, ptr);
    printf("Index: %zu, Value: %d\n", idx, *ptr);
}

Parameters:
  • a – Base fat pointer

  • i – Iterator pointer

Returns:

Index as size_t

fp_iterate_reverse_named(a, iter)#

Iterate over fat pointer in reverse with named iterator.

int* arr = fp_malloc(int, 5);
int val = 0;
fp_iterate(arr) { *i = val++; }

fp_iterate_reverse(arr, item) {
    printf("%d ", *item); // Prints: 4 3 2 1 0
}

Parameters:
  • a – Fat pointer

  • iter – Iterator variable name

fp_iterate_reverse(a)#

Iterate over fat pointer in reverse (iterator named ‘i’)

int* arr = fp_malloc(int, 5);
int val = 0;
fp_iterate(arr) { *i = val++; }

fp_iterate_reverse(arr) {
    printf("%d ", *i); // Prints: 4 3 2 1 0
}

Parameters:
  • a – Fat pointer

fp_clone(a)#

Create a shallow copy of a fat pointer.

Creates an independent copy of the data. The new pointer must be freed separately.

int* original = fp_malloc(int, 10);
fp_iterate(original) { *i = 42; }

int* copy = fp_clone(original);
copy[0] = 99; // Doesn't affect original

fp_free_and_null(original);
fp_free_and_null(copy);
Parameters:
  • a – Fat pointer to clone

Returns:

New heap-allocated fat pointer with copied data

fp_contains(a, needle)#

Check if a fat pointer contains a specific value.

Uses memcmp for comparison, so works with any type.

int* numbers = fp_malloc(int, 100);
numbers[50] = 42;

assert(fp_contains(numbers, 42));
assert(!fp_contains(numbers, 999));
Parameters:
  • a – Fat pointer to search

  • needle – Value to find

Returns:

true if value found, false otherwise

fp_not_found#

Sentinel value indicating element not found in search operations.

This value is returned by fp_find and fp_rfind when the searched element is not present in the fat pointer. It is set to SIZE_MAX, the maximum value of size_t, which cannot be a valid index.

int* arr = fp_malloc(int, 10);
size_t idx = fp_find(arr, 42);
if(idx == fp_not_found) {
    printf("Value not found\n");
}
fp_find(a, needle)#

Find the index of a specific value in a fat pointer.

Uses memcmp for comparison, so works with any type. Returns fp_not_found (SIZE_MAX) if the element is not present.

int* numbers = fp_malloc(int, 100);
for(size_t i = 0; i < fp_length(numbers); i++) {
    numbers[i] = i * 2;
}
numbers[50] = 999;

size_t idx = fp_find(numbers, 999);
if(idx != fp_not_found) {
    printf("Found at index: %zu\n", idx); // Prints: 50
} else {
    printf("Not found\n");
}

// Not found case
assert(fp_find(numbers, 777) == fp_not_found);
Parameters:
  • a – Fat pointer to search

  • needle – Value to find

Returns:

Index of first occurrence (size_t), or fp_not_found if not found

fp_rfind(a, needle)#

Find the index of a specific value searching from the end (reverse find)

Searches backwards through the fat pointer, returning the index of the last occurrence of the value. Uses memcmp for comparison, so works with any type. Returns fp_not_found (SIZE_MAX) if the element is not present.

int* numbers = fp_malloc(int, 100);
for(size_t i = 0; i < fp_length(numbers); i++) {
    numbers[i] = i % 10;  // 0,1,2,...,9,0,1,2,...
}

// Find last occurrence of 5
size_t first = fp_find(numbers, 5);   // Returns 5
size_t last = fp_rfind(numbers, 5);   // Returns 95

printf("First occurrence: %zu\n", first);
printf("Last occurrence: %zu\n", last);

// Not found case
assert(fp_rfind(numbers, 999) == fp_not_found);
Parameters:
  • a – Fat pointer to search

  • needle – Value to find

Returns:

Index of last occurrence (size_t), or fp_not_found if not found

fp_view(type)#

Define a view type for a specific element type (C version)

fp_view(int) my_view;
fp_view(char) str_view;

Parameters:
  • type – Element type

fp_view_literal(type, data, size)#

Create a view literal (C version)

int buffer[10];
fp_view(int) v = fp_view_literal(int, buffer, 10);

Parameters:
  • type – Element type

  • data – Pointer to data

  • size – Number of elements

Returns:

View structure

fp_void_view_literal(data, size)#

Create a void view literal.

Parameters:
  • data – Pointer to data

  • size – Number of elements

Returns:

Untyped view

fp_view_from_variable(type, var)#

Create a view from a single variable.

int x = 42;
fp_view(int) v = fp_view_from_variable(int, x);
assert(fp_view_size(v) == 1);
assert(*fp_view_data(int, v) == 42);

Parameters:
  • type – Element type

  • var – Variable to create view of

Returns:

View with size 1 pointing to the variable

fp_variable_to_void_view(type, data)#

Convert a variable to a void view.

double value = 3.14;
fp_void_view v = fp_variable_to_void_view(double, value);
assert(fp_view_size(v) == sizeof(double));

Parameters:
  • type – Type of variable

  • data – Variable

Returns:

Void view of the variable’s memory

fp_view_make(type, p, start, length)#

Create a view from a range within a fat pointer.

int* arr = fp_malloc(int, 100);

// Create view of elements 10-29 (20 elements)
fp_view(int) middle = fp_view_make(int, arr, 10, 20);

fp_view_iterate(int, middle) {
    *i = 999;
}

Parameters:
  • type – Element type

  • p – Fat pointer

  • start – Starting index (inclusive)

  • length – Number of elements

Returns:

Typed view of the specified range

fp_view_make_full(type, p)#

Create a view of an entire fat pointer.

float* data = fp_malloc(float, 50);
fp_view(float) full = fp_view_make_full(float, data);
assert(fp_view_size(full) == 50);

Parameters:
  • type – Element type

  • p – Fat pointer

Returns:

View covering all elements

fp_view_make_start_end(type, p, start, end)#

Create a view using start and end indices.

int* arr = fp_malloc(int, 100);
// View elements 20-30 (inclusive)
fp_view(int) slice = fp_view_make_start_end(int, arr, 20, 30);
assert(fp_view_size(slice) == 11);

Parameters:
  • type – Element type

  • p – Fat pointer

  • start – Starting index (inclusive)

  • end – Ending index (inclusive)

Returns:

View of the range [start, end]

fp_view_length(view)#

Get the number of elements in a view.

Parameters:
  • view – View structure

Returns:

Number of elements

fp_view_size(view)#

Alias for fp_view_length.

fp_view_data_void(view)#

Get untyped data pointer from view.

Parameters:
  • view – View structure

Returns:

Void pointer to data

fp_view_data(type, view)#

Get typed data pointer from view.

fp_view(double) v = ...;
double* ptr = fp_view_data(double, v);

Parameters:
  • type – Element type

  • view – View structure

Returns:

Typed pointer to data

fp_view_access(type, view, index)#

Access element in view by index.

Asserts that index is within bounds.

fp_view(int) v = ...;
int* elem = fp_view_access(int, v, 5);
*elem = 42;
Parameters:
  • type – Element type

  • view – View structure

  • index – Index to access

Returns:

Pointer to element at index

fp_view_subscript(type, view, index)#

Alias for fp_view_access.

fp_view_to_byte_view(type, view)#

Convert a typed view to a byte view.

fp_view(int) int_view = ...;
fp_view(uint8_t) bytes = fp_view_to_byte_view(int, int_view);
// bytes.size == int_view.size * sizeof(int)

Parameters:
  • type – Original element type

  • view – View to convert

Returns:

View of bytes (fp_view(uint8_t))

fp_view_front(type, view)#

Get pointer to first element in view.

Parameters:
  • type – Element type

  • view – View structure

Returns:

Pointer to first element

fp_view_begin(type, view)#

Alias for fp_view_front.

fp_view_back(type, view)#

Get pointer to last element in view.

fp_view(int) v = ...;
*fp_view_front(int, v) = 1;
*fp_view_back(int, v) = 100;

Parameters:
  • type – Element type

  • view – View structure

Returns:

Pointer to last element

fp_view_end(type, view)#

Get pointer to one past the last element (end iterator)

Parameters:
  • type – Element type

  • view – View structure

Returns:

Pointer to one past the end

fp_view_subview(type, view, start, length)#

Create a subview from an existing view.

fp_view(int) full = fp_view_make_full(int, arr);
fp_view(int) first_half = fp_view_subview(int, full, 0, fp_view_size(full) / 2);

Parameters:
  • type – Element type

  • view – Parent view

  • start – Starting index

  • length – Number of elements

Returns:

Subview of the specified range

fp_view_subview_start_end(type, view, start, end)#

Create a subview using start and end indices.

Parameters:
  • type – Element type

  • view – Parent view

  • start – Starting index (inclusive)

  • end – Ending index (inclusive)

Returns:

Subview of the range [start, end]

fp_view_iterate_named(type, view, iter)#

Iterate over view with named iterator.

fp_view(float) v = ...;
fp_view_iterate_named(float, v, ptr) {
    *ptr *= 2.0f;
}

Parameters:
  • type – Element type

  • view – View structure

  • iter – Iterator variable name

fp_view_iterate(type, view)#

Iterate over view (iterator named ‘i’)

fp_view(int) v = ...;
fp_view_iterate(int, v) {
    printf("%d ", *i);
}

Parameters:
  • type – Element type

  • view – View structure

fp_view_iterate_calculate_index(type, view, i)#

Calculate index from iterator and view.

fp_view(int) v = ...;
fp_view_iterate_named(int, v, ptr) {
    size_t idx = fp_view_iterate_calculate_index(int, v, ptr);
    printf("Index %zu: %d\n", idx, *ptr);
}

Parameters:
  • type – Element type

  • view – View structure

  • i – Iterator pointer

Returns:

Index as size_t

fp_view_make_dynamic(type, view)#

Create a heap-allocated fat pointer from a view.

This creates an owning copy of the view’s data.

int buffer[100];
fp_view(int) v = fp_view_literal(int, buffer, 100);

// Create owning copy
int* owned = fp_view_make_dynamic(int, v);

// Can modify independently
owned[0] = 999;

fp_free_and_null(owned);
Parameters:
  • type – Element type

  • view – View to copy

Returns:

New heap-allocated fat pointer (must be freed with e.g. fp_free)

struct __fp_view#
#include <pointer.h>

View structure for non-owning references to memory regions (C version)

In C, all views are untyped. Use casting to access typed data.

Dynamic Arrays (C API)#

static inline constexpr uint64_t fp_upper_power_of_two(uint64_t v) noexcept#

Calculate next power of two greater than or equal to v.

Used internally for capacity growth. Ensures efficient memory usage with power-of-two allocations.

assert(fp_upper_power_of_two(5) == 8);
assert(fp_upper_power_of_two(16) == 16);
assert(fp_upper_power_of_two(100) == 128);
Parameters:

v – Input value

Returns:

Next power of two >= v

struct __FatDynamicArrayHeader *__fpda_header_null_ref() noexcept#

Get reference to null dynamic array header.

Returns:

Pointer to static null header

static inline struct __FatDynamicArrayHeader *__fpda_header(const void *da) noexcept#

Get dynamic array header from data pointer.

Parameters:

da – Dynamic array pointer

Returns:

Pointer to header (or null ref if da is NULL)

void *__fpda_malloc(size_t _size) noexcept#

Internal allocation function for dynamic arrays.

Parameters:

_size – Size in bytes to allocate

Returns:

Pointer to allocated data

static inline bool is_fpda(const void *da) noexcept#

Check if pointer is a dynamic array.

fp_dynarray(int) arr = NULL;
assert(!is_fpda(arr));  // NULL is not a dynamic array

fpda_push_back(arr, 42);
assert(is_fpda(arr));   // Now it is

fpda_free_and_null(arr);

Parameters:

da – Pointer to check

Returns:

true if da is a valid dynamic array

void fpda_free(void *da) noexcept#

Free a dynamic array.

fp_dynarray(int) arr = NULL;
fpda_push_back(arr, 10);
fpda_free(arr);
// arr is now invalid - do not use

Parameters:

da – Dynamic array to free

static inline size_t fpda_capacity(const void *da) noexcept#

Get allocated capacity of dynamic array.

fp_dynarray(int) arr = NULL;
fpda_reserve(arr, 50);

assert(fpda_capacity(arr) == 50);
assert(fpda_size(arr) == 0);

// Add 30 elements - no reallocation
for(int i = 0; i < 30; i++)
    fpda_push_back(arr, i);

assert(fpda_capacity(arr) == 50);  // Still has room

fpda_free_and_null(arr);

Parameters:

da – Dynamic array

Returns:

Number of elements that can be stored without reallocation

static inline void *__fpda_maybe_grow(void **da, size_t type_size, size_t new_size, bool update_utilized, bool exact_sizing) noexcept#

Internal function to grow dynamic array if needed.

static inline void *__fpda_maybe_grow_insert(void **da, size_t type_size, size_t new_index, size_t new_size, bool exact_sizing) noexcept#

Internal function for insert with growth.

static inline void *__fpda_delete(void **da, size_t type_size, size_t start, size_t count, bool make_size_match_capacity) noexcept#

Internal function for deletion.

static inline void __fpda_swap_range(void *da, size_t type_size, size_t range1_start, size_t range2_start, size_t count, bool bidirectional) noexcept#

Internal function for swapping ranges.

static inline void *__fpda_pop_front(void *_da, size_t type_size) noexcept#

Internal function for deleting the first element.

static inline void __fpda_clone_to(void **dest, const void *src, size_t type_size, bool shrink_to_fit) noexcept#

Internal function for cloning to destination.

static inline void *__fpda_clone(const void *src, size_t type_size) noexcept#

Internal function for cloning.

static inline void *__fpda_concatenate_view_in_place(void **dest, const fp_void_view src, size_t type_size) noexcept#

Internal function for concatenating view in place.

fp_dynarray(type)#

Define a dynamic array type.

Creates a typed dynamic array pointer. The array starts as NULL and grows automatically as elements are added.

fp_dynarray(int) integers = NULL;
fp_dynarray(float) floats = NULL;
fp_dynarray(MyStruct) structs = NULL;
Parameters:
  • type – Element type

FPDA_DEFAULT_SIZE_BYTES#

Default initial allocation size in bytes (defaults to 16)

When a dynamic array is first used, it allocates at least this many bytes. Can be overridden by defining before including this header.

#define FPDA_DEFAULT_SIZE_BYTES 64
#include "dynarray.h"
FPDA_HEADER_SIZE#

Size of dynamic array header in bytes.

fpda_malloc(type, _size)#

Allocate a dynamic array with initial capacity.

// Allocate with capacity for 100 integers
fp_dynarray(int) arr = fpda_malloc(int, 100);
assert(fpda_capacity(arr) == 100);
assert(fpda_size(arr) == 0);  // Size is 0 until elements added

fpda_free_and_null(arr);

Parameters:
  • type – Element type

  • _size – Initial capacity in elements

Returns:

Dynamic array with specified capacity and size

fpda_free_and_null(da)#

Free a dynamic array and set pointer to NULL.

Safer than fpda_free() as it prevents use-after-free.

fp_dynarray(int) arr = NULL;
fpda_push_back(arr, 42);
fpda_free_and_null(arr);
assert(arr == NULL);
Parameters:
  • da – Dynamic array to free

fpda_length#

Get number of elements in dynamic array.

Alias for fp_length. Returns the count of elements currently in use, not the capacity.

fp_dynarray(int) arr = NULL;
fpda_reserve(arr, 100);
fpda_push_back(arr, 1);
fpda_push_back(arr, 2);

assert(fpda_length(arr) == 2);      // Elements in use
assert(fpda_capacity(arr) == 100);  // Allocated capacity
fpda_size#

Alias for fpda_length.

fpda_empty#

Check if dynamic array is empty.

fp_dynarray(int) arr = NULL;
assert(fpda_empty(arr));

fpda_push_back(arr, 42);
assert(!fpda_empty(arr));

fpda_free_and_null(arr);
fpda_front(a)#

Get pointer to first element.

fpda_begin(a)#

Get iterator to beginning (alias for fpda_front)

fpda_back(a)#

Get pointer to last element.

fpda_end(a)#

Get iterator to one past end.

fpda_iterate_named#

Iterate over dynamic array with named iterator.

fp_dynarray(int) arr = NULL;
for(int i = 0; i < 10; i++)
    fpda_push_back(arr, i);

fpda_iterate_named(arr, ptr)
    *ptr *= 2;

fpda_free_and_null(arr);
fpda_iterate#

Iterate over dynamic array (iterator named ‘i’)

fpda_iterate_calculate_index#

Calculate index from iterator.

fpda_reserve(a, _size)#

Reserve capacity for at least _size elements.

Ensures the array can hold at least _size elements without reallocation. Does not change the size, only the capacity.

fp_dynarray(int) arr = NULL;

// Reserve space for 1000 elements
fpda_reserve(arr, 1000);

// Add elements without reallocation
for(int i = 0; i < 1000; i++)
    fpda_push_back(arr, i);  // No reallocations

fpda_free_and_null(arr);
Parameters:
  • a – Dynamic array

  • _size – Minimum capacity to reserve

fpda_reserve_void_pointer(a, type_size, _size)#

Reserve capacity (void pointer version)

Parameters:
  • a – Dynamic array (void*)

  • type_size – Size of each element

  • _size – Minimum capacity to reserve

fpda_grow_to_size(a, _size)#

Grow array to exact size.

fp_dynarray(int) arr = NULL;
fpda_grow_to_size(arr, 50);

assert(fpda_size(arr) == 50);
assert(fpda_capacity(arr) >= 50);

fpda_free_and_null(arr);

Parameters:
  • a – Dynamic array

  • _size – New size (updates both size and ensures capacity)

fpda_grow(a, _to_add)#

Grow array by adding elements.

Increases size by _to_add. New elements are uninitialized.

fp_dynarray(int) arr = NULL;
fpda_push_back(arr, 1);
fpda_push_back(arr, 2);

fpda_grow(arr, 3);  // Add 3 more elements
assert(fpda_size(arr) == 5);

// Initialize new elements
arr[2] = 3;
arr[3] = 4;
arr[4] = 5;

fpda_free_and_null(arr);
Parameters:
  • a – Dynamic array

  • _to_add – Number of elements to add

fpda_push_back(a, _value)#

Add element to end of array.

Automatically grows array if needed. Amortized O(1) operation.

fp_dynarray(int) numbers = NULL;

for(int i = 0; i < 100; i++)
    fpda_push_back(numbers, i * i);

assert(fpda_size(numbers) == 100);
assert(numbers[50] == 2500);

fpda_free(numbers);
Parameters:
  • a – Dynamic array

  • _value – Value to add

fpda_grow_and_initialize(a, _to_add, _value)#

Grow array and initialize new elements.

fp_dynarray(int) arr = NULL;
fpda_push_back(arr, 1);
fpda_push_back(arr, 2);

// Add 5 more elements, all initialized to 99
fpda_grow_and_initialize(arr, 5, 99);

assert(fpda_size(arr) == 7);
assert(arr[2] == 99);
assert(arr[6] == 99);

fpda_free_and_null(arr);

Parameters:
  • a – Dynamic array

  • _to_add – Number of elements to add

  • _value – Value to initialize new elements with

fpda_grow_to_size_and_initialize(a, _size, _value)#

Grow to size and initialize new elements.

fp_dynarray(float) data = NULL;
fpda_grow_to_size_and_initialize(data, 100, 3.14f);

assert(fpda_size(data) == 100);
for(size_t i = 0; i < fpda_size(data); i++)
    assert(data[i] == 3.14f);

fpda_free_and_null(data);

Parameters:
  • a – Dynamic array

  • _size – Target size

  • _value – Value to initialize new elements with

fpda_pop_back_n(a, _count)#

Remove last n elements.

fp_dynarray(int) arr = NULL;
for(int i = 0; i < 10; i++)
    fpda_push_back(arr, i);

fpda_pop_back_n(arr, 3);  // Remove last 3 elements
assert(fpda_size(arr) == 7);

fpda_free_and_null(arr);

Parameters:
  • a – Dynamic array

  • _count – Number of elements to remove

Returns:

Pointer to first removed element (the element after the new last element/now invalid)

fpda_pop_back(a)#

Remove last element.

fp_dynarray(int) stack = NULL;
fpda_push_back(stack, 10);
fpda_push_back(stack, 20);
fpda_push_back(stack, 30);

fpda_pop_back(stack);
assert(fpda_size(stack) == 2);
assert(stack[fpda_size(stack) - 1] == 20);

fpda_free_and_null(stack);

Parameters:
  • a – Dynamic array

Returns:

Pointer to removed element (now invalid)

fpda_pop_back_to_size(a, size)#

Pop elements until size reaches target.

fp_dynarray(int) arr = NULL;
for(int i = 0; i < 100; i++)
    fpda_push_back(arr, i);

fpda_pop_back_to_size(arr, 50);
assert(fpda_size(arr) == 50);

fpda_free_and_null(arr);

Parameters:
  • a – Dynamic array

  • size – Target size

Returns:

Pointer to first removed element (the element after the new last element/now invalid)

fpda_insert(a, _pos, _val)#

Insert element at position.

Elements after _pos are shifted right. O(n) operation.

fp_dynarray(int) arr = NULL;
fpda_push_back(arr, 1);
fpda_push_back(arr, 2);
fpda_push_back(arr, 4);
fpda_push_back(arr, 5);

// Insert 3 at index 2
fpda_insert(arr, 2, 3);

// arr is now: [1, 2, 3, 4, 5]
assert(fpda_size(arr) == 5);
assert(arr[2] == 3);

fpda_free_and_null(arr);
Parameters:
  • a – Dynamic array

  • _pos – Position to insert at

  • _val – Value to insert

fpda_push_front(a, _val)#

Insert element at front.

fp_dynarray(int) deque = NULL;
fpda_push_front(deque, 3);
fpda_push_front(deque, 2);
fpda_push_front(deque, 1);

// deque is now: [1, 2, 3]
assert(deque[0] == 1);

fpda_free_and_null(deque);

Parameters:
  • a – Dynamic array

  • _val – Value to insert

fpda_insert_uninitialized(a, _pos, _count)#

Insert uninitialized elements.

Useful for bulk insertion where you’ll initialize elements manually.

fp_dynarray(int) arr = NULL;
fpda_push_back(arr, 1);
fpda_push_back(arr, 5);

// Insert 3 uninitialized elements at position 1
int* new_elems = fpda_insert_uninitialized(arr, 1, 3);
// TODO: Would this be 1, 3 or 0, 3?
new_elems[0] = 2;
new_elems[1] = 3;
new_elems[2] = 4;

// arr is now: [1, 2, 3, 4, 5]

fpda_free_and_null(arr);
Parameters:
  • a – Dynamic array

  • _pos – Position to insert at

  • _count – Number of elements to insert

Returns:

Pointer to first new uninitialized element

fpda_delete_range(a, _pos, _count)#

Delete range of elements.

Removes elements and shifts remaining elements left. Does not reduce capacity. O(n) operation.

fp_dynarray(int) arr = NULL;
for(int i = 0; i < 10; i++)
    fpda_push_back(arr, i);
// arr: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

fpda_delete_range(arr, 3, 4);
// arr: [0, 1, 2, 7, 8, 9]

assert(fpda_size(arr) == 6);
assert(arr[3] == 7);

fpda_free_and_null(arr);
Parameters:
  • a – Dynamic array

  • _pos – Starting position

  • _count – Number of elements to delete

Returns:

Modified a.

fpda_delete(a, _pos)#

Delete single element.

fp_dynarray(int) arr = NULL;
for(int i = 0; i < 5; i++)
    fpda_push_back(arr, i * 10);
// arr: [0, 10, 20, 30, 40]

fpda_delete(arr, 2);
// arr: [0, 10, 30, 40]

assert(fpda_size(arr) == 4);

fpda_free_and_null(arr);

Parameters:
  • a – Dynamic array

  • _pos – Position of element to delete

Returns:

Modified a.

fpda_delete_start_end(a, _start, _end)#

Delete range using start and end indices.

fp_dynarray(int) arr = NULL;
for(int i = 0; i < 10; i++)
    fpda_push_back(arr, i);

// Delete elements 3 through 7 (inclusive)
fpda_delete_start_end(arr, 3, 7);
// arr: [0, 1, 2, 8, 9]

assert(fpda_size(arr) == 5);

fpda_free_and_null(arr);

Parameters:
  • a – Dynamic array

  • _start – Starting index (inclusive)

  • _end – Ending index (inclusive)

Returns:

Modified a.

fpda_shrink_delete_range(a, _pos, _count)#

Delete range and shrink capacity to match size.

Like fpda_delete_range but also reallocates to free unused capacity.

fp_dynarray(int) arr = NULL;
fpda_reserve(arr, 100);
for(int i = 0; i < 50; i++)
    fpda_push_back(arr, i);

assert(fpda_capacity(arr) == 100);

fpda_shrink_delete_range(arr, 0, 40);
// Only 10 elements remain
assert(fpda_size(arr) == 10);
assert(fpda_capacity(arr) == 10);  // Capacity shrunk

fpda_free_and_null(arr);
Parameters:
  • a – Dynamic array

  • _pos – Starting position

  • _count – Number of elements to delete

Returns:

Modified a.

fpda_shrink_delete(a, _pos)#

Delete single element and shrink capacity.

Parameters:
  • a – Dynamic array

  • _pos – Position to delete

Returns:

Modified a.

fpda_shrink_delete_start_end(a, _start, _end)#

Delete range and shrink using start/end indices.

Parameters:
  • a – Dynamic array

  • _start – Starting index (inclusive)

  • _end – Ending index (inclusive)

Returns:

Modified a.

fpda_resize(a, _size)#

Resize array to new size.

If new size is larger, capacity grows and new elements are uninitialized. If new size is smaller, elements are deleted and capacity shrinks.

fp_dynarray(int) arr = NULL;
for(int i = 0; i < 10; i++)
    fpda_push_back(arr, i);

// Grow
fpda_resize(arr, 20);
assert(fpda_size(arr) == 20);

// Shrink
fpda_resize(arr, 5);
assert(fpda_size(arr) == 5);

fpda_free_and_null(arr);
Parameters:
  • a – Dynamic array

  • _size – New size

fpda_shrink_to_fit(a)#

Shrink capacity to match current size.

Frees unused capacity. Useful after removing many elements.

fp_dynarray(int) arr = NULL;
fpda_reserve(arr, 1000);
for(int i = 0; i < 100; i++)
    fpda_push_back(arr, i);

assert(fpda_capacity(arr) == 1000);
assert(fpda_size(arr) == 100);

fpda_shrink_to_fit(arr);
assert(fpda_capacity(arr) == 100);  // Freed 900 elements worth of memory

fpda_free_and_null(arr);
Parameters:
  • a – Dynamic array

fpda_swap_range(a, _start1, _start2, _count)#

Swap two ranges of elements.

fp_dynarray(int) arr = NULL;
for(int i = 0; i < 10; i++)
    fpda_push_back(arr, i);
// arr: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

// Swap [0,1,2] with [7,8,9]
fpda_swap_range(arr, 0, 7, 3);
// arr: [7, 8, 9, 3, 4, 5, 6, 0, 1, 2]

fpda_free_and_null(arr);

Parameters:
  • a – Dynamic array

  • _start1 – Start of first range

  • _start2 – Start of second range

  • _count – Number of elements to swap

fpda_swap(a, _pos1, _pos2)#

Swap two elements.

fp_dynarray(int) arr = NULL;
fpda_push_back(arr, 10);
fpda_push_back(arr, 20);
fpda_push_back(arr, 30);

fpda_swap(arr, 0, 2);
// arr: [30, 20, 10]

assert(arr[0] == 30);
assert(arr[2] == 10);

fpda_free_and_null(arr);

Parameters:
  • a – Dynamic array

  • _pos1 – First position

  • _pos2 – Second position

fpda_swap_delete_range(a, _pos, _count)#

Swap range to end and delete.

Efficiently deletes by swapping with end elements instead of shifting. O(1) operation but doesn’t preserve order.

fp_dynarray(int) arr = NULL;
for(int i = 0; i < 10; i++)
    fpda_push_back(arr, i);
// arr: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

// Delete elements at positions 2-3 by swapping with end
fpda_swap_delete_range(arr, 2, 2);
// arr: [0, 1, 8, 9, 4, 5, 6, 7]

assert(fpda_size(arr) == 8);

fpda_free_and_null(arr);
Parameters:
  • a – Dynamic array

  • _pos – Starting position

  • _count – Number of elements

fpda_swap_delete(a, _pos)#

Swap element to end and delete.

Fast O(1) deletion that doesn’t preserve order.

fp_dynarray(int) arr = NULL;
for(int i = 0; i < 5; i++)
    fpda_push_back(arr, i * 10);
// arr: [0, 10, 20, 30, 40]

fpda_swap_delete(arr, 1);
// arr: [0, 40, 20, 30]  (40 moved to position 1)

assert(fpda_size(arr) == 4);
assert(arr[1] == 40);

fpda_free_and_null(arr);
Parameters:
  • a – Dynamic array

  • _pos – Position to delete

fpda_swap_delete_start_end(a, _start, _end)#

Swap range to end and delete using start/end indices.

Parameters:
  • a – Dynamic array

  • _start – Starting index (inclusive)

  • _end – Ending index (inclusive)

fpda_pop_front(a)#

Remove and return first element.

Shifts all elements left. O(n) operation.

fp_dynarray(int) queue = NULL;
fpda_push_back(queue, 1);
fpda_push_back(queue, 2);
fpda_push_back(queue, 3);

fpda_pop_front(queue);
// queue: [2, 3]

assert(fpda_size(queue) == 2);
assert(queue[0] == 2);

fpda_free_and_null(queue);

Note

The final element is just shifted to the end, so the pointer to it is valid as long as the array is.

Parameters:
  • a – Dynamic array

Returns:

Pointer to removed element (now at end of array)

fpda_clone_to(dest, src)#

Clone array to destination.

Copies all elements and capacity from src to dest.

fp_dynarray(int) original = NULL;
for(int i = 0; i < 10; i++)
    fpda_push_back(original, i);

fp_dynarray(int) copy = NULL;
fpda_clone_to(copy, original);

assert(fpda_size(copy) == 10);
assert(copy[5] == 5);

fpda_free_and_null(original);
fpda_free_and_null(copy);
Parameters:
  • dest – Destination array

  • src – Source array

fpda_assign(dest, src)#

Alias for fpda_clone_to.

fpda_clone_to_shrink(dest, src)#

Clone array to destination with minimal capacity.

Like fpda_clone_to but capacity matches size exactly.

Parameters:
  • dest – Destination array

  • src – Source array

fpda_clone(src)#

Create a clone of dynamic array.

fp_dynarray(int) original = NULL;
for(int i = 0; i < 5; i++)
    fpda_push_back(original, i * i);

fp_dynarray(int) clone = fpda_clone(original);

clone[0] = 999;  // Doesn't affect original
assert(original[0] == 0);
assert(clone[0] == 999);

fpda_free_and_null(original);
fpda_free_and_null(clone);

Parameters:
  • src – Source array

Returns:

New array with copied data

fpda_copy(src)#

Alias for fpda_clone.

fpda_clear(a)#

Clear all elements (keeps capacity)

Sets size to 0 but doesn’t free memory.

fp_dynarray(int) arr = NULL;
fpda_reserve(arr, 100);
for(int i = 0; i < 50; i++)
    fpda_push_back(arr, i);

assert(fpda_size(arr) == 50);
assert(fpda_capacity(arr) == 100);

fpda_clear(arr);

assert(fpda_size(arr) == 0);
assert(fpda_capacity(arr) == 100);  // Capacity unchanged

fpda_free_and_null(arr);
Parameters:
  • a – Dynamic array

fpda_concatenate_view_in_place(dest, src)#

Concatenate view to array in place.

fp_dynarray(int) arr = NULL;
fpda_push_back(arr, 1);
fpda_push_back(arr, 2);

int more[] = {3, 4, 5};
fp_view(int) v = fp_view_literal(int, more, 3);

fpda_concatenate_view_in_place(arr, v);
// arr: [1, 2, 3, 4, 5]

assert(fpda_size(arr) == 5);

fpda_free_and_null(arr);

Parameters:
  • dest – Destination array (modified)

  • src – Source view

fpda_concatenate_view(dest, src)#

Concatenate view to array (returns new array)

fp_dynarray(int) arr1 = NULL;
fpda_push_back(arr1, 1);
fpda_push_back(arr1, 2);

int extra[] = {3, 4};
fp_view(int) v = fp_view_literal(int, extra, 2);

fp_dynarray(int) result = fpda_concatenate_view(arr1, v);
// result: [1, 2, 3, 4]
// arr1: [1, 2] (unchanged)

fpda_free_and_null(arr1);
fpda_free_and_null(result);

Parameters:
  • dest – Source array (not modified)

  • src – View to append

Returns:

New array with concatenated data

fpda_concatenate_in_place(dest, src)#

Concatenate two arrays in place.

fp_dynarray(int) arr1 = NULL;
fp_dynarray(int) arr2 = NULL;

fpda_push_back(arr1, 1);
fpda_push_back(arr1, 2);
fpda_push_back(arr2, 3);
fpda_push_back(arr2, 4);

fpda_concatenate_in_place(arr1, arr2);
// arr1: [1, 2, 3, 4]
// arr2: [3, 4] (unchanged)

fpda_free_and_null(arr1);
fpda_free_and_null(arr2);

Parameters:
  • dest – Destination array (modified)

  • src – Source array

fpda_concatenate(dest, src)#

Concatenate two arrays (returns new array)

fp_dynarray(int) arr1 = NULL;
fp_dynarray(int) arr2 = NULL;

fpda_push_back(arr1, 1);
fpda_push_back(arr1, 2);
fpda_push_back(arr2, 3);
fpda_push_back(arr2, 4);

fp_dynarray(int) combined = fpda_concatenate(arr1, arr2);
// combined: [1, 2, 3, 4]
// arr1 and arr2 unchanged

assert(fpda_size(combined) == 4);

fpda_free_and_null(arr1);
fpda_free_and_null(arr2);
fpda_free_and_null(combined);

Parameters:
  • dest – First array (not modified)

  • src – Second array (not modified)

Returns:

New array with concatenated data

struct __FatDynamicArrayHeader#
#include <dynarray.h>

Header structure for dynamic arrays.

Extends the fat pointer header with capacity information. Layout: [__FatDynamicArrayHeader][__FatPointerHeader][user data…][null terminator] ^ returned pointer

Generated Documentation (C++ API)#

constexpr size_t fp::not_found = SIZE_MAX#

Sentinel value indicating element not found in search operations.

This value is returned by fp_find and fp_rfind when the searched element is not present in the fat pointer. It is set to SIZE_MAX, the maximum value of size_t, which cannot be a valid index.

fp::auto_free arr = fp::malloc(int, 10);
size_t idx = arr.find(42);
if(idx == fp::not_found)
    printf("Value not found\n");
template<typename T>
inline void fp::__destroy(view<T> view)#

Internal function used to free all elements in a view.

template<typename T>
inline pointer<T> fp::malloc(size_t count = 1)

Allocate a fat pointer on the heap.

// Allocate single element
fp::pointer<int> single = fp::malloc<int>();
*single = 42;

// Allocate array
fp::pointer<float> arr = fp::malloc<float>(100);
for(size_t i = 0; i < arr.size(); i++)
    arr[i] = i * 1.5f;

// Allocate for complex types
struct Point { int x, y; };
fp::pointer<Point> points = fp::malloc<Point>(50);
points[0] = {10, 20};

single.free();
arr.free();
points.free();

See also

fp_alloc() for a version that dynamically stack allocates.

RAII Allocation#

// Use with RAII for automatic cleanup
fp::raii::pointer<int> auto_arr = fp::malloc<int>(100);
fp::auto_free same_as_above = fp::malloc<int>(100);
// No need to call free() - automatic cleanup
Template Parameters:

T – Element type

Parameters:

count – Number of elements to allocate (default: 1)

Returns:

New fat pointer with allocated memory

template<typename T>
inline pointer<T> fp::realloc(pointer<T> ptr, size_t new_count)#

Reallocate a fat pointer with a new size.

This is equivalent to calling ptr.realloc(new_count).

fp::pointer<int> arr = fp::malloc<int>(10);
arr = fp::realloc(arr, 20);
assert(arr.size() == 20);
arr.free();
Template Parameters:

T – Element type

Parameters:
  • ptr – Pointer to reallocate

  • new_count – New number of elements

Returns:

Reallocated pointer (may have different address)

template<typename T>
inline void fp::free(pointer<T> &ptr, bool nullify = true)#

Free a fat pointer (functional style)

This is a functional-style interface to free memory, equivalent to calling ptr.free(). The pointer is passed by reference and will be nullified by default, preventing use-after-free bugs.

This function provides a familiar interface for developers used to C-style free(), while maintaining the safety guarantees of the C++ wrapper.

// Functional style (like C's free)
fp::pointer<int> arr = fp::malloc<int>(50);
fp::free(arr);  // Equivalent to arr.free()
assert(!arr);   // Pointer is null

// Member function style
fp::pointer<int> arr2 = fp::malloc<int>(50);
arr2.free();    // Same effect

See also

fp::pointer::free() for member function version

See also

fp::raii::pointer for automatic memory management

Template Parameters:

T – Element type

Parameters:
  • ptr – Pointer to free (passed by reference)

  • nullify – If true, sets the pointer to nullptr after freeing (default: true)

template<typename T>
inline void fp::free_and_null(pointer<T> &ptr)#

Free a fat pointer and explicitly set it to null.

Deprecated:

This function is redundant since fp::free() nullifies by default. Use fp::free(ptr) instead, which has the same behavior.

This function frees the allocated memory and sets the pointer to nullptr. It exists for clarity and backwards compatibility, but is functionally identical to calling fp::free(ptr) with default parameters.

The explicit name makes the nullification behavior clear in code, which can improve readability in some contexts.

fp::pointer<int> arr = fp::malloc<int>(50);
arr.fill(42);

// These are exactly equivalent:
fp::free_and_null(arr);  // Explicit name
fp::free(arr);           // Default behavior (preferred)

// Both result in:
assert(arr.data() == nullptr);
assert(!arr);

See also

fp::free() for the preferred interface

See also

fp::pointer::free() for member function version

See also

fp::raii::pointer for automatic memory management

Template Parameters:

T – Element type

Parameters:

ptr – Pointer to free (passed by reference, will be nullified)

template<typename T>
inline void fp::free(const pointer<T> &ptr)#

Free a fat pointer (const version)

This is a functional-style interface for freeing through const references. This overload does not nullify the pointer since the reference is const.

This overload exists primarily for compatibility with const contexts and generic code that may receive const references. However, its use is discouraged as it leaves the pointer in a dangerous dangling state.

// Dangerous usage (avoid if possible)
void unsafe_cleanup(const fp::pointer<int>& arr) {
    // Process data
    for(size_t i = 0; i < arr.size(); i++) {
        process(arr[i]);
    }

    fp::free(arr);  // Calls const version - doesn't nullify
    // arr is now dangling but we can't nullify it
}

fp::pointer<int> data = fp::malloc<int>(10);
unsafe_cleanup(data);
// data still appears non-null but is invalid - DANGEROUS!

Why to Avoid#

void demonstrate_danger() {
    fp::pointer<int> arr = fp::malloc<int>(5);
    arr.fill(42);

    const fp::pointer<int>& const_ref = arr;

    fp::free(const_ref);  // Const version called

    // arr is now invalid but still appears non-null
    if(arr) {  // Returns true - MISLEADING!
        // arr[0] = 99;  // UNDEFINED BEHAVIOR - use after free!
    }
}

See also

free(pointer<T>&, bool) for the safer, non-const version

See also

fp::pointer::free() for member function version

See also

fp::raii::pointer for automatic, safe memory management

Warning

This version does not nullify the pointer, making it dangerous. The pointer becomes invalid but cannot be modified through the const reference.

Warning

Strongly prefer the non-const version or RAII pointers.

Template Parameters:

T – Element type

Parameters:

ptr – Const pointer to free

static inline size_t fp::encode_utf8(uint32_t codepoint, char out[4])#
template<typename T, size_t N>
struct array : public fp::pointer_crtp<T, array<T, N>>#
#include <pointer.hpp>

Fixed-size array (fp::array) with fat pointer interface.

This class provides a stack-allocated array with the same interface as fat pointers. It’s useful when you know the size at compile time and want to avoid heap allocation. The array is automatically destroyed when it goes out of scope.

// Create fixed-size array
fp::array<int, 10> arr;

// Initialize with initializer list
fp::array<float, 5> values = {1.0f, 2.0f, 3.0f, 4.0f, 5.0f};

// Access elements
arr[0] = 42;
arr.fill(99);

// Use like any other fat pointer
for(int& val : arr)
    val *= 2;

// Create views
auto v = arr.view(2, 5);

// Check properties
assert(arr.size() == 10);
assert(arr.stack_allocated());
assert(!arr.heap_allocated());

// No need to free - automatically destroyed

Compile-Time Usage#

constexpr fp::array<int, 5> get_primes() {
    fp::array<int, 5> primes;
    primes[0] = 2;
    primes[1] = 3;
    primes[2] = 5;
    primes[3] = 7;
    primes[4] = 11;
    return primes;
}

constexpr auto primes = get_primes();
static_assert(primes[2] == 5);

Using as Function Parameters#

template<size_t N>
void process(fp::array<int, N>& arr) {
    for(size_t i = 0; i < arr.size(); i++)
        arr[i] *= 2;
}

fp::array<int, 10> data;
data.fill(5);
process(data);
assert(data[0] == 10);

STL Compatibility#

fp::array<int, 20> numbers;

// Use with STL algorithms
std::iota(numbers.begin(), numbers.end(), 1);
std::reverse(numbers.begin(), numbers.end());

// Convert to span
std::span<int> s = numbers.span();

Aggregate Operations#

fp::array<double, 100> data;
data.fill(1.0);

// Use front() and back()
data.front() = 0.0;
data.back() = 100.0;

// Create subviews
auto first_quarter = data.view(0, 25);
auto last_quarter = data.view(75, 25);

first_quarter.fill(2.0);
last_quarter.fill(3.0);
Template Parameters:
  • T – Element type

  • N – Number of elements (compile-time constant)

Unnamed Group

inline T *data()#

Get raw pointer to data.

fp::auto_free arr = fp::malloc<int>(10);
int* raw = arr.data();

// Use with C functions
memset(raw, 0, arr.size() * sizeof(int));

// Pass to C APIs
qsort(arr.data(), arr.size(), sizeof(int), compare_func);

Unnamed Group

inline explicit operator T*()#

Explicit conversion to raw pointer.

Requires explicit cast to prevent accidental implicit conversions.

fp::auto_free arr = fp::malloc<int>(10);

// Explicit cast required
int* ptr = (int*)arr;

// Won't compile (good!):
// int* ptr2 = arr;

Unnamed Group

inline size_t length() const#

Get the number of elements.

fp::auto_free arr = fp::malloc<int>(50);

std::cout << "Array has " << arr.length() << " elements\n";

// Use in loops
for(size_t i = 0; i < arr.length(); i++)
    arr[i] = i;

Unnamed Group

inline T *begin()#

Get iterator to beginning.

fp::auto_free arr = fp::malloc<int>(50);

// Manual iteration
for(int* it = arr.begin(); it != arr.end(); ++it)
    *it = 42;

// Use with STL algorithms
std::iota(arr.begin(), arr.end(), 0);
std::sort(arr.begin(), arr.end());
std::reverse(arr.begin(), arr.end());

// Distance
size_t count = std::distance(arr.begin(), arr.end());
assert(count == arr.size());

Unnamed Group

inline T *end()#

Get iterator to end.

fp::auto_free arr = fp::malloc<int>(100);
arr.fill(1);

// Range-based for loop
for(int& val : arr)
    val *= 2;

// Find operations
auto it = std::find(arr.begin(), arr.end(), 42);
if(it != arr.end())
    std::cout << "Found at index: " << (it - arr.begin()) << "\n";

// Accumulate
int sum = std::accumulate(arr.begin(), arr.end(), 0);

Unnamed Group

inline T &operator*()#

Dereference operator - access first element.

Returns:

Reference to first element

Unnamed Group

inline T *operator->()#

Arrow operator - access first element’s members.

Returns:

Pointer to first element

Unnamed Group

inline T &operator[](size_t i)#

Subscript operator - access element by index.

Note

Bounds checked in debug!

Parameters:

i – Index

Returns:

Reference to element at index

Public Types

using view_t = fp::view<T>#

View type for this pointer.

Public Functions

constexpr array() = default#

Default constructor - elements are default-initialized.

constexpr array(const array&) = default#

Copy constructor.

constexpr array(array&&) = default#

Move constructor.

constexpr array &operator=(const array&) = default#

Copy assignment operator.

constexpr array &operator=(array&&) = default#

Move assignment operator.

inline constexpr array(std::initializer_list<T> init)#

Construct from initializer list.

fp::array<int, 5> arr = {1, 2, 3, 4, 5};
fp::array<std::string, 3> names = {"Alice", "Bob", "Charlie"};

// This would fail assertion:
// fp::array<int, 5> bad = {1, 2, 3};  // Only 3 elements!

Parameters:

init – Initializer list of values (must have exactly N elements)

inline operator array<std::add_const_t<T>, N>() const#

Implicit conversion to const array.

Allows passing non-const arrays to functions expecting const arrays.

void print_array(const fp::array<const int, 5>& arr) {
    for(const auto& val : arr) {
        std::cout << val << " ";
    }
}

fp::array<int, 5> data = {1, 2, 3, 4, 5};
print_array(data);  // Implicit conversion
inline T *release()#

Release ownership of the pointer.

After calling release(), the pointer object no longer owns the memory. The caller becomes responsible for freeing it.

fp::auto_free arr = fp::malloc<int>(10).fill(42);

// Transfer ownership
int* raw = arr.release();
assert(arr.data() == nullptr);

// Now we must manually free
fp_free(raw);
Returns:

The raw pointer (ownership transferred to caller)

inline bool is_fp() const#

Check if this is a valid fat pointer.

int regular[10];
fp::auto_free arr = fp::malloc<int>(10);
fp::pointer<int> null_ptr;

assert(arr.is_fp());
assert(!null_ptr.is_fp());
inline bool stack_allocated() const#

Check if this is stack-allocated.

fp::array<int, 10> stack_arr;
fp::auto_free heap_arr = fp::malloc<int>(10);

assert(stack_arr.stack_allocated());
assert(!heap_arr.stack_allocated());
inline bool heap_allocated() const#

Check if this is heap-allocated.

fp::array<int, 10> stack_arr;
fp::auto_free heap_arr = fp::malloc<int>(10);

assert(!stack_arr.heap_allocated());
assert(heap_arr.heap_allocated());
inline operator bool() const#

Check if pointer is non-null.

fp::auto_free arr = fp::malloc<int>(10);
fp::pointer<int> null_ptr;

if(arr)
    std::cout << "Array is valid\n";

if(!null_ptr)
    std::cout << "Null pointer\n";

// Safe access pattern
if(arr && arr.size() > 0)
    arr[0] = 42;
inline bool empty() const#

Check if the pointer is empty.

fp::auto_free arr1 = fp::malloc<int>(10);
fp::auto_free arr2 = fp::malloc<int>(0);

assert(!arr1.empty());
assert(arr2.empty());

// Safe access pattern
if(!arr1.empty())
    arr1[0] = 42;
inline view_t view(size_t start, size_t length)#

Create a view of a portion of this pointer.

fp::auto_free arr = fp::malloc<int>(100);

// View elements 20-39
auto v = arr.view(20, 20);
v.fill(999);

assert(arr[20] == 999);
assert(arr[39] == 999);
assert(arr[19] != 999);  // Outside view

// Process data in chunks
for(size_t i = 0; i < arr.size(); i += 10) {
    auto chunk = arr.view(i, 10);
    process_chunk(chunk);
}

Parameters:
  • start – Starting index

  • length – Number of elements

Returns:

View over the specified range

inline const view_t view(size_t start, size_t length) const#

Create a view (const version)

inline view_t view_full()#

Create a view of the entire pointer.

fp::auto_free arr = fp::malloc<int>(50);

// Create full view
auto v = arr.view_full();
assert(v.size() == arr.size());

// Useful for passing to functions expecting views
void process_view(fp::view<int> data) {
    for(auto& val : data) val *= 2;
}

process_view(arr.view_full());

Returns:

View over all elements

inline const view_t view_full() const#

Create a full view (const version)

inline view_t full_view()#

Alias for view_full.

fp::auto_free arr = fp::malloc<int>(50);

// Both are equivalent
auto v1 = arr.view_full();
auto v2 = arr.full_view();

assert(v1.size() == v2.size());
inline const view_t full_view() const#

Alias for view_full (const version)

inline view_t view_start_end(size_t start, size_t end)#

Create a view using start and end indices.

fp::auto_free arr = fp::malloc<int>(100);

// View elements 10 through 20 (inclusive) - 11 elements
auto v = arr.view_start_end(10, 20);
assert(v.size() == 11);

// Process quarters
size_t quarter = arr.size() / 4;
auto q1 = arr.view_start_end(0, quarter - 1);
auto q2 = arr.view_start_end(quarter, 2 * quarter - 1);
auto q3 = arr.view_start_end(2 * quarter, 3 * quarter - 1);
auto q4 = arr.view_start_end(3 * quarter, arr.size() - 1);

Parameters:
  • start – Starting index (inclusive)

  • end – Ending index (inclusive)

Returns:

View over the range [start, end]

inline const view_t view_start_end(size_t start, size_t end) const#

Create a view with start/end indices (const version)

inline T &front()#

Get reference to first element.

fp::auto_free arr = fp::malloc<int>(10).fill(5);

arr.front() = 100;
assert(arr[0] == 100);

// Useful for algorithms
int first = arr.front();
int last = arr.back();
inline T &back()#

Get reference to last element.

fp::auto_free arr = fp::malloc<int>(10);
arr.fill(5);

arr.back() = 999;
assert(arr[arr.size() - 1] == 999);

// Access both ends
if(!arr.empty()) {
    arr.front() = 1;
    arr.back() = 100;
}
inline array<T, N> &fill(const T &value = {})#

Fill all elements with a value.

fp::auto_free arr = fp::malloc<int>(100);

// Fill with specific value
arr.fill(42);
assert(arr[0] == 42);
assert(arr[99] == 42);

// Fill with default value (0 for int)
arr.fill();
assert(arr[0] == 0);

// Method chaining
fp::auto_free data = fp::malloc<float>(50).fill(3.14f);

// Fill with custom types
struct Point { int x = 0, y = 0; };
fp::auto_free points = fp::malloc<Point>(10);
points.fill(Point{5, 10});

Parameters:

value – Value to fill with (default-constructed if not provided)

Returns:

Reference to derived class for chaining

Public Members

__FatPointerHeaderTruncated __header = __default_header#

Fat pointer header with metadata.

T raw[N]#

Actual array storage.

Public Static Attributes

static constexpr __FatPointerHeaderTruncated __default_header  = {.magic =FP_STACK_MAGIC_NUMBER,.size =N,}

Default header for stack-allocated arrays.

template<typename T>
struct auto_free : public T#
#include <pointer.hpp>

RAII wrapper that automatically frees the wrapped pointer on destruction.

This class wraps any type with a .free method a bool convertible raw field and construction from a (possibly null) pointer and ensures automatic cleanup when the object goes out of scope. It implements proper move semantics and copy on assignment.

void process() {
    auto_free<fp::pointer<int>> data(fp::malloc<int>(100));
    data[0] = 42;
    // Automatically freed when data goes out of scope
}

// Move semantics work correctly
auto_free<fp::pointer<int>> create() {
    auto_free<fp::pointer<int>> temp(fp::malloc<int>(10));
    return temp; // Moved, not freed
}
Template Parameters:

T – Wrapped pointer type (e.g., fp::pointer<int>)

Public Functions

inline constexpr auto_free()#

Default constructor - initializes to nullptr.

inline constexpr auto_free(std::nullptr_t)#

Construct from nullptr.

inline constexpr auto_free(T *ptr)#

Construct from raw pointer (takes ownership)

inline constexpr auto_free(const T &o)#

Copy constructor - clones the underlying data.

inline constexpr auto_free(T &&o)#

Move constructor - transfers ownership.

inline constexpr auto_free(const auto_free &o)#

Copy constructor from another auto_free - clones the data.

inline constexpr auto_free(auto_free &&o)#

Move constructor from another auto_free - transfers ownership.

inline constexpr auto_free &operator=(const T &o)#

Copy assignment - clones the data, frees old data.

inline constexpr auto_free &operator=(T &&o)#

Move assignment from T - transfers ownership, frees old data.

inline constexpr auto_free &operator=(const auto_free &o)#

Copy assignment from auto_free - clones the data, frees old data.

inline constexpr auto_free &operator=(auto_free &&o)#

Move assignment from auto_free - transfers ownership, frees old data.

inline ~auto_free()#

Destructor - automatically frees the pointer if non-null.

template<>
struct auto_free<string> : public fp::string#

Unnamed Group

inline T *data()#

Get raw pointer to data.

fp::auto_free arr = fp::malloc<int>(10);
int* raw = arr.data();

// Use with C functions
memset(raw, 0, arr.size() * sizeof(int));

// Pass to C APIs
qsort(arr.data(), arr.size(), sizeof(int), compare_func);

Unnamed Group

inline T &operator*()#

Dereference operator.

Returns:

Reference to the pointed-to value

Unnamed Group

inline T &operator*()

Dereference operator - access first element.

Returns:

Reference to first element

Unnamed Group

inline T *operator->()#

Arrow operator for member access.

Returns:

Raw pointer

Unnamed Group

inline T *operator->()

Arrow operator - access first element’s members.

Returns:

Pointer to first element

Unnamed Group

inline explicit operator T*()#

Explicit conversion to raw pointer.

Requires explicit cast to prevent accidental implicit conversions.

fp::auto_free arr = fp::malloc<int>(10);

// Explicit cast required
int* ptr = (int*)arr;

// Won't compile (good!):
// int* ptr2 = arr;

Unnamed Group

inline size_t length() const#

Get the number of elements.

fp::auto_free arr = fp::malloc<int>(50);

std::cout << "Array has " << arr.length() << " elements\n";

// Use in loops
for(size_t i = 0; i < arr.length(); i++)
    arr[i] = i;

Unnamed Group

inline T *begin()#

Get iterator to beginning.

fp::auto_free arr = fp::malloc<int>(50);

// Manual iteration
for(int* it = arr.begin(); it != arr.end(); ++it)
    *it = 42;

// Use with STL algorithms
std::iota(arr.begin(), arr.end(), 0);
std::sort(arr.begin(), arr.end());
std::reverse(arr.begin(), arr.end());

// Distance
size_t count = std::distance(arr.begin(), arr.end());
assert(count == arr.size());

Unnamed Group

inline T *end()#

Get iterator to end.

fp::auto_free arr = fp::malloc<int>(100);
arr.fill(1);

// Range-based for loop
for(int& val : arr)
    val *= 2;

// Find operations
auto it = std::find(arr.begin(), arr.end(), 42);
if(it != arr.end())
    std::cout << "Found at index: " << (it - arr.begin()) << "\n";

// Accumulate
int sum = std::accumulate(arr.begin(), arr.end(), 0);

Unnamed Group

inline T &operator[](size_t i)#

Subscript operator - access element by index.

Note

Bounds checked in debug!

Parameters:

i – Index

Returns:

Reference to element at index

Public Types

using view_t = fp::view<T>#

View type for this pointer.

Public Functions

inline operator dynarray<std::add_const_t<char>>() const#

Implicit conversion to const dynarray.

Allows passing non-const arrays to functions expecting const arrays.

void print_numbers(const fp::dynarray<const int>& arr) {
    for(auto& n : arr)
        std::cout << n << " ";
}

fp::dynarray<int> numbers = {1, 2, 3};
print_numbers(numbers);  // Implicit conversion
numbers.free();
inline operator pointer<std::add_const_t<T>>() const#

Implicit conversion to const pointer.

Allows passing non-const pointers to functions expecting const pointers.

void print_data(fp::pointer<const int> data) {
    for(size_t i = 0; i < data.size(); i++) {
        std::cout << data[i] << " ";
    }
}

fp::auto_release arr = fp::malloc<int>(5).fill(42);
print_data(arr);  // Implicit conversion
inline void free(bool nullify = true)#

Free the allocated memory.

Releases the memory allocated for this fat pointer. By default, the internal pointer is set to nullptr after freeing to prevent use-after-free bugs.

This is the preferred method for freeing memory as it provides safety by default. The pointer object remains valid but will be null after this call.

fp::pointer<int> arr = fp::malloc<int>(20);

// Default: nullifies after free
arr.free();
assert(arr.data() == nullptr);

// Explicit: don't nullify (use with caution)
fp::pointer<int> arr2 = fp::malloc<int>(20);
arr2.free(false);
// arr2.data() is now a dangling pointer - DANGEROUS!

// Better: use default behavior
fp::pointer<int> arr3 = fp::malloc<int>(20);
arr3.free(true);  // Explicit, but default

RAII Dynarray Usage#

// If you don't want to call free() manually, use RAII
{
    fp::raii::pointer<int> auto_arr = fp::malloc<int>(100);
    // or fp::auto_free auto_arr = ...
    // Use auto_arr...
    // Automatically freed when scope exits
}

See also

fp::raii::pointer for automatic memory management

Warning

After calling free(), any views or references to the data become invalid.

Warning

Only call free() on heap-allocated pointers, not stack-allocated ones.

Parameters:

nullify – If true, sets the internal pointer to nullptr after freeing (default: true)

inline void free() const#

Free the allocated memory (const version)

This overload is provided for const pointers and does not nullify the internal pointer. Use with extreme caution as the pointer will be left in a dangling state.

This overload exists primarily for compatibility with const contexts, but its use is discouraged. Consider restructuring your code to avoid freeing through const references.

// Dangerous usage (avoid if possible)
void unsafe_free(const fp::pointer<int>& arr) {
    arr.free();  // Calls const version
    // arr.data() is now dangling but we can't nullify it
}

See also

free(bool) for the safer, non-const version

Warning

This version does not nullify the pointer, making it dangerous. Prefer the non-const version whenever possible.

Warning

The pointer becomes invalid but the const object cannot be modified.

inline void free(bool nullify = true)

Free the dynamic array.

Calls destructors for non-trivial types before freeing memory.

fp::dynarray<std::string> strings;
strings.push_back("Hello");
strings.push_back("World");

strings.free();  // Destructors called, memory freed, ptr nulled
assert(!strings);
Parameters:

nullify – If true, sets pointer to null (default: true)

inline void free() const

Free the dynamic array (const version)

Warning

Does not nullify pointer. Prefer non-const version.

inline pointer &realloc(size_t new_count)#

Reallocate with a new size.

Existing data is preserved up to the minimum of old and new sizes. The pointer address may change, but the reference is updated automatically.

fp::pointer<int> arr = fp::malloc<int>(10);
for(size_t i = 0; i < arr.size(); i++)
    arr[i] = i;

// Expand to 20 elements (first 10 preserved)
arr.realloc(20);
assert(arr.size() == 20);
assert(arr[5] == 5);  // Old data still there

// Shrink to 5 elements
arr.realloc(5);
assert(arr.size() == 5);

arr.free();
Parameters:

new_count – New number of elements

Returns:

Reference to this pointer (may have new address)

inline pointer clone() const#

Create a copy of this pointer.

Creates an independent copy of the data. Modifications to the clone do not affect the original.

fp::auto_free original = fp::malloc<int>(5).fill(42);

fp::pointer<int> copy = original.clone();
copy[0] = 99;  // Doesn't affect original

assert(original[0] == 42);
assert(copy[0] == 99);

copy.free();
Returns:

New pointer with copied data (must be freed separately)

inline Derived clone() const

Create a copy of this array.

fp::raii::dynarray<int> original;
original.push_back(1);
original.push_back(2);
original.push_back(3);

fp::raii::dynarray<int> copy = original.clone();
copy[0] = 999;  // Doesn't affect original

assert(original[0] == 1);
assert(copy[0] == 999);

Returns:

New array with copied data

inline T *data()

Get the raw pointer.

inline const T *data() const#

Get the raw pointer (const version)

inline T *release()#

Release ownership of the pointer.

After calling release(), the pointer object no longer owns the memory. The caller becomes responsible for freeing it.

fp::auto_free arr = fp::malloc<int>(10).fill(42);

// Transfer ownership
int* raw = arr.release();
assert(arr.data() == nullptr);

// Now we must manually free
fp_free(raw);
Returns:

The raw pointer (ownership transferred to caller)

inline bool is_fp() const#

Check if this is a valid fat pointer.

int regular[10];
fp::auto_free arr = fp::malloc<int>(10);
fp::pointer<int> null_ptr;

assert(arr.is_fp());
assert(!null_ptr.is_fp());
inline bool stack_allocated() const#

Check if this is stack-allocated.

fp::array<int, 10> stack_arr;
fp::auto_free heap_arr = fp::malloc<int>(10);

assert(stack_arr.stack_allocated());
assert(!heap_arr.stack_allocated());
inline bool heap_allocated() const#

Check if this is heap-allocated.

fp::array<int, 10> stack_arr;
fp::auto_free heap_arr = fp::malloc<int>(10);

assert(!stack_arr.heap_allocated());
assert(heap_arr.heap_allocated());
inline operator bool() const#

Check if pointer is non-null.

fp::auto_free arr = fp::malloc<int>(10);
fp::pointer<int> null_ptr;

if(arr)
    std::cout << "Array is valid\n";

if(!null_ptr)
    std::cout << "Null pointer\n";

// Safe access pattern
if(arr && arr.size() > 0)
    arr[0] = 42;
inline bool empty() const#

Check if the pointer is empty.

fp::auto_free arr1 = fp::malloc<int>(10);
fp::auto_free arr2 = fp::malloc<int>(0);

assert(!arr1.empty());
assert(arr2.empty());

// Safe access pattern
if(!arr1.empty())
    arr1[0] = 42;
inline view_t view(size_t start, size_t length)#

Create a view of a portion of this pointer.

fp::auto_free arr = fp::malloc<int>(100);

// View elements 20-39
auto v = arr.view(20, 20);
v.fill(999);

assert(arr[20] == 999);
assert(arr[39] == 999);
assert(arr[19] != 999);  // Outside view

// Process data in chunks
for(size_t i = 0; i < arr.size(); i += 10) {
    auto chunk = arr.view(i, 10);
    process_chunk(chunk);
}

Parameters:
  • start – Starting index

  • length – Number of elements

Returns:

View over the specified range

inline const view_t view(size_t start, size_t length) const#

Create a view (const version)

inline view_t view_full()#

Create a view of the entire pointer.

fp::auto_free arr = fp::malloc<int>(50);

// Create full view
auto v = arr.view_full();
assert(v.size() == arr.size());

// Useful for passing to functions expecting views
void process_view(fp::view<int> data) {
    for(auto& val : data) val *= 2;
}

process_view(arr.view_full());

Returns:

View over all elements

inline const view_t view_full() const#

Create a full view (const version)

inline view_t full_view()#

Alias for view_full.

fp::auto_free arr = fp::malloc<int>(50);

// Both are equivalent
auto v1 = arr.view_full();
auto v2 = arr.full_view();

assert(v1.size() == v2.size());
inline const view_t full_view() const#

Alias for view_full (const version)

inline view_t view_start_end(size_t start, size_t end)#

Create a view using start and end indices.

fp::auto_free arr = fp::malloc<int>(100);

// View elements 10 through 20 (inclusive) - 11 elements
auto v = arr.view_start_end(10, 20);
assert(v.size() == 11);

// Process quarters
size_t quarter = arr.size() / 4;
auto q1 = arr.view_start_end(0, quarter - 1);
auto q2 = arr.view_start_end(quarter, 2 * quarter - 1);
auto q3 = arr.view_start_end(2 * quarter, 3 * quarter - 1);
auto q4 = arr.view_start_end(3 * quarter, arr.size() - 1);

Parameters:
  • start – Starting index (inclusive)

  • end – Ending index (inclusive)

Returns:

View over the range [start, end]

inline const view_t view_start_end(size_t start, size_t end) const#

Create a view with start/end indices (const version)

inline T &front()#

Get reference to first element.

fp::auto_free arr = fp::malloc<int>(10).fill(5);

arr.front() = 100;
assert(arr[0] == 100);

// Useful for algorithms
int first = arr.front();
int last = arr.back();
inline T &back()#

Get reference to last element.

fp::auto_free arr = fp::malloc<int>(10);
arr.fill(5);

arr.back() = 999;
assert(arr[arr.size() - 1] == 999);

// Access both ends
if(!arr.empty()) {
    arr.front() = 1;
    arr.back() = 100;
}
inline pointer<T> &fill(const T &value = {})#

Fill all elements with a value.

fp::auto_free arr = fp::malloc<int>(100);

// Fill with specific value
arr.fill(42);
assert(arr[0] == 42);
assert(arr[99] == 42);

// Fill with default value (0 for int)
arr.fill();
assert(arr[0] == 0);

// Method chaining
fp::auto_free data = fp::malloc<float>(50).fill(3.14f);

// Fill with custom types
struct Point { int x = 0, y = 0; };
fp::auto_free points = fp::malloc<Point>(10);
points.fill(Point{5, 10});

Parameters:

value – Value to fill with (default-constructed if not provided)

Returns:

Reference to derived class for chaining

inline bool is_dynarray() const#

Check if this is a valid dynamic array.

fp::raii::dynarray<int> arr;
assert(!arr.is_dynarray());  // Empty/null

arr.push_back(42);
assert(arr.is_dynarray());   // Now valid

Returns:

true if this is a dynamic array

inline size_t capacity() const#

Get allocated capacity.

fp::raii::dynarray<int> arr;
arr.reserve(50);

assert(arr.capacity() == 50);
assert(arr.size() == 0);

arr.push_back(1);
assert(arr.capacity() == 50);  // No reallocation

Returns:

Number of elements that can be stored without reallocation

inline Derived &reserve(size_t size)#

Reserve capacity for at least size elements.

fp::raii::dynarray<int> arr;

// Reserve and chain operations
arr.reserve(1000)
   .push_back(42);
arr.push_back(99);

// No reallocations for next 998 elements

Parameters:

size – Minimum capacity to reserve

Returns:

Reference to this array for chaining

inline Derived &grow(size_t to_add, const T &value = {})#

Grow array by adding initialized elements.

fp::raii::dynarray<int> arr;
arr.push_back(1);
arr.push_back(2);

// Add 5 elements, all initialized to 99
arr.grow(5, 99);

assert(arr.size() == 7);
assert(arr[2] == 99);

Parameters:
  • to_add – Number of elements to add

  • value – Value to initialize new elements with (default-constructed if omitted)

Returns:

Reference to this array for chaining

inline Derived &grow_uninitialized(size_t to_add)#

Grow array with uninitialized elements.

New elements are uninitialized - you must initialize them before use. Useful for performance when you’ll immediately overwrite values.

fp::raii::dynarray<int> arr;
arr.push_back(1);

size_t old_size = arr.size();
arr.grow_uninitialized(10);

// Initialize new elements
for(size_t i = old_size; i < arr.size(); i++)
    arr[i] = i * 10;
Parameters:

to_add – Number of elements to add

Returns:

Reference to this array for chaining

inline Derived &grow_to_size(size_t size, const T &value = {})#

Grow to exact size with initialized elements.

fp::raii::dynarray<float> data;
data.grow_to_size(100, 3.14f);

assert(data.size() == 100);
for(auto& val : data)
    assert(val == 3.14f);

Note

If size is smaller than the current size, this function does nothing!

Parameters:
  • size – Target size

  • value – Value to initialize new elements with (default-constructed if omitted)

Returns:

Reference to this array for chaining

inline Derived &grow_to_size_uninitialized(size_t size)#

Grow to exact size with uninitialized elements.

Parameters:

size – Target size

Returns:

Reference to this array for chaining

inline T &push_back(const T &value)#

Add element to end of array.

fp::raii::dynarray<std::string> names;
names.push_back("Alice");
names.push_back("Bob");

std::string& ref = names.push_back("Charlie");
ref += " Smith";  // Modify in place

assert(names[2] == "Charlie Smith");

Parameters:

value – Value to add

Returns:

Reference to the newly added element

template<typename ...Targs>
inline T &emplace_back(Targs... args)#

Construct element in-place at end of array.

Constructs the element directly in the array without copying/moving.

struct Point {
    int x, y;
    Point(int x_, int y_) : x(x_), y(y_) {}
};

fp::raii::dynarray<Point> points;

// Construct directly in array
points.emplace_back(10, 20);
points.emplace_back(30, 40);

assert(points[0].x == 10);
Template Parameters:

Targs – Constructor argument types

Parameters:

args – Constructor arguments

Returns:

Reference to the newly constructed element

inline T &pop_back_n(size_t count)#

Remove last n elements.

fp::raii::dynarray<int> arr;
for(int i = 0; i < 10; i++) arr.push_back(i);

arr.pop_back_n(3);
assert(arr.size() == 7);

Parameters:

count – Number of elements to remove

Returns:

Reference to first element removed (one after the new last element)

inline T &pop_back()#

Remove last element.

fp::raii::dynarray<int> stack;
stack.push_back(10);
stack.push_back(20);
stack.push_back(30);

stack.pop_back();
assert(stack.size() == 2);
assert(stack.back() == 20);

Returns:

Reference to removed element (now invalid)

inline T &pop_back_to_size(size_t size)#

Pop elements until size reaches target.

fp::raii::dynarray<int> arr;
for(int i = 0; i < 100; i++) arr.push_back(i);

arr.pop_back_to_size(50);
assert(arr.size() == 50);

Parameters:

size – Target size

Returns:

Reference to first element removed (one after the new last element)

inline T &insert(size_t pos, const T &value = {})#

Insert element at position.

Elements after pos are shifted right. O(n) operation.

fp::raii::dynarray<int> arr;
arr.push_back(1);
arr.push_back(2);
arr.push_back(4);

arr.insert(2, 3);
// arr: [1, 2, 3, 4]

assert(arr[2] == 3);
Parameters:
  • pos – Position to insert at

  • value – Value to insert (default-constructed if omitted)

Returns:

Reference to inserted element

template<typename ...Targs>
inline T &emplace(size_t pos, Targs... args)#

Construct element in-place at position.

Elements after pos are shifted right. O(n) operation.

struct Widget {
    int id;
    std::string name;
    Widget(int i, std::string n) : id(i), name(std::move(n)) {}
};

fp::raii::dynarray<Widget> widgets;
widgets.emplace_back(1, "First");
widgets.emplace_back(3, "Third");

widgets.emplace(1, 2, "Second");
// widgets: [(1,"First"), (2,"Second"), (3,"Third")]
Template Parameters:

Targs – Constructor argument types

Parameters:
  • pos – Position to insert at

  • args – Constructor arguments

Returns:

Reference to inserted element

inline T &push_front(const T &value = {})#

Insert element at front.

All current elements are shifted right. O(n) operation.

fp::raii::dynarray<int> deque;
deque.push_front(3);
deque.push_front(2);
deque.push_front(1);

// deque: [1, 2, 3]
assert(deque[0] == 1);
Parameters:

value – Value to insert

Returns:

Reference to inserted element

template<typename ...Targs>
inline T &emplace_front(Targs... args)#

Construct element in-place at front.

All current elements are shifted right. O(n) operation.

fp::raii::dynarray<std::string> list;
list.emplace_front("World");
list.emplace_front("Hello");

// list: ["Hello", "World"]
Template Parameters:

Targs – Constructor argument types

Parameters:

args – Constructor arguments

Returns:

Reference to newly constructed element

inline view<T> insert_uninitialized(size_t pos, size_t count = 1)#

Insert uninitialized elements.

Elements after pos are shifted right. O(n) operation.

fp::dynarray<int> arr;
arr.push_back(1);
arr.push_back(5);

auto view = arr.insert_uninitialized(1, 3);
view[0] = 2;
view[1] = 3;
view[2] = 4;

// arr: [1, 2, 3, 4, 5]
Parameters:
  • pos – Position to insert at

  • count – Number of elements to insert (default: 1)

Returns:

View over the inserted uninitialized elements

inline Derived &delete_range(size_t pos, size_t count)#

Delete range of elements.

Elements after pos + count are shifted left. O(n) operation.

fp::raii::dynarray<int> arr;
for(int i = 0; i < 10; i++) arr.push_back(i);

arr.delete_range(3, 4)
   .push_back(99);

assert(arr.size() == 7);
Parameters:
  • pos – Starting position

  • count – Number of elements to delete

Returns:

Reference to this array for chaining

inline Derived &delete_(size_t pos)#

Delete single element.

Elements after pos are shifted left. O(n) operation.

fp::raii::dynarray<int> arr;
arr.push_back(1).push_back(2).push_back(3);

arr.delete_(1);
// arr: [1, 3]
Parameters:

pos – Position to delete

Returns:

Reference to this array for chaining

inline Derived &remove(size_t pos)#

Alias for delete_.

inline Derived &delete_start_end(size_t start, size_t end)#

Delete range using start and end indices.

Elements after end are shifted left. O(n) operation.

Parameters:
  • start – Starting index (inclusive)

  • end – Ending index (inclusive)

Returns:

Reference to this array for chaining

inline Derived &shrink_delete_range(size_t pos, size_t count)#

Delete range and shrink capacity.

Like delete_range but also reallocates to free unused capacity. All elements are copied. O(n) operation.

Parameters:
  • pos – Starting position

  • count – Number of elements to delete

Returns:

Reference to this array for chaining

inline Derived &shrink_delete(size_t pos)#

Delete element and shrink capacity.

Like delete_ but also reallocates to free unused capacity. All elements are copied. O(n) operation.

Parameters:

pos – Position to delete

Returns:

Reference to this array for chaining

inline Derived &shrink_delete_start_end(size_t start, size_t end)#

Delete range and shrink using start/end indices.

Like delete_start_end but also reallocates to free unused capacity. All elements are copied. O(n) operation.

Parameters:
  • start – Starting index (inclusive)

  • end – Ending index (inclusive)

Returns:

Reference to this array for chaining

inline Derived &resize(size_t size)#

Resize array to new size.

fp::raii::dynarray<int> arr;
for(int i = 0; i < 10; i++) arr.push_back(i);

arr.resize(20);  // Grow
assert(arr.size() == 20);

arr.resize(5);   // Shrink
assert(arr.size() == 5);

Parameters:

size – New size

Returns:

Reference to this array for chaining

inline Derived &shrink_to_fit()#

Shrink capacity to match size.

fp::raii::dynarray<int> arr;
arr.reserve(1000);
for(int i = 0; i < 100; i++) arr.push_back(i);

assert(arr.capacity() == 1000);

arr.shrink_to_fit();
assert(arr.capacity() == 100);

Returns:

Reference to this array for chaining

inline Derived &swap_range(size_t start1, size_t start2, size_t count)#

Swap two ranges of elements.

fp::raii::dynarray<int> arr;
for(int i = 0; i < 10; i++) arr.push_back(i);

arr.swap_range(0, 7, 3);
// Swapped [0,1,2] with [7,8,9]

Parameters:
  • start1 – Start of first range

  • start2 – Start of second range

  • count – Number of elements to swap

Returns:

Reference to this array for chaining

inline Derived &swap(size_t pos1, size_t pos2)#

Swap two elements.

fp::raii::dynarray<int> arr = {10, 20, 30};
arr.swap(0, 2);
// arr: [30, 20, 10]

Parameters:
  • pos1 – First position

  • pos2 – Second position

Returns:

Reference to this array for chaining

inline Derived &swap_delete_range(size_t pos, size_t count)#

Swap range to end and delete.

O(1) deletion that doesn’t preserve order.

fp::dynarray<int> arr;
for(int i = 0; i < 10; i++) arr.push_back(i);

arr.swap_delete_range(2, 2);
// Deleted elements at 2-3 by swapping with end
assert(arr.size() == 8);
Parameters:
  • pos – Starting position

  • count – Number of elements

Returns:

Reference to this array for chaining

inline Derived &swap_delete(size_t pos)#

Swap element to end and delete.

Fast O(1) unordered deletion.

fp::dynarray<int> arr = {0, 10, 20, 30, 40};
arr.swap_delete(1);
// arr: [0, 40, 20, 30]  (40 moved to position 1)
Parameters:

pos – Position to delete

Returns:

Reference to this array for chaining

inline Derived &swap_delete_start_end(size_t start, size_t end)#

Swap range to end and delete using start/end indices.

O(1) deletion that doesn’t preserve order.

Parameters:
  • start – Starting index (inclusive)

  • end – Ending index (inclusive)

Returns:

Reference to this array for chaining

inline T &pop_front()#

Remove and return first element.

Shifts all elements left. O(n) operation.

fp::dynarray<int> queue;
queue.push_back(1).push_back(2).push_back(3);

queue.pop_front();
// queue: [2, 3]
Returns:

Reference to removed element (now invalid)

inline Derived &clear()#

Clear all elements (keeps capacity)

Calls destructors for non-trivial types.

fp::raii::dynarray<std::string> strings;
strings.push_back("Hello");
strings.push_back("World");

assert(strings.size() == 2);

strings.clear();

assert(strings.size() == 0);
assert(strings.capacity() > 0);  // Capacity unchanged
Returns:

Reference to this array for chaining

inline Derived &concatenate_view_in_place(const view<const T> view)#

Concatenate view to this array in place.

fp::raii::dynarray<int> arr;
arr.push_back(1);
arr.push_back(2);

int more[] = {3, 4, 5};
arr.concatenate_view_in_place({more, 3});
// arr: [1, 2, 3, 4, 5]

Parameters:

view – View to append

Returns:

Reference to this array for chaining

inline Derived concatenate_view(const view<const T> view) const#

Concatenate view (returns new array)

fp::raii::dynarray<int> arr;
arr.push_back(1);
arr.push_back(2);

int more[] = {3, 4, 5};
fp::raii::dynarray<int> combined = arr.concatenate_view({more, 3});
// combined: [1, 2, 3, 4, 5]

Parameters:

view – View to append

Returns:

New array with concatenated data

inline Derived &concatenate_in_place(const Derived &other)#

Concatenate other array to this one in place.

fp::raii::dynarray<int> arr1, arr2;
arr1.push_back(1);
arr1.push_back(2);
arr2.push_back(3);
arr2.push_back(4);

arr1.concatenate_in_place(arr2);
// arr1: [1, 2, 3, 4]
// arr2: [3, 4] (unchanged)

Parameters:

other – Array to append

Returns:

Reference to this array for chaining

inline Derived concatenate(const Derived &other) const#

Concatenate two arrays (returns new array)

fp::dynarray<int> arr1, arr2;
arr1.push_back(1);
arr1.push_back(2);
arr2.push_back(3);
arr2.push_back(4);

fp::dynarray<int> combined = arr1.concatenate(arr2);
// combined: [1, 2, 3, 4]
// arr1 and arr2 unchanged

Parameters:

other – Array to append

Returns:

New array with concatenated data

Public Members

T *raw#

Raw pointer to data.

template<typename T>
struct dynarray : public fp::pointer<T>, public fp::dynarray_crtp<T, dynarray<T>>#
#include <dynarray.hpp>

Dynamic array with manual memory management.

fp::dynarray is a std::vector-like container built on fat pointers. Provides automatic growth, efficient insertion/deletion, and full iterator support. Requires manual free() call or use fp::raii::dynarray for automatic cleanup.

Features:

  • Automatic capacity management with amortized O(1) push

  • Method chaining for fluent interface

  • Perfect forwarding with emplace operations

  • Automatic destructor calls for non-trivial types

  • STL-compatible interface

Basic Dynarray Usage#

fp::dynarray<int> numbers;

// Add elements
numbers.push_back(10);
numbers.push_back(20);
numbers.push_back(30);

// Method chaining
numbers.reserve(100)
       .grow(5, 42)
       .push_back(99);

// Iteration
for(auto& n : numbers)
    std::cout << n << " ";

// Manual cleanup
numbers.free();

Initializer List#

fp::dynarray<int> numbers = {1, 2, 3, 4, 5};
assert(numbers.size() == 5);
assert(numbers[2] == 3);

fp::dynarray<std::string> names = {"Alice", "Bob", "Charlie"};
assert(names.size() == 3);

numbers.free();
names.free();

Complex Types in a Dynarray#

struct Person {
    std::string name;
    int age;
    Person(std::string n, int a) : name(std::move(n)), age(a) {}
};

fp::dynarray<Person> people;

// In-place construction (no copies)
people.emplace_back("Alice", 30);
people.emplace_back("Bob", 25);
people.emplace(1, "Charlie", 35);  // Insert at position 1

// people: [Alice(30), Charlie(35), Bob(25)]

people.free();  // Destructors called automatically

Common Dynarray Operations#

fp::dynarray<int> data;
for(int i = 0; i < 100; i++)
    data.push_back(i);

// Delete range
data.delete_range(10, 20);

// Fast unordered delete (O(1))
data.swap_delete(5);

// Insert multiple elements
auto view = data.insert_uninitialized(0, 5);
for(size_t i = 0; i < view.size(); i++)
    view[i] = -1;

// Clone and concatenate
fp::dynarray<int> copy = data.clone();
data.concatenate_in_place(copy);

data.free();
copy.free();

STL Compatibility#

fp::dynarray<int> arr = {5, 2, 8, 1, 9};

// Use STL algorithms
std::sort(arr.begin(), arr.end());
std::reverse(arr.begin(), arr.end());

// Find element
auto it = std::find(arr.begin(), arr.end(), 5);
if(it != arr.end())
    std::cout << "Found at position: " << (it - arr.begin()) << "\n";

// Use with span (C++20)
std::span<int> s = arr.span();

arr.free();
Template Parameters:

T – Element type

Unnamed Group

inline T *data()#

Get raw pointer to data.

fp::auto_free arr = fp::malloc<int>(10);
int* raw = arr.data();

// Use with C functions
memset(raw, 0, arr.size() * sizeof(int));

// Pass to C APIs
qsort(arr.data(), arr.size(), sizeof(int), compare_func);

Unnamed Group

inline T &operator*()#

Dereference operator.

Returns:

Reference to the pointed-to value

Unnamed Group

inline T &operator*()

Dereference operator - access first element.

Returns:

Reference to first element

Unnamed Group

inline T *operator->()#

Arrow operator for member access.

Returns:

Raw pointer

Unnamed Group

inline T *operator->()

Arrow operator - access first element’s members.

Returns:

Pointer to first element

Unnamed Group

inline explicit operator T*()#

Explicit conversion to raw pointer.

Requires explicit cast to prevent accidental implicit conversions.

fp::auto_free arr = fp::malloc<int>(10);

// Explicit cast required
int* ptr = (int*)arr;

// Won't compile (good!):
// int* ptr2 = arr;

Unnamed Group

inline size_t length() const#

Get the number of elements.

fp::auto_free arr = fp::malloc<int>(50);

std::cout << "Array has " << arr.length() << " elements\n";

// Use in loops
for(size_t i = 0; i < arr.length(); i++)
    arr[i] = i;

Unnamed Group

inline T *begin()#

Get iterator to beginning.

fp::auto_free arr = fp::malloc<int>(50);

// Manual iteration
for(int* it = arr.begin(); it != arr.end(); ++it)
    *it = 42;

// Use with STL algorithms
std::iota(arr.begin(), arr.end(), 0);
std::sort(arr.begin(), arr.end());
std::reverse(arr.begin(), arr.end());

// Distance
size_t count = std::distance(arr.begin(), arr.end());
assert(count == arr.size());

Unnamed Group

inline T *end()#

Get iterator to end.

fp::auto_free arr = fp::malloc<int>(100);
arr.fill(1);

// Range-based for loop
for(int& val : arr)
    val *= 2;

// Find operations
auto it = std::find(arr.begin(), arr.end(), 42);
if(it != arr.end())
    std::cout << "Found at index: " << (it - arr.begin()) << "\n";

// Accumulate
int sum = std::accumulate(arr.begin(), arr.end(), 0);

Unnamed Group

inline T &operator[](size_t i)#

Subscript operator - access element by index.

Note

Bounds checked in debug!

Parameters:

i – Index

Returns:

Reference to element at index

Public Types

using view_t = fp::view<T>#

View type for this pointer.

Public Functions

inline dynarray(std::initializer_list<T> init)#

Construct from initializer list.

fp::dynarray<int> numbers = {1, 2, 3, 4, 5};
fp::dynarray<double> values = {3.14, 2.71, 1.41};
fp::dynarray<std::string> names = {"Alice", "Bob", "Charlie"};

assert(numbers.size() == 5);
assert(values.size() == 3);
assert(names[1] == "Bob");

numbers.free();
values.free();
names.free();

Parameters:

init – Initializer list of values

inline operator dynarray<std::add_const_t<T>>() const#

Implicit conversion to const dynarray.

Allows passing non-const arrays to functions expecting const arrays.

void print_numbers(const fp::dynarray<const int>& arr) {
    for(auto& n : arr)
        std::cout << n << " ";
}

fp::dynarray<int> numbers = {1, 2, 3};
print_numbers(numbers);  // Implicit conversion
numbers.free();
inline operator pointer<std::add_const_t<T>>() const#

Implicit conversion to const pointer.

Allows passing non-const pointers to functions expecting const pointers.

void print_data(fp::pointer<const int> data) {
    for(size_t i = 0; i < data.size(); i++) {
        std::cout << data[i] << " ";
    }
}

fp::auto_release arr = fp::malloc<int>(5).fill(42);
print_data(arr);  // Implicit conversion
inline void free(bool nullify = true)#

Free the allocated memory.

Releases the memory allocated for this fat pointer. By default, the internal pointer is set to nullptr after freeing to prevent use-after-free bugs.

This is the preferred method for freeing memory as it provides safety by default. The pointer object remains valid but will be null after this call.

fp::pointer<int> arr = fp::malloc<int>(20);

// Default: nullifies after free
arr.free();
assert(arr.data() == nullptr);

// Explicit: don't nullify (use with caution)
fp::pointer<int> arr2 = fp::malloc<int>(20);
arr2.free(false);
// arr2.data() is now a dangling pointer - DANGEROUS!

// Better: use default behavior
fp::pointer<int> arr3 = fp::malloc<int>(20);
arr3.free(true);  // Explicit, but default

RAII Dynarray Usage#

// If you don't want to call free() manually, use RAII
{
    fp::raii::pointer<int> auto_arr = fp::malloc<int>(100);
    // or fp::auto_free auto_arr = ...
    // Use auto_arr...
    // Automatically freed when scope exits
}

See also

fp::raii::pointer for automatic memory management

Warning

After calling free(), any views or references to the data become invalid.

Warning

Only call free() on heap-allocated pointers, not stack-allocated ones.

Parameters:

nullify – If true, sets the internal pointer to nullptr after freeing (default: true)

inline void free() const#

Free the allocated memory (const version)

This overload is provided for const pointers and does not nullify the internal pointer. Use with extreme caution as the pointer will be left in a dangling state.

This overload exists primarily for compatibility with const contexts, but its use is discouraged. Consider restructuring your code to avoid freeing through const references.

// Dangerous usage (avoid if possible)
void unsafe_free(const fp::pointer<int>& arr) {
    arr.free();  // Calls const version
    // arr.data() is now dangling but we can't nullify it
}

See also

free(bool) for the safer, non-const version

Warning

This version does not nullify the pointer, making it dangerous. Prefer the non-const version whenever possible.

Warning

The pointer becomes invalid but the const object cannot be modified.

inline pointer &realloc(size_t new_count)#

Reallocate with a new size.

Existing data is preserved up to the minimum of old and new sizes. The pointer address may change, but the reference is updated automatically.

fp::pointer<int> arr = fp::malloc<int>(10);
for(size_t i = 0; i < arr.size(); i++)
    arr[i] = i;

// Expand to 20 elements (first 10 preserved)
arr.realloc(20);
assert(arr.size() == 20);
assert(arr[5] == 5);  // Old data still there

// Shrink to 5 elements
arr.realloc(5);
assert(arr.size() == 5);

arr.free();
Parameters:

new_count – New number of elements

Returns:

Reference to this pointer (may have new address)

inline pointer clone() const#

Create a copy of this pointer.

Creates an independent copy of the data. Modifications to the clone do not affect the original.

fp::auto_free original = fp::malloc<int>(5).fill(42);

fp::pointer<int> copy = original.clone();
copy[0] = 99;  // Doesn't affect original

assert(original[0] == 42);
assert(copy[0] == 99);

copy.free();
Returns:

New pointer with copied data (must be freed separately)

inline T *data()

Get the raw pointer.

inline const T *data() const#

Get the raw pointer (const version)

inline T *release()#

Release ownership of the pointer.

After calling release(), the pointer object no longer owns the memory. The caller becomes responsible for freeing it.

fp::auto_free arr = fp::malloc<int>(10).fill(42);

// Transfer ownership
int* raw = arr.release();
assert(arr.data() == nullptr);

// Now we must manually free
fp_free(raw);
Returns:

The raw pointer (ownership transferred to caller)

inline bool is_fp() const#

Check if this is a valid fat pointer.

int regular[10];
fp::auto_free arr = fp::malloc<int>(10);
fp::pointer<int> null_ptr;

assert(arr.is_fp());
assert(!null_ptr.is_fp());
inline bool stack_allocated() const#

Check if this is stack-allocated.

fp::array<int, 10> stack_arr;
fp::auto_free heap_arr = fp::malloc<int>(10);

assert(stack_arr.stack_allocated());
assert(!heap_arr.stack_allocated());
inline bool heap_allocated() const#

Check if this is heap-allocated.

fp::array<int, 10> stack_arr;
fp::auto_free heap_arr = fp::malloc<int>(10);

assert(!stack_arr.heap_allocated());
assert(heap_arr.heap_allocated());
inline operator bool() const#

Check if pointer is non-null.

fp::auto_free arr = fp::malloc<int>(10);
fp::pointer<int> null_ptr;

if(arr)
    std::cout << "Array is valid\n";

if(!null_ptr)
    std::cout << "Null pointer\n";

// Safe access pattern
if(arr && arr.size() > 0)
    arr[0] = 42;
inline bool empty() const#

Check if the pointer is empty.

fp::auto_free arr1 = fp::malloc<int>(10);
fp::auto_free arr2 = fp::malloc<int>(0);

assert(!arr1.empty());
assert(arr2.empty());

// Safe access pattern
if(!arr1.empty())
    arr1[0] = 42;
inline view_t view(size_t start, size_t length)#

Create a view of a portion of this pointer.

fp::auto_free arr = fp::malloc<int>(100);

// View elements 20-39
auto v = arr.view(20, 20);
v.fill(999);

assert(arr[20] == 999);
assert(arr[39] == 999);
assert(arr[19] != 999);  // Outside view

// Process data in chunks
for(size_t i = 0; i < arr.size(); i += 10) {
    auto chunk = arr.view(i, 10);
    process_chunk(chunk);
}

Parameters:
  • start – Starting index

  • length – Number of elements

Returns:

View over the specified range

inline const view_t view(size_t start, size_t length) const#

Create a view (const version)

inline view_t view_full()#

Create a view of the entire pointer.

fp::auto_free arr = fp::malloc<int>(50);

// Create full view
auto v = arr.view_full();
assert(v.size() == arr.size());

// Useful for passing to functions expecting views
void process_view(fp::view<int> data) {
    for(auto& val : data) val *= 2;
}

process_view(arr.view_full());

Returns:

View over all elements

inline const view_t view_full() const#

Create a full view (const version)

inline view_t full_view()#

Alias for view_full.

fp::auto_free arr = fp::malloc<int>(50);

// Both are equivalent
auto v1 = arr.view_full();
auto v2 = arr.full_view();

assert(v1.size() == v2.size());
inline const view_t full_view() const#

Alias for view_full (const version)

inline view_t view_start_end(size_t start, size_t end)#

Create a view using start and end indices.

fp::auto_free arr = fp::malloc<int>(100);

// View elements 10 through 20 (inclusive) - 11 elements
auto v = arr.view_start_end(10, 20);
assert(v.size() == 11);

// Process quarters
size_t quarter = arr.size() / 4;
auto q1 = arr.view_start_end(0, quarter - 1);
auto q2 = arr.view_start_end(quarter, 2 * quarter - 1);
auto q3 = arr.view_start_end(2 * quarter, 3 * quarter - 1);
auto q4 = arr.view_start_end(3 * quarter, arr.size() - 1);

Parameters:
  • start – Starting index (inclusive)

  • end – Ending index (inclusive)

Returns:

View over the range [start, end]

inline const view_t view_start_end(size_t start, size_t end) const#

Create a view with start/end indices (const version)

inline T &front()#

Get reference to first element.

fp::auto_free arr = fp::malloc<int>(10).fill(5);

arr.front() = 100;
assert(arr[0] == 100);

// Useful for algorithms
int first = arr.front();
int last = arr.back();
inline T &back()#

Get reference to last element.

fp::auto_free arr = fp::malloc<int>(10);
arr.fill(5);

arr.back() = 999;
assert(arr[arr.size() - 1] == 999);

// Access both ends
if(!arr.empty()) {
    arr.front() = 1;
    arr.back() = 100;
}
inline pointer<T> &fill(const T &value = {})#

Fill all elements with a value.

fp::auto_free arr = fp::malloc<int>(100);

// Fill with specific value
arr.fill(42);
assert(arr[0] == 42);
assert(arr[99] == 42);

// Fill with default value (0 for int)
arr.fill();
assert(arr[0] == 0);

// Method chaining
fp::auto_free data = fp::malloc<float>(50).fill(3.14f);

// Fill with custom types
struct Point { int x = 0, y = 0; };
fp::auto_free points = fp::malloc<Point>(10);
points.fill(Point{5, 10});

Parameters:

value – Value to fill with (default-constructed if not provided)

Returns:

Reference to derived class for chaining

inline bool is_dynarray() const#

Check if this is a valid dynamic array.

fp::raii::dynarray<int> arr;
assert(!arr.is_dynarray());  // Empty/null

arr.push_back(42);
assert(arr.is_dynarray());   // Now valid

Returns:

true if this is a dynamic array

inline size_t capacity() const#

Get allocated capacity.

fp::raii::dynarray<int> arr;
arr.reserve(50);

assert(arr.capacity() == 50);
assert(arr.size() == 0);

arr.push_back(1);
assert(arr.capacity() == 50);  // No reallocation

Returns:

Number of elements that can be stored without reallocation

inline void free(bool nullify = true)

Free the dynamic array.

Calls destructors for non-trivial types before freeing memory.

fp::dynarray<std::string> strings;
strings.push_back("Hello");
strings.push_back("World");

strings.free();  // Destructors called, memory freed, ptr nulled
assert(!strings);
Parameters:

nullify – If true, sets pointer to null (default: true)

inline void free() const

Free the dynamic array (const version)

Warning

Does not nullify pointer. Prefer non-const version.

inline dynarray<T> &reserve(size_t size)#

Reserve capacity for at least size elements.

fp::raii::dynarray<int> arr;

// Reserve and chain operations
arr.reserve(1000)
   .push_back(42);
arr.push_back(99);

// No reallocations for next 998 elements

Parameters:

size – Minimum capacity to reserve

Returns:

Reference to this array for chaining

inline dynarray<T> &grow(size_t to_add, const T &value = {})#

Grow array by adding initialized elements.

fp::raii::dynarray<int> arr;
arr.push_back(1);
arr.push_back(2);

// Add 5 elements, all initialized to 99
arr.grow(5, 99);

assert(arr.size() == 7);
assert(arr[2] == 99);

Parameters:
  • to_add – Number of elements to add

  • value – Value to initialize new elements with (default-constructed if omitted)

Returns:

Reference to this array for chaining

inline dynarray<T> &grow_uninitialized(size_t to_add)#

Grow array with uninitialized elements.

New elements are uninitialized - you must initialize them before use. Useful for performance when you’ll immediately overwrite values.

fp::raii::dynarray<int> arr;
arr.push_back(1);

size_t old_size = arr.size();
arr.grow_uninitialized(10);

// Initialize new elements
for(size_t i = old_size; i < arr.size(); i++)
    arr[i] = i * 10;
Parameters:

to_add – Number of elements to add

Returns:

Reference to this array for chaining

inline dynarray<T> &grow_to_size(size_t size, const T &value = {})#

Grow to exact size with initialized elements.

fp::raii::dynarray<float> data;
data.grow_to_size(100, 3.14f);

assert(data.size() == 100);
for(auto& val : data)
    assert(val == 3.14f);

Note

If size is smaller than the current size, this function does nothing!

Parameters:
  • size – Target size

  • value – Value to initialize new elements with (default-constructed if omitted)

Returns:

Reference to this array for chaining

inline dynarray<T> &grow_to_size_uninitialized(size_t size)#

Grow to exact size with uninitialized elements.

Parameters:

size – Target size

Returns:

Reference to this array for chaining

inline T &push_back(const T &value)#

Add element to end of array.

fp::raii::dynarray<std::string> names;
names.push_back("Alice");
names.push_back("Bob");

std::string& ref = names.push_back("Charlie");
ref += " Smith";  // Modify in place

assert(names[2] == "Charlie Smith");

Parameters:

value – Value to add

Returns:

Reference to the newly added element

inline T &emplace_back(Targs... args)#

Construct element in-place at end of array.

Constructs the element directly in the array without copying/moving.

struct Point {
    int x, y;
    Point(int x_, int y_) : x(x_), y(y_) {}
};

fp::raii::dynarray<Point> points;

// Construct directly in array
points.emplace_back(10, 20);
points.emplace_back(30, 40);

assert(points[0].x == 10);
Template Parameters:

Targs – Constructor argument types

Parameters:

args – Constructor arguments

Returns:

Reference to the newly constructed element

inline T &pop_back_n(size_t count)#

Remove last n elements.

fp::raii::dynarray<int> arr;
for(int i = 0; i < 10; i++) arr.push_back(i);

arr.pop_back_n(3);
assert(arr.size() == 7);

Parameters:

count – Number of elements to remove

Returns:

Reference to first element removed (one after the new last element)

inline T &pop_back()#

Remove last element.

fp::raii::dynarray<int> stack;
stack.push_back(10);
stack.push_back(20);
stack.push_back(30);

stack.pop_back();
assert(stack.size() == 2);
assert(stack.back() == 20);

Returns:

Reference to removed element (now invalid)

inline T &pop_back_to_size(size_t size)#

Pop elements until size reaches target.

fp::raii::dynarray<int> arr;
for(int i = 0; i < 100; i++) arr.push_back(i);

arr.pop_back_to_size(50);
assert(arr.size() == 50);

Parameters:

size – Target size

Returns:

Reference to first element removed (one after the new last element)

inline T &insert(size_t pos, const T &value = {})#

Insert element at position.

Elements after pos are shifted right. O(n) operation.

fp::raii::dynarray<int> arr;
arr.push_back(1);
arr.push_back(2);
arr.push_back(4);

arr.insert(2, 3);
// arr: [1, 2, 3, 4]

assert(arr[2] == 3);
Parameters:
  • pos – Position to insert at

  • value – Value to insert (default-constructed if omitted)

Returns:

Reference to inserted element

inline T &emplace(size_t pos, Targs... args)#

Construct element in-place at position.

Elements after pos are shifted right. O(n) operation.

struct Widget {
    int id;
    std::string name;
    Widget(int i, std::string n) : id(i), name(std::move(n)) {}
};

fp::raii::dynarray<Widget> widgets;
widgets.emplace_back(1, "First");
widgets.emplace_back(3, "Third");

widgets.emplace(1, 2, "Second");
// widgets: [(1,"First"), (2,"Second"), (3,"Third")]
Template Parameters:

Targs – Constructor argument types

Parameters:
  • pos – Position to insert at

  • args – Constructor arguments

Returns:

Reference to inserted element

inline T &push_front(const T &value = {})#

Insert element at front.

All current elements are shifted right. O(n) operation.

fp::raii::dynarray<int> deque;
deque.push_front(3);
deque.push_front(2);
deque.push_front(1);

// deque: [1, 2, 3]
assert(deque[0] == 1);
Parameters:

value – Value to insert

Returns:

Reference to inserted element

inline T &emplace_front(Targs... args)#

Construct element in-place at front.

All current elements are shifted right. O(n) operation.

fp::raii::dynarray<std::string> list;
list.emplace_front("World");
list.emplace_front("Hello");

// list: ["Hello", "World"]
Template Parameters:

Targs – Constructor argument types

Parameters:

args – Constructor arguments

Returns:

Reference to newly constructed element

inline view<T> insert_uninitialized(size_t pos, size_t count = 1)#

Insert uninitialized elements.

Elements after pos are shifted right. O(n) operation.

fp::dynarray<int> arr;
arr.push_back(1);
arr.push_back(5);

auto view = arr.insert_uninitialized(1, 3);
view[0] = 2;
view[1] = 3;
view[2] = 4;

// arr: [1, 2, 3, 4, 5]
Parameters:
  • pos – Position to insert at

  • count – Number of elements to insert (default: 1)

Returns:

View over the inserted uninitialized elements

inline dynarray<T> &delete_range(size_t pos, size_t count)#

Delete range of elements.

Elements after pos + count are shifted left. O(n) operation.

fp::raii::dynarray<int> arr;
for(int i = 0; i < 10; i++) arr.push_back(i);

arr.delete_range(3, 4)
   .push_back(99);

assert(arr.size() == 7);
Parameters:
  • pos – Starting position

  • count – Number of elements to delete

Returns:

Reference to this array for chaining

inline dynarray<T> &delete_(size_t pos)#

Delete single element.

Elements after pos are shifted left. O(n) operation.

fp::raii::dynarray<int> arr;
arr.push_back(1).push_back(2).push_back(3);

arr.delete_(1);
// arr: [1, 3]
Parameters:

pos – Position to delete

Returns:

Reference to this array for chaining

inline dynarray<T> &remove(size_t pos)#

Alias for delete_.

inline dynarray<T> &delete_start_end(size_t start, size_t end)#

Delete range using start and end indices.

Elements after end are shifted left. O(n) operation.

Parameters:
  • start – Starting index (inclusive)

  • end – Ending index (inclusive)

Returns:

Reference to this array for chaining

inline dynarray<T> &shrink_delete_range(size_t pos, size_t count)#

Delete range and shrink capacity.

Like delete_range but also reallocates to free unused capacity. All elements are copied. O(n) operation.

Parameters:
  • pos – Starting position

  • count – Number of elements to delete

Returns:

Reference to this array for chaining

inline dynarray<T> &shrink_delete(size_t pos)#

Delete element and shrink capacity.

Like delete_ but also reallocates to free unused capacity. All elements are copied. O(n) operation.

Parameters:

pos – Position to delete

Returns:

Reference to this array for chaining

inline dynarray<T> &shrink_delete_start_end(size_t start, size_t end)#

Delete range and shrink using start/end indices.

Like delete_start_end but also reallocates to free unused capacity. All elements are copied. O(n) operation.

Parameters:
  • start – Starting index (inclusive)

  • end – Ending index (inclusive)

Returns:

Reference to this array for chaining

inline dynarray<T> &resize(size_t size)#

Resize array to new size.

fp::raii::dynarray<int> arr;
for(int i = 0; i < 10; i++) arr.push_back(i);

arr.resize(20);  // Grow
assert(arr.size() == 20);

arr.resize(5);   // Shrink
assert(arr.size() == 5);

Parameters:

size – New size

Returns:

Reference to this array for chaining

inline dynarray<T> &shrink_to_fit()#

Shrink capacity to match size.

fp::raii::dynarray<int> arr;
arr.reserve(1000);
for(int i = 0; i < 100; i++) arr.push_back(i);

assert(arr.capacity() == 1000);

arr.shrink_to_fit();
assert(arr.capacity() == 100);

Returns:

Reference to this array for chaining

inline dynarray<T> &swap_range(size_t start1, size_t start2, size_t count)#

Swap two ranges of elements.

fp::raii::dynarray<int> arr;
for(int i = 0; i < 10; i++) arr.push_back(i);

arr.swap_range(0, 7, 3);
// Swapped [0,1,2] with [7,8,9]

Parameters:
  • start1 – Start of first range

  • start2 – Start of second range

  • count – Number of elements to swap

Returns:

Reference to this array for chaining

inline dynarray<T> &swap(size_t pos1, size_t pos2)#

Swap two elements.

fp::raii::dynarray<int> arr = {10, 20, 30};
arr.swap(0, 2);
// arr: [30, 20, 10]

Parameters:
  • pos1 – First position

  • pos2 – Second position

Returns:

Reference to this array for chaining

inline dynarray<T> &swap_delete_range(size_t pos, size_t count)#

Swap range to end and delete.

O(1) deletion that doesn’t preserve order.

fp::dynarray<int> arr;
for(int i = 0; i < 10; i++) arr.push_back(i);

arr.swap_delete_range(2, 2);
// Deleted elements at 2-3 by swapping with end
assert(arr.size() == 8);
Parameters:
  • pos – Starting position

  • count – Number of elements

Returns:

Reference to this array for chaining

inline dynarray<T> &swap_delete(size_t pos)#

Swap element to end and delete.

Fast O(1) unordered deletion.

fp::dynarray<int> arr = {0, 10, 20, 30, 40};
arr.swap_delete(1);
// arr: [0, 40, 20, 30]  (40 moved to position 1)
Parameters:

pos – Position to delete

Returns:

Reference to this array for chaining

inline dynarray<T> &swap_delete_start_end(size_t start, size_t end)#

Swap range to end and delete using start/end indices.

O(1) deletion that doesn’t preserve order.

Parameters:
  • start – Starting index (inclusive)

  • end – Ending index (inclusive)

Returns:

Reference to this array for chaining

inline T &pop_front()#

Remove and return first element.

Shifts all elements left. O(n) operation.

fp::dynarray<int> queue;
queue.push_back(1).push_back(2).push_back(3);

queue.pop_front();
// queue: [2, 3]
Returns:

Reference to removed element (now invalid)

inline dynarray<T> clone() const

Create a copy of this array.

fp::raii::dynarray<int> original;
original.push_back(1);
original.push_back(2);
original.push_back(3);

fp::raii::dynarray<int> copy = original.clone();
copy[0] = 999;  // Doesn't affect original

assert(original[0] == 1);
assert(copy[0] == 999);

Returns:

New array with copied data

inline dynarray<T> &clear()#

Clear all elements (keeps capacity)

Calls destructors for non-trivial types.

fp::raii::dynarray<std::string> strings;
strings.push_back("Hello");
strings.push_back("World");

assert(strings.size() == 2);

strings.clear();

assert(strings.size() == 0);
assert(strings.capacity() > 0);  // Capacity unchanged
Returns:

Reference to this array for chaining

inline dynarray<T> &concatenate_view_in_place(const view<const T> view)#

Concatenate view to this array in place.

fp::raii::dynarray<int> arr;
arr.push_back(1);
arr.push_back(2);

int more[] = {3, 4, 5};
arr.concatenate_view_in_place({more, 3});
// arr: [1, 2, 3, 4, 5]

Parameters:

view – View to append

Returns:

Reference to this array for chaining

inline dynarray<T> concatenate_view(const view<const T> view) const#

Concatenate view (returns new array)

fp::raii::dynarray<int> arr;
arr.push_back(1);
arr.push_back(2);

int more[] = {3, 4, 5};
fp::raii::dynarray<int> combined = arr.concatenate_view({more, 3});
// combined: [1, 2, 3, 4, 5]

Parameters:

view – View to append

Returns:

New array with concatenated data

inline dynarray<T> &concatenate_in_place(const dynarray<T> &other)#

Concatenate other array to this one in place.

fp::raii::dynarray<int> arr1, arr2;
arr1.push_back(1);
arr1.push_back(2);
arr2.push_back(3);
arr2.push_back(4);

arr1.concatenate_in_place(arr2);
// arr1: [1, 2, 3, 4]
// arr2: [3, 4] (unchanged)

Parameters:

other – Array to append

Returns:

Reference to this array for chaining

inline dynarray<T> concatenate(const dynarray<T> &other) const#

Concatenate two arrays (returns new array)

fp::dynarray<int> arr1, arr2;
arr1.push_back(1);
arr1.push_back(2);
arr2.push_back(3);
arr2.push_back(4);

fp::dynarray<int> combined = arr1.concatenate(arr2);
// combined: [1, 2, 3, 4]
// arr1 and arr2 unchanged

Parameters:

other – Array to append

Returns:

New array with concatenated data

Public Members

T *raw#

Raw pointer to data.

template<typename T, typename Derived>
struct dynarray_crtp#
#include <dynarray.hpp>

CRTP base providing dynamic array operations.

This class uses the Curiously Recurring Template Pattern (CRTP) to provide dynamic array operations to derived classes. It handles automatic destructor calls for non-trivial types and provides method chaining.

Template Parameters:
  • T – Element type

  • Derived – The derived class (CRTP pattern)

Subclassed by fp::dynarray< char >

Public Functions

inline bool is_dynarray() const#

Check if this is a valid dynamic array.

fp::raii::dynarray<int> arr;
assert(!arr.is_dynarray());  // Empty/null

arr.push_back(42);
assert(arr.is_dynarray());   // Now valid

Returns:

true if this is a dynamic array

inline size_t capacity() const#

Get allocated capacity.

fp::raii::dynarray<int> arr;
arr.reserve(50);

assert(arr.capacity() == 50);
assert(arr.size() == 0);

arr.push_back(1);
assert(arr.capacity() == 50);  // No reallocation

Returns:

Number of elements that can be stored without reallocation

inline void free(bool nullify = true)#

Free the dynamic array.

Calls destructors for non-trivial types before freeing memory.

fp::dynarray<std::string> strings;
strings.push_back("Hello");
strings.push_back("World");

strings.free();  // Destructors called, memory freed, ptr nulled
assert(!strings);
Parameters:

nullify – If true, sets pointer to null (default: true)

inline void free() const#

Free the dynamic array (const version)

Warning

Does not nullify pointer. Prefer non-const version.

inline Derived &reserve(size_t size)#

Reserve capacity for at least size elements.

fp::raii::dynarray<int> arr;

// Reserve and chain operations
arr.reserve(1000)
   .push_back(42);
arr.push_back(99);

// No reallocations for next 998 elements

Parameters:

size – Minimum capacity to reserve

Returns:

Reference to this array for chaining

inline Derived &grow(size_t to_add, const T &value = {})#

Grow array by adding initialized elements.

fp::raii::dynarray<int> arr;
arr.push_back(1);
arr.push_back(2);

// Add 5 elements, all initialized to 99
arr.grow(5, 99);

assert(arr.size() == 7);
assert(arr[2] == 99);

Parameters:
  • to_add – Number of elements to add

  • value – Value to initialize new elements with (default-constructed if omitted)

Returns:

Reference to this array for chaining

inline Derived &grow_uninitialized(size_t to_add)#

Grow array with uninitialized elements.

New elements are uninitialized - you must initialize them before use. Useful for performance when you’ll immediately overwrite values.

fp::raii::dynarray<int> arr;
arr.push_back(1);

size_t old_size = arr.size();
arr.grow_uninitialized(10);

// Initialize new elements
for(size_t i = old_size; i < arr.size(); i++)
    arr[i] = i * 10;
Parameters:

to_add – Number of elements to add

Returns:

Reference to this array for chaining

inline Derived &grow_to_size(size_t size, const T &value = {})#

Grow to exact size with initialized elements.

fp::raii::dynarray<float> data;
data.grow_to_size(100, 3.14f);

assert(data.size() == 100);
for(auto& val : data)
    assert(val == 3.14f);

Note

If size is smaller than the current size, this function does nothing!

Parameters:
  • size – Target size

  • value – Value to initialize new elements with (default-constructed if omitted)

Returns:

Reference to this array for chaining

inline Derived &grow_to_size_uninitialized(size_t size)#

Grow to exact size with uninitialized elements.

Parameters:

size – Target size

Returns:

Reference to this array for chaining

inline T &push_back(const T &value)#

Add element to end of array.

fp::raii::dynarray<std::string> names;
names.push_back("Alice");
names.push_back("Bob");

std::string& ref = names.push_back("Charlie");
ref += " Smith";  // Modify in place

assert(names[2] == "Charlie Smith");

Parameters:

value – Value to add

Returns:

Reference to the newly added element

template<typename ...Targs>
inline T &emplace_back(Targs... args)#

Construct element in-place at end of array.

Constructs the element directly in the array without copying/moving.

struct Point {
    int x, y;
    Point(int x_, int y_) : x(x_), y(y_) {}
};

fp::raii::dynarray<Point> points;

// Construct directly in array
points.emplace_back(10, 20);
points.emplace_back(30, 40);

assert(points[0].x == 10);
Template Parameters:

Targs – Constructor argument types

Parameters:

args – Constructor arguments

Returns:

Reference to the newly constructed element

inline T &pop_back_n(size_t count)#

Remove last n elements.

fp::raii::dynarray<int> arr;
for(int i = 0; i < 10; i++) arr.push_back(i);

arr.pop_back_n(3);
assert(arr.size() == 7);

Parameters:

count – Number of elements to remove

Returns:

Reference to first element removed (one after the new last element)

inline T &pop_back()#

Remove last element.

fp::raii::dynarray<int> stack;
stack.push_back(10);
stack.push_back(20);
stack.push_back(30);

stack.pop_back();
assert(stack.size() == 2);
assert(stack.back() == 20);

Returns:

Reference to removed element (now invalid)

inline T &pop_back_to_size(size_t size)#

Pop elements until size reaches target.

fp::raii::dynarray<int> arr;
for(int i = 0; i < 100; i++) arr.push_back(i);

arr.pop_back_to_size(50);
assert(arr.size() == 50);

Parameters:

size – Target size

Returns:

Reference to first element removed (one after the new last element)

inline T &insert(size_t pos, const T &value = {})#

Insert element at position.

Elements after pos are shifted right. O(n) operation.

fp::raii::dynarray<int> arr;
arr.push_back(1);
arr.push_back(2);
arr.push_back(4);

arr.insert(2, 3);
// arr: [1, 2, 3, 4]

assert(arr[2] == 3);
Parameters:
  • pos – Position to insert at

  • value – Value to insert (default-constructed if omitted)

Returns:

Reference to inserted element

template<typename ...Targs>
inline T &emplace(size_t pos, Targs... args)#

Construct element in-place at position.

Elements after pos are shifted right. O(n) operation.

struct Widget {
    int id;
    std::string name;
    Widget(int i, std::string n) : id(i), name(std::move(n)) {}
};

fp::raii::dynarray<Widget> widgets;
widgets.emplace_back(1, "First");
widgets.emplace_back(3, "Third");

widgets.emplace(1, 2, "Second");
// widgets: [(1,"First"), (2,"Second"), (3,"Third")]
Template Parameters:

Targs – Constructor argument types

Parameters:
  • pos – Position to insert at

  • args – Constructor arguments

Returns:

Reference to inserted element

inline T &push_front(const T &value = {})#

Insert element at front.

All current elements are shifted right. O(n) operation.

fp::raii::dynarray<int> deque;
deque.push_front(3);
deque.push_front(2);
deque.push_front(1);

// deque: [1, 2, 3]
assert(deque[0] == 1);
Parameters:

value – Value to insert

Returns:

Reference to inserted element

template<typename ...Targs>
inline T &emplace_front(Targs... args)#

Construct element in-place at front.

All current elements are shifted right. O(n) operation.

fp::raii::dynarray<std::string> list;
list.emplace_front("World");
list.emplace_front("Hello");

// list: ["Hello", "World"]
Template Parameters:

Targs – Constructor argument types

Parameters:

args – Constructor arguments

Returns:

Reference to newly constructed element

inline view<T> insert_uninitialized(size_t pos, size_t count = 1)#

Insert uninitialized elements.

Elements after pos are shifted right. O(n) operation.

fp::dynarray<int> arr;
arr.push_back(1);
arr.push_back(5);

auto view = arr.insert_uninitialized(1, 3);
view[0] = 2;
view[1] = 3;
view[2] = 4;

// arr: [1, 2, 3, 4, 5]
Parameters:
  • pos – Position to insert at

  • count – Number of elements to insert (default: 1)

Returns:

View over the inserted uninitialized elements

inline Derived &delete_range(size_t pos, size_t count)#

Delete range of elements.

Elements after pos + count are shifted left. O(n) operation.

fp::raii::dynarray<int> arr;
for(int i = 0; i < 10; i++) arr.push_back(i);

arr.delete_range(3, 4)
   .push_back(99);

assert(arr.size() == 7);
Parameters:
  • pos – Starting position

  • count – Number of elements to delete

Returns:

Reference to this array for chaining

inline Derived &delete_(size_t pos)#

Delete single element.

Elements after pos are shifted left. O(n) operation.

fp::raii::dynarray<int> arr;
arr.push_back(1).push_back(2).push_back(3);

arr.delete_(1);
// arr: [1, 3]
Parameters:

pos – Position to delete

Returns:

Reference to this array for chaining

inline Derived &remove(size_t pos)#

Alias for delete_.

inline Derived &delete_start_end(size_t start, size_t end)#

Delete range using start and end indices.

Elements after end are shifted left. O(n) operation.

Parameters:
  • start – Starting index (inclusive)

  • end – Ending index (inclusive)

Returns:

Reference to this array for chaining

inline Derived &shrink_delete_range(size_t pos, size_t count)#

Delete range and shrink capacity.

Like delete_range but also reallocates to free unused capacity. All elements are copied. O(n) operation.

Parameters:
  • pos – Starting position

  • count – Number of elements to delete

Returns:

Reference to this array for chaining

inline Derived &shrink_delete(size_t pos)#

Delete element and shrink capacity.

Like delete_ but also reallocates to free unused capacity. All elements are copied. O(n) operation.

Parameters:

pos – Position to delete

Returns:

Reference to this array for chaining

inline Derived &shrink_delete_start_end(size_t start, size_t end)#

Delete range and shrink using start/end indices.

Like delete_start_end but also reallocates to free unused capacity. All elements are copied. O(n) operation.

Parameters:
  • start – Starting index (inclusive)

  • end – Ending index (inclusive)

Returns:

Reference to this array for chaining

inline Derived &resize(size_t size)#

Resize array to new size.

fp::raii::dynarray<int> arr;
for(int i = 0; i < 10; i++) arr.push_back(i);

arr.resize(20);  // Grow
assert(arr.size() == 20);

arr.resize(5);   // Shrink
assert(arr.size() == 5);

Parameters:

size – New size

Returns:

Reference to this array for chaining

inline Derived &shrink_to_fit()#

Shrink capacity to match size.

fp::raii::dynarray<int> arr;
arr.reserve(1000);
for(int i = 0; i < 100; i++) arr.push_back(i);

assert(arr.capacity() == 1000);

arr.shrink_to_fit();
assert(arr.capacity() == 100);

Returns:

Reference to this array for chaining

inline Derived &swap_range(size_t start1, size_t start2, size_t count)#

Swap two ranges of elements.

fp::raii::dynarray<int> arr;
for(int i = 0; i < 10; i++) arr.push_back(i);

arr.swap_range(0, 7, 3);
// Swapped [0,1,2] with [7,8,9]

Parameters:
  • start1 – Start of first range

  • start2 – Start of second range

  • count – Number of elements to swap

Returns:

Reference to this array for chaining

inline Derived &swap(size_t pos1, size_t pos2)#

Swap two elements.

fp::raii::dynarray<int> arr = {10, 20, 30};
arr.swap(0, 2);
// arr: [30, 20, 10]

Parameters:
  • pos1 – First position

  • pos2 – Second position

Returns:

Reference to this array for chaining

inline Derived &swap_delete_range(size_t pos, size_t count)#

Swap range to end and delete.

O(1) deletion that doesn’t preserve order.

fp::dynarray<int> arr;
for(int i = 0; i < 10; i++) arr.push_back(i);

arr.swap_delete_range(2, 2);
// Deleted elements at 2-3 by swapping with end
assert(arr.size() == 8);
Parameters:
  • pos – Starting position

  • count – Number of elements

Returns:

Reference to this array for chaining

inline Derived &swap_delete(size_t pos)#

Swap element to end and delete.

Fast O(1) unordered deletion.

fp::dynarray<int> arr = {0, 10, 20, 30, 40};
arr.swap_delete(1);
// arr: [0, 40, 20, 30]  (40 moved to position 1)
Parameters:

pos – Position to delete

Returns:

Reference to this array for chaining

inline Derived &swap_delete_start_end(size_t start, size_t end)#

Swap range to end and delete using start/end indices.

O(1) deletion that doesn’t preserve order.

Parameters:
  • start – Starting index (inclusive)

  • end – Ending index (inclusive)

Returns:

Reference to this array for chaining

inline T &pop_front()#

Remove and return first element.

Shifts all elements left. O(n) operation.

fp::dynarray<int> queue;
queue.push_back(1).push_back(2).push_back(3);

queue.pop_front();
// queue: [2, 3]
Returns:

Reference to removed element (now invalid)

inline Derived clone() const#

Create a copy of this array.

fp::raii::dynarray<int> original;
original.push_back(1);
original.push_back(2);
original.push_back(3);

fp::raii::dynarray<int> copy = original.clone();
copy[0] = 999;  // Doesn't affect original

assert(original[0] == 1);
assert(copy[0] == 999);

Returns:

New array with copied data

inline Derived &clear()#

Clear all elements (keeps capacity)

Calls destructors for non-trivial types.

fp::raii::dynarray<std::string> strings;
strings.push_back("Hello");
strings.push_back("World");

assert(strings.size() == 2);

strings.clear();

assert(strings.size() == 0);
assert(strings.capacity() > 0);  // Capacity unchanged
Returns:

Reference to this array for chaining

inline Derived &concatenate_view_in_place(const view<const T> view)#

Concatenate view to this array in place.

fp::raii::dynarray<int> arr;
arr.push_back(1);
arr.push_back(2);

int more[] = {3, 4, 5};
arr.concatenate_view_in_place({more, 3});
// arr: [1, 2, 3, 4, 5]

Parameters:

view – View to append

Returns:

Reference to this array for chaining

inline Derived concatenate_view(const view<const T> view) const#

Concatenate view (returns new array)

fp::raii::dynarray<int> arr;
arr.push_back(1);
arr.push_back(2);

int more[] = {3, 4, 5};
fp::raii::dynarray<int> combined = arr.concatenate_view({more, 3});
// combined: [1, 2, 3, 4, 5]

Parameters:

view – View to append

Returns:

New array with concatenated data

inline Derived &concatenate_in_place(const Derived &other)#

Concatenate other array to this one in place.

fp::raii::dynarray<int> arr1, arr2;
arr1.push_back(1);
arr1.push_back(2);
arr2.push_back(3);
arr2.push_back(4);

arr1.concatenate_in_place(arr2);
// arr1: [1, 2, 3, 4]
// arr2: [3, 4] (unchanged)

Parameters:

other – Array to append

Returns:

Reference to this array for chaining

inline Derived concatenate(const Derived &other) const#

Concatenate two arrays (returns new array)

fp::dynarray<int> arr1, arr2;
arr1.push_back(1);
arr1.push_back(2);
arr2.push_back(3);
arr2.push_back(4);

fp::dynarray<int> combined = arr1.concatenate(arr2);
// combined: [1, 2, 3, 4]
// arr1 and arr2 unchanged

Parameters:

other – Array to append

Returns:

New array with concatenated data

template<typename T>
struct pointer : public fp::wrapped::pointer<T>, public fp::pointer_crtp<T, pointer<T>>#
#include <pointer.hpp>

Main fat pointer class (fp::pointer) with manual memory management.

This is the primary fat pointer class that provides manual memory management with explicit free() calls. For automatic memory management, use fp::raii::pointer or fp::auto_free.

The pointer class combines the wrapped::pointer base (for raw pointer access) with pointer_crtp (for fat pointer operations), providing a complete interface for working with dynamically allocated arrays with bounds checking.

// Manual memory management
fp::pointer<int> data = fp::malloc<int>(100);
data[0] = 42;
data.realloc(200);  // Resize to 200 elements
data.free();

// Or use clone for deep copy
fp::auto_free original = fp::malloc<int>(10);
original.fill(5);
fp::pointer<int> copy = original.clone();
copy.free();
original.free(false); // ERROR: Using non-nullifying free on an auto_free type will result in a double free!

Iteration Examples#

fp::auto_free arr = fp::malloc<int>(50);

// Range-based for loop
for(int& val : arr)
    val = 10;

// STL algorithms
std::sort(arr.begin(), arr.end());
std::iota(arr.begin(), arr.end(), 0);

// Manual iteration
for(size_t i = 0; i < arr.size(); i++)
    arr[i] *= 2;

Working with Views#

fp::auto_free data = fp::malloc<int>(100);

// Create view of middle section
auto middle = data.view(25, 50);
for(int& val : middle) {
    val = 999;
}

// Create multiple views
auto first_half = data.view(0, 50);
auto second_half = data.view(50, 50);
Template Parameters:

T – Element type

Subclassed by fp::dynarray< char >, fp::dynarray< T >

Unnamed Group

inline T &operator*()#

Dereference operator.

Returns:

Reference to the pointed-to value

Unnamed Group

inline T *operator->()#

Arrow operator for member access.

Returns:

Raw pointer

Unnamed Group

inline T *data()#

Get raw pointer to data.

fp::auto_free arr = fp::malloc<int>(10);
int* raw = arr.data();

// Use with C functions
memset(raw, 0, arr.size() * sizeof(int));

// Pass to C APIs
qsort(arr.data(), arr.size(), sizeof(int), compare_func);

Unnamed Group

inline explicit operator T*()#

Explicit conversion to raw pointer.

Requires explicit cast to prevent accidental implicit conversions.

fp::auto_free arr = fp::malloc<int>(10);

// Explicit cast required
int* ptr = (int*)arr;

// Won't compile (good!):
// int* ptr2 = arr;

Unnamed Group

inline size_t length() const#

Get the number of elements.

fp::auto_free arr = fp::malloc<int>(50);

std::cout << "Array has " << arr.length() << " elements\n";

// Use in loops
for(size_t i = 0; i < arr.length(); i++)
    arr[i] = i;

Unnamed Group

inline T *begin()#

Get iterator to beginning.

fp::auto_free arr = fp::malloc<int>(50);

// Manual iteration
for(int* it = arr.begin(); it != arr.end(); ++it)
    *it = 42;

// Use with STL algorithms
std::iota(arr.begin(), arr.end(), 0);
std::sort(arr.begin(), arr.end());
std::reverse(arr.begin(), arr.end());

// Distance
size_t count = std::distance(arr.begin(), arr.end());
assert(count == arr.size());

Unnamed Group

inline T *end()#

Get iterator to end.

fp::auto_free arr = fp::malloc<int>(100);
arr.fill(1);

// Range-based for loop
for(int& val : arr)
    val *= 2;

// Find operations
auto it = std::find(arr.begin(), arr.end(), 42);
if(it != arr.end())
    std::cout << "Found at index: " << (it - arr.begin()) << "\n";

// Accumulate
int sum = std::accumulate(arr.begin(), arr.end(), 0);

Unnamed Group

inline T &operator*()

Dereference operator - access first element.

Returns:

Reference to first element

Unnamed Group

inline T *operator->()

Arrow operator - access first element’s members.

Returns:

Pointer to first element

Unnamed Group

inline T &operator[](size_t i)#

Subscript operator - access element by index.

Note

Bounds checked in debug!

Parameters:

i – Index

Returns:

Reference to element at index

Public Types

using view_t = fp::view<T>#

View type for this pointer.

Public Functions

inline constexpr pointer(T *ptr_)#

Construct from raw fat pointer.

// From C-style allocation
int* c_style = fp_malloc(int, 20);
fp::pointer<int> wrapper(c_style);
assert(wrapper.size() == 20);
wrapper.free();

Parameters:

ptr_ – Raw fat pointer (must be null or valid fat pointer)

inline operator pointer<std::add_const_t<T>>() const#

Implicit conversion to const pointer.

Allows passing non-const pointers to functions expecting const pointers.

void print_data(fp::pointer<const int> data) {
    for(size_t i = 0; i < data.size(); i++) {
        std::cout << data[i] << " ";
    }
}

fp::auto_release arr = fp::malloc<int>(5).fill(42);
print_data(arr);  // Implicit conversion
inline void free(bool nullify = true)#

Free the allocated memory.

Releases the memory allocated for this fat pointer. By default, the internal pointer is set to nullptr after freeing to prevent use-after-free bugs.

This is the preferred method for freeing memory as it provides safety by default. The pointer object remains valid but will be null after this call.

fp::pointer<int> arr = fp::malloc<int>(20);

// Default: nullifies after free
arr.free();
assert(arr.data() == nullptr);

// Explicit: don't nullify (use with caution)
fp::pointer<int> arr2 = fp::malloc<int>(20);
arr2.free(false);
// arr2.data() is now a dangling pointer - DANGEROUS!

// Better: use default behavior
fp::pointer<int> arr3 = fp::malloc<int>(20);
arr3.free(true);  // Explicit, but default

RAII Dynarray Usage#

// If you don't want to call free() manually, use RAII
{
    fp::raii::pointer<int> auto_arr = fp::malloc<int>(100);
    // or fp::auto_free auto_arr = ...
    // Use auto_arr...
    // Automatically freed when scope exits
}

See also

fp::raii::pointer for automatic memory management

Warning

After calling free(), any views or references to the data become invalid.

Warning

Only call free() on heap-allocated pointers, not stack-allocated ones.

Parameters:

nullify – If true, sets the internal pointer to nullptr after freeing (default: true)

inline void free() const#

Free the allocated memory (const version)

This overload is provided for const pointers and does not nullify the internal pointer. Use with extreme caution as the pointer will be left in a dangling state.

This overload exists primarily for compatibility with const contexts, but its use is discouraged. Consider restructuring your code to avoid freeing through const references.

// Dangerous usage (avoid if possible)
void unsafe_free(const fp::pointer<int>& arr) {
    arr.free();  // Calls const version
    // arr.data() is now dangling but we can't nullify it
}

See also

free(bool) for the safer, non-const version

Warning

This version does not nullify the pointer, making it dangerous. Prefer the non-const version whenever possible.

Warning

The pointer becomes invalid but the const object cannot be modified.

inline pointer &realloc(size_t new_count)#

Reallocate with a new size.

Existing data is preserved up to the minimum of old and new sizes. The pointer address may change, but the reference is updated automatically.

fp::pointer<int> arr = fp::malloc<int>(10);
for(size_t i = 0; i < arr.size(); i++)
    arr[i] = i;

// Expand to 20 elements (first 10 preserved)
arr.realloc(20);
assert(arr.size() == 20);
assert(arr[5] == 5);  // Old data still there

// Shrink to 5 elements
arr.realloc(5);
assert(arr.size() == 5);

arr.free();
Parameters:

new_count – New number of elements

Returns:

Reference to this pointer (may have new address)

inline pointer clone() const#

Create a copy of this pointer.

Creates an independent copy of the data. Modifications to the clone do not affect the original.

fp::auto_free original = fp::malloc<int>(5).fill(42);

fp::pointer<int> copy = original.clone();
copy[0] = 99;  // Doesn't affect original

assert(original[0] == 42);
assert(copy[0] == 99);

copy.free();
Returns:

New pointer with copied data (must be freed separately)

inline T *data()

Get the raw pointer.

inline const T *data() const#

Get the raw pointer (const version)

inline T *release()#

Release ownership of the pointer.

After calling release(), the pointer object no longer owns the memory. The caller becomes responsible for freeing it.

fp::auto_free arr = fp::malloc<int>(10).fill(42);

// Transfer ownership
int* raw = arr.release();
assert(arr.data() == nullptr);

// Now we must manually free
fp_free(raw);
Returns:

The raw pointer (ownership transferred to caller)

inline bool is_fp() const#

Check if this is a valid fat pointer.

int regular[10];
fp::auto_free arr = fp::malloc<int>(10);
fp::pointer<int> null_ptr;

assert(arr.is_fp());
assert(!null_ptr.is_fp());
inline bool stack_allocated() const#

Check if this is stack-allocated.

fp::array<int, 10> stack_arr;
fp::auto_free heap_arr = fp::malloc<int>(10);

assert(stack_arr.stack_allocated());
assert(!heap_arr.stack_allocated());
inline bool heap_allocated() const#

Check if this is heap-allocated.

fp::array<int, 10> stack_arr;
fp::auto_free heap_arr = fp::malloc<int>(10);

assert(!stack_arr.heap_allocated());
assert(heap_arr.heap_allocated());
inline operator bool() const#

Check if pointer is non-null.

fp::auto_free arr = fp::malloc<int>(10);
fp::pointer<int> null_ptr;

if(arr)
    std::cout << "Array is valid\n";

if(!null_ptr)
    std::cout << "Null pointer\n";

// Safe access pattern
if(arr && arr.size() > 0)
    arr[0] = 42;
inline bool empty() const#

Check if the pointer is empty.

fp::auto_free arr1 = fp::malloc<int>(10);
fp::auto_free arr2 = fp::malloc<int>(0);

assert(!arr1.empty());
assert(arr2.empty());

// Safe access pattern
if(!arr1.empty())
    arr1[0] = 42;
inline view_t view(size_t start, size_t length)#

Create a view of a portion of this pointer.

fp::auto_free arr = fp::malloc<int>(100);

// View elements 20-39
auto v = arr.view(20, 20);
v.fill(999);

assert(arr[20] == 999);
assert(arr[39] == 999);
assert(arr[19] != 999);  // Outside view

// Process data in chunks
for(size_t i = 0; i < arr.size(); i += 10) {
    auto chunk = arr.view(i, 10);
    process_chunk(chunk);
}

Parameters:
  • start – Starting index

  • length – Number of elements

Returns:

View over the specified range

inline const view_t view(size_t start, size_t length) const#

Create a view (const version)

inline view_t view_full()#

Create a view of the entire pointer.

fp::auto_free arr = fp::malloc<int>(50);

// Create full view
auto v = arr.view_full();
assert(v.size() == arr.size());

// Useful for passing to functions expecting views
void process_view(fp::view<int> data) {
    for(auto& val : data) val *= 2;
}

process_view(arr.view_full());

Returns:

View over all elements

inline const view_t view_full() const#

Create a full view (const version)

inline view_t full_view()#

Alias for view_full.

fp::auto_free arr = fp::malloc<int>(50);

// Both are equivalent
auto v1 = arr.view_full();
auto v2 = arr.full_view();

assert(v1.size() == v2.size());
inline const view_t full_view() const#

Alias for view_full (const version)

inline view_t view_start_end(size_t start, size_t end)#

Create a view using start and end indices.

fp::auto_free arr = fp::malloc<int>(100);

// View elements 10 through 20 (inclusive) - 11 elements
auto v = arr.view_start_end(10, 20);
assert(v.size() == 11);

// Process quarters
size_t quarter = arr.size() / 4;
auto q1 = arr.view_start_end(0, quarter - 1);
auto q2 = arr.view_start_end(quarter, 2 * quarter - 1);
auto q3 = arr.view_start_end(2 * quarter, 3 * quarter - 1);
auto q4 = arr.view_start_end(3 * quarter, arr.size() - 1);

Parameters:
  • start – Starting index (inclusive)

  • end – Ending index (inclusive)

Returns:

View over the range [start, end]

inline const view_t view_start_end(size_t start, size_t end) const#

Create a view with start/end indices (const version)

inline T &front()#

Get reference to first element.

fp::auto_free arr = fp::malloc<int>(10).fill(5);

arr.front() = 100;
assert(arr[0] == 100);

// Useful for algorithms
int first = arr.front();
int last = arr.back();
inline T &back()#

Get reference to last element.

fp::auto_free arr = fp::malloc<int>(10);
arr.fill(5);

arr.back() = 999;
assert(arr[arr.size() - 1] == 999);

// Access both ends
if(!arr.empty()) {
    arr.front() = 1;
    arr.back() = 100;
}
inline pointer<T> &fill(const T &value = {})#

Fill all elements with a value.

fp::auto_free arr = fp::malloc<int>(100);

// Fill with specific value
arr.fill(42);
assert(arr[0] == 42);
assert(arr[99] == 42);

// Fill with default value (0 for int)
arr.fill();
assert(arr[0] == 0);

// Method chaining
fp::auto_free data = fp::malloc<float>(50).fill(3.14f);

// Fill with custom types
struct Point { int x = 0, y = 0; };
fp::auto_free points = fp::malloc<Point>(10);
points.fill(Point{5, 10});

Parameters:

value – Value to fill with (default-constructed if not provided)

Returns:

Reference to derived class for chaining

Public Members

T *raw#

Raw pointer to data.

template<typename T, typename Derived>
struct pointer_crtp#
#include <pointer.hpp>

CRTP base providing common fat pointer operations.

This class uses the Curiously Recurring Template Pattern (CRTP) to provide common fat pointer operations to derived classes. It expects the derived class to provide a ptr() method that returns a reference to the underlying fat pointer.

template<typename T>
struct my_pointer : public pointer_crtp<T, my_pointer<T>> {
    T* raw_ptr;
protected:
    T*& ptr() { return raw_ptr; }
    const T* const& ptr() const { return raw_ptr; }
};

// Now my_pointer has all fat pointer operations
my_pointer<int> p;
p.raw_ptr = fp_malloc(int, 10);
assert(p.size() == 10);
p.fill(42);
fp_free(p.data());
Template Parameters:
  • T – Element type

  • Derived – The derived class (CRTP pattern)

Subclassed by fp::pointer< char >

Unnamed Group

inline T *data()#

Get raw pointer to data.

fp::auto_free arr = fp::malloc<int>(10);
int* raw = arr.data();

// Use with C functions
memset(raw, 0, arr.size() * sizeof(int));

// Pass to C APIs
qsort(arr.data(), arr.size(), sizeof(int), compare_func);

Unnamed Group

inline explicit operator T*()#

Explicit conversion to raw pointer.

Requires explicit cast to prevent accidental implicit conversions.

fp::auto_free arr = fp::malloc<int>(10);

// Explicit cast required
int* ptr = (int*)arr;

// Won't compile (good!):
// int* ptr2 = arr;

Unnamed Group

inline size_t length() const#

Get the number of elements.

fp::auto_free arr = fp::malloc<int>(50);

std::cout << "Array has " << arr.length() << " elements\n";

// Use in loops
for(size_t i = 0; i < arr.length(); i++)
    arr[i] = i;

Unnamed Group

inline T *begin()#

Get iterator to beginning.

fp::auto_free arr = fp::malloc<int>(50);

// Manual iteration
for(int* it = arr.begin(); it != arr.end(); ++it)
    *it = 42;

// Use with STL algorithms
std::iota(arr.begin(), arr.end(), 0);
std::sort(arr.begin(), arr.end());
std::reverse(arr.begin(), arr.end());

// Distance
size_t count = std::distance(arr.begin(), arr.end());
assert(count == arr.size());

Unnamed Group

inline T *end()#

Get iterator to end.

fp::auto_free arr = fp::malloc<int>(100);
arr.fill(1);

// Range-based for loop
for(int& val : arr)
    val *= 2;

// Find operations
auto it = std::find(arr.begin(), arr.end(), 42);
if(it != arr.end())
    std::cout << "Found at index: " << (it - arr.begin()) << "\n";

// Accumulate
int sum = std::accumulate(arr.begin(), arr.end(), 0);

Unnamed Group

inline T &operator*()#

Dereference operator - access first element.

Returns:

Reference to first element

Unnamed Group

inline T *operator->()#

Arrow operator - access first element’s members.

Returns:

Pointer to first element

Unnamed Group

inline T &operator[](size_t i)#

Subscript operator - access element by index.

Note

Bounds checked in debug!

Parameters:

i – Index

Returns:

Reference to element at index

Public Types

using view_t = fp::view<T>#

View type for this pointer.

Public Functions

inline T *release()#

Release ownership of the pointer.

After calling release(), the pointer object no longer owns the memory. The caller becomes responsible for freeing it.

fp::auto_free arr = fp::malloc<int>(10).fill(42);

// Transfer ownership
int* raw = arr.release();
assert(arr.data() == nullptr);

// Now we must manually free
fp_free(raw);
Returns:

The raw pointer (ownership transferred to caller)

inline bool is_fp() const#

Check if this is a valid fat pointer.

int regular[10];
fp::auto_free arr = fp::malloc<int>(10);
fp::pointer<int> null_ptr;

assert(arr.is_fp());
assert(!null_ptr.is_fp());
inline bool stack_allocated() const#

Check if this is stack-allocated.

fp::array<int, 10> stack_arr;
fp::auto_free heap_arr = fp::malloc<int>(10);

assert(stack_arr.stack_allocated());
assert(!heap_arr.stack_allocated());
inline bool heap_allocated() const#

Check if this is heap-allocated.

fp::array<int, 10> stack_arr;
fp::auto_free heap_arr = fp::malloc<int>(10);

assert(!stack_arr.heap_allocated());
assert(heap_arr.heap_allocated());
inline operator bool() const#

Check if pointer is non-null.

fp::auto_free arr = fp::malloc<int>(10);
fp::pointer<int> null_ptr;

if(arr)
    std::cout << "Array is valid\n";

if(!null_ptr)
    std::cout << "Null pointer\n";

// Safe access pattern
if(arr && arr.size() > 0)
    arr[0] = 42;
inline bool empty() const#

Check if the pointer is empty.

fp::auto_free arr1 = fp::malloc<int>(10);
fp::auto_free arr2 = fp::malloc<int>(0);

assert(!arr1.empty());
assert(arr2.empty());

// Safe access pattern
if(!arr1.empty())
    arr1[0] = 42;
inline view_t view(size_t start, size_t length)#

Create a view of a portion of this pointer.

fp::auto_free arr = fp::malloc<int>(100);

// View elements 20-39
auto v = arr.view(20, 20);
v.fill(999);

assert(arr[20] == 999);
assert(arr[39] == 999);
assert(arr[19] != 999);  // Outside view

// Process data in chunks
for(size_t i = 0; i < arr.size(); i += 10) {
    auto chunk = arr.view(i, 10);
    process_chunk(chunk);
}

Parameters:
  • start – Starting index

  • length – Number of elements

Returns:

View over the specified range

inline view_t view_full()#

Create a view of the entire pointer.

fp::auto_free arr = fp::malloc<int>(50);

// Create full view
auto v = arr.view_full();
assert(v.size() == arr.size());

// Useful for passing to functions expecting views
void process_view(fp::view<int> data) {
    for(auto& val : data) val *= 2;
}

process_view(arr.view_full());

Returns:

View over all elements

inline view_t full_view()#

Alias for view_full.

fp::auto_free arr = fp::malloc<int>(50);

// Both are equivalent
auto v1 = arr.view_full();
auto v2 = arr.full_view();

assert(v1.size() == v2.size());
inline view_t view_start_end(size_t start, size_t end)#

Create a view using start and end indices.

fp::auto_free arr = fp::malloc<int>(100);

// View elements 10 through 20 (inclusive) - 11 elements
auto v = arr.view_start_end(10, 20);
assert(v.size() == 11);

// Process quarters
size_t quarter = arr.size() / 4;
auto q1 = arr.view_start_end(0, quarter - 1);
auto q2 = arr.view_start_end(quarter, 2 * quarter - 1);
auto q3 = arr.view_start_end(2 * quarter, 3 * quarter - 1);
auto q4 = arr.view_start_end(3 * quarter, arr.size() - 1);

Parameters:
  • start – Starting index (inclusive)

  • end – Ending index (inclusive)

Returns:

View over the range [start, end]

inline const view_t view(size_t start, size_t length) const#

Create a view (const version)

inline const view_t view_full() const#

Create a full view (const version)

inline const view_t full_view() const#

Alias for view_full (const version)

inline const view_t view_start_end(size_t start, size_t end) const#

Create a view with start/end indices (const version)

inline T &front()#

Get reference to first element.

fp::auto_free arr = fp::malloc<int>(10).fill(5);

arr.front() = 100;
assert(arr[0] == 100);

// Useful for algorithms
int first = arr.front();
int last = arr.back();
inline T &back()#

Get reference to last element.

fp::auto_free arr = fp::malloc<int>(10);
arr.fill(5);

arr.back() = 999;
assert(arr[arr.size() - 1] == 999);

// Access both ends
if(!arr.empty()) {
    arr.front() = 1;
    arr.back() = 100;
}
inline Derived &fill(const T &value = {})#

Fill all elements with a value.

fp::auto_free arr = fp::malloc<int>(100);

// Fill with specific value
arr.fill(42);
assert(arr[0] == 42);
assert(arr[99] == 42);

// Fill with default value (0 for int)
arr.fill();
assert(arr[0] == 0);

// Method chaining
fp::auto_free data = fp::malloc<float>(50).fill(3.14f);

// Fill with custom types
struct Point { int x = 0, y = 0; };
fp::auto_free points = fp::malloc<Point>(10);
points.fill(Point{5, 10});

Parameters:

value – Value to fill with (default-constructed if not provided)

Returns:

Reference to derived class for chaining

struct string : public fp::dynarray<char>, public fp::string_crtp<string>#

Subclassed by fp::auto_free< string >

Unnamed Group

inline T *data()#

Get raw pointer to data.

fp::auto_free arr = fp::malloc<int>(10);
int* raw = arr.data();

// Use with C functions
memset(raw, 0, arr.size() * sizeof(int));

// Pass to C APIs
qsort(arr.data(), arr.size(), sizeof(int), compare_func);

Unnamed Group

inline T &operator*()#

Dereference operator.

Returns:

Reference to the pointed-to value

Unnamed Group

inline T &operator*()

Dereference operator - access first element.

Returns:

Reference to first element

Unnamed Group

inline T *operator->()#

Arrow operator for member access.

Returns:

Raw pointer

Unnamed Group

inline T *operator->()

Arrow operator - access first element’s members.

Returns:

Pointer to first element

Unnamed Group

inline explicit operator T*()#

Explicit conversion to raw pointer.

Requires explicit cast to prevent accidental implicit conversions.

fp::auto_free arr = fp::malloc<int>(10);

// Explicit cast required
int* ptr = (int*)arr;

// Won't compile (good!):
// int* ptr2 = arr;

Unnamed Group

inline size_t length() const#

Get the number of elements.

fp::auto_free arr = fp::malloc<int>(50);

std::cout << "Array has " << arr.length() << " elements\n";

// Use in loops
for(size_t i = 0; i < arr.length(); i++)
    arr[i] = i;

Unnamed Group

inline T *begin()#

Get iterator to beginning.

fp::auto_free arr = fp::malloc<int>(50);

// Manual iteration
for(int* it = arr.begin(); it != arr.end(); ++it)
    *it = 42;

// Use with STL algorithms
std::iota(arr.begin(), arr.end(), 0);
std::sort(arr.begin(), arr.end());
std::reverse(arr.begin(), arr.end());

// Distance
size_t count = std::distance(arr.begin(), arr.end());
assert(count == arr.size());

Unnamed Group

inline T *end()#

Get iterator to end.

fp::auto_free arr = fp::malloc<int>(100);
arr.fill(1);

// Range-based for loop
for(int& val : arr)
    val *= 2;

// Find operations
auto it = std::find(arr.begin(), arr.end(), 42);
if(it != arr.end())
    std::cout << "Found at index: " << (it - arr.begin()) << "\n";

// Accumulate
int sum = std::accumulate(arr.begin(), arr.end(), 0);

Unnamed Group

inline T &operator[](size_t i)#

Subscript operator - access element by index.

Note

Bounds checked in debug!

Parameters:

i – Index

Returns:

Reference to element at index

Public Types

using view_t = fp::view<T>#

View type for this pointer.

Public Functions

inline operator dynarray<std::add_const_t<char>>() const#

Implicit conversion to const dynarray.

Allows passing non-const arrays to functions expecting const arrays.

void print_numbers(const fp::dynarray<const int>& arr) {
    for(auto& n : arr)
        std::cout << n << " ";
}

fp::dynarray<int> numbers = {1, 2, 3};
print_numbers(numbers);  // Implicit conversion
numbers.free();
inline operator pointer<std::add_const_t<T>>() const#

Implicit conversion to const pointer.

Allows passing non-const pointers to functions expecting const pointers.

void print_data(fp::pointer<const int> data) {
    for(size_t i = 0; i < data.size(); i++) {
        std::cout << data[i] << " ";
    }
}

fp::auto_release arr = fp::malloc<int>(5).fill(42);
print_data(arr);  // Implicit conversion
inline void free(bool nullify = true)#

Free the allocated memory.

Releases the memory allocated for this fat pointer. By default, the internal pointer is set to nullptr after freeing to prevent use-after-free bugs.

This is the preferred method for freeing memory as it provides safety by default. The pointer object remains valid but will be null after this call.

fp::pointer<int> arr = fp::malloc<int>(20);

// Default: nullifies after free
arr.free();
assert(arr.data() == nullptr);

// Explicit: don't nullify (use with caution)
fp::pointer<int> arr2 = fp::malloc<int>(20);
arr2.free(false);
// arr2.data() is now a dangling pointer - DANGEROUS!

// Better: use default behavior
fp::pointer<int> arr3 = fp::malloc<int>(20);
arr3.free(true);  // Explicit, but default

RAII Dynarray Usage#

// If you don't want to call free() manually, use RAII
{
    fp::raii::pointer<int> auto_arr = fp::malloc<int>(100);
    // or fp::auto_free auto_arr = ...
    // Use auto_arr...
    // Automatically freed when scope exits
}

See also

fp::raii::pointer for automatic memory management

Warning

After calling free(), any views or references to the data become invalid.

Warning

Only call free() on heap-allocated pointers, not stack-allocated ones.

Parameters:

nullify – If true, sets the internal pointer to nullptr after freeing (default: true)

inline void free() const#

Free the allocated memory (const version)

This overload is provided for const pointers and does not nullify the internal pointer. Use with extreme caution as the pointer will be left in a dangling state.

This overload exists primarily for compatibility with const contexts, but its use is discouraged. Consider restructuring your code to avoid freeing through const references.

// Dangerous usage (avoid if possible)
void unsafe_free(const fp::pointer<int>& arr) {
    arr.free();  // Calls const version
    // arr.data() is now dangling but we can't nullify it
}

See also

free(bool) for the safer, non-const version

Warning

This version does not nullify the pointer, making it dangerous. Prefer the non-const version whenever possible.

Warning

The pointer becomes invalid but the const object cannot be modified.

inline void free(bool nullify = true)

Free the dynamic array.

Calls destructors for non-trivial types before freeing memory.

fp::dynarray<std::string> strings;
strings.push_back("Hello");
strings.push_back("World");

strings.free();  // Destructors called, memory freed, ptr nulled
assert(!strings);
Parameters:

nullify – If true, sets pointer to null (default: true)

inline void free() const

Free the dynamic array (const version)

Warning

Does not nullify pointer. Prefer non-const version.

inline pointer &realloc(size_t new_count)#

Reallocate with a new size.

Existing data is preserved up to the minimum of old and new sizes. The pointer address may change, but the reference is updated automatically.

fp::pointer<int> arr = fp::malloc<int>(10);
for(size_t i = 0; i < arr.size(); i++)
    arr[i] = i;

// Expand to 20 elements (first 10 preserved)
arr.realloc(20);
assert(arr.size() == 20);
assert(arr[5] == 5);  // Old data still there

// Shrink to 5 elements
arr.realloc(5);
assert(arr.size() == 5);

arr.free();
Parameters:

new_count – New number of elements

Returns:

Reference to this pointer (may have new address)

inline pointer clone() const#

Create a copy of this pointer.

Creates an independent copy of the data. Modifications to the clone do not affect the original.

fp::auto_free original = fp::malloc<int>(5).fill(42);

fp::pointer<int> copy = original.clone();
copy[0] = 99;  // Doesn't affect original

assert(original[0] == 42);
assert(copy[0] == 99);

copy.free();
Returns:

New pointer with copied data (must be freed separately)

inline Derived clone() const

Create a copy of this array.

fp::raii::dynarray<int> original;
original.push_back(1);
original.push_back(2);
original.push_back(3);

fp::raii::dynarray<int> copy = original.clone();
copy[0] = 999;  // Doesn't affect original

assert(original[0] == 1);
assert(copy[0] == 999);

Returns:

New array with copied data

inline T *data()

Get the raw pointer.

inline const T *data() const#

Get the raw pointer (const version)

inline T *release()#

Release ownership of the pointer.

After calling release(), the pointer object no longer owns the memory. The caller becomes responsible for freeing it.

fp::auto_free arr = fp::malloc<int>(10).fill(42);

// Transfer ownership
int* raw = arr.release();
assert(arr.data() == nullptr);

// Now we must manually free
fp_free(raw);
Returns:

The raw pointer (ownership transferred to caller)

inline bool is_fp() const#

Check if this is a valid fat pointer.

int regular[10];
fp::auto_free arr = fp::malloc<int>(10);
fp::pointer<int> null_ptr;

assert(arr.is_fp());
assert(!null_ptr.is_fp());
inline bool stack_allocated() const#

Check if this is stack-allocated.

fp::array<int, 10> stack_arr;
fp::auto_free heap_arr = fp::malloc<int>(10);

assert(stack_arr.stack_allocated());
assert(!heap_arr.stack_allocated());
inline bool heap_allocated() const#

Check if this is heap-allocated.

fp::array<int, 10> stack_arr;
fp::auto_free heap_arr = fp::malloc<int>(10);

assert(!stack_arr.heap_allocated());
assert(heap_arr.heap_allocated());
inline operator bool() const#

Check if pointer is non-null.

fp::auto_free arr = fp::malloc<int>(10);
fp::pointer<int> null_ptr;

if(arr)
    std::cout << "Array is valid\n";

if(!null_ptr)
    std::cout << "Null pointer\n";

// Safe access pattern
if(arr && arr.size() > 0)
    arr[0] = 42;
inline bool empty() const#

Check if the pointer is empty.

fp::auto_free arr1 = fp::malloc<int>(10);
fp::auto_free arr2 = fp::malloc<int>(0);

assert(!arr1.empty());
assert(arr2.empty());

// Safe access pattern
if(!arr1.empty())
    arr1[0] = 42;
inline view_t view(size_t start, size_t length)#

Create a view of a portion of this pointer.

fp::auto_free arr = fp::malloc<int>(100);

// View elements 20-39
auto v = arr.view(20, 20);
v.fill(999);

assert(arr[20] == 999);
assert(arr[39] == 999);
assert(arr[19] != 999);  // Outside view

// Process data in chunks
for(size_t i = 0; i < arr.size(); i += 10) {
    auto chunk = arr.view(i, 10);
    process_chunk(chunk);
}

Parameters:
  • start – Starting index

  • length – Number of elements

Returns:

View over the specified range

inline const view_t view(size_t start, size_t length) const#

Create a view (const version)

inline view_t view_full()#

Create a view of the entire pointer.

fp::auto_free arr = fp::malloc<int>(50);

// Create full view
auto v = arr.view_full();
assert(v.size() == arr.size());

// Useful for passing to functions expecting views
void process_view(fp::view<int> data) {
    for(auto& val : data) val *= 2;
}

process_view(arr.view_full());

Returns:

View over all elements

inline const view_t view_full() const#

Create a full view (const version)

inline view_t full_view()#

Alias for view_full.

fp::auto_free arr = fp::malloc<int>(50);

// Both are equivalent
auto v1 = arr.view_full();
auto v2 = arr.full_view();

assert(v1.size() == v2.size());
inline const view_t full_view() const#

Alias for view_full (const version)

inline view_t view_start_end(size_t start, size_t end)#

Create a view using start and end indices.

fp::auto_free arr = fp::malloc<int>(100);

// View elements 10 through 20 (inclusive) - 11 elements
auto v = arr.view_start_end(10, 20);
assert(v.size() == 11);

// Process quarters
size_t quarter = arr.size() / 4;
auto q1 = arr.view_start_end(0, quarter - 1);
auto q2 = arr.view_start_end(quarter, 2 * quarter - 1);
auto q3 = arr.view_start_end(2 * quarter, 3 * quarter - 1);
auto q4 = arr.view_start_end(3 * quarter, arr.size() - 1);

Parameters:
  • start – Starting index (inclusive)

  • end – Ending index (inclusive)

Returns:

View over the range [start, end]

inline const view_t view_start_end(size_t start, size_t end) const#

Create a view with start/end indices (const version)

inline T &front()#

Get reference to first element.

fp::auto_free arr = fp::malloc<int>(10).fill(5);

arr.front() = 100;
assert(arr[0] == 100);

// Useful for algorithms
int first = arr.front();
int last = arr.back();
inline T &back()#

Get reference to last element.

fp::auto_free arr = fp::malloc<int>(10);
arr.fill(5);

arr.back() = 999;
assert(arr[arr.size() - 1] == 999);

// Access both ends
if(!arr.empty()) {
    arr.front() = 1;
    arr.back() = 100;
}
inline pointer<T> &fill(const T &value = {})#

Fill all elements with a value.

fp::auto_free arr = fp::malloc<int>(100);

// Fill with specific value
arr.fill(42);
assert(arr[0] == 42);
assert(arr[99] == 42);

// Fill with default value (0 for int)
arr.fill();
assert(arr[0] == 0);

// Method chaining
fp::auto_free data = fp::malloc<float>(50).fill(3.14f);

// Fill with custom types
struct Point { int x = 0, y = 0; };
fp::auto_free points = fp::malloc<Point>(10);
points.fill(Point{5, 10});

Parameters:

value – Value to fill with (default-constructed if not provided)

Returns:

Reference to derived class for chaining

inline bool is_dynarray() const#

Check if this is a valid dynamic array.

fp::raii::dynarray<int> arr;
assert(!arr.is_dynarray());  // Empty/null

arr.push_back(42);
assert(arr.is_dynarray());   // Now valid

Returns:

true if this is a dynamic array

inline size_t capacity() const#

Get allocated capacity.

fp::raii::dynarray<int> arr;
arr.reserve(50);

assert(arr.capacity() == 50);
assert(arr.size() == 0);

arr.push_back(1);
assert(arr.capacity() == 50);  // No reallocation

Returns:

Number of elements that can be stored without reallocation

inline Derived &reserve(size_t size)#

Reserve capacity for at least size elements.

fp::raii::dynarray<int> arr;

// Reserve and chain operations
arr.reserve(1000)
   .push_back(42);
arr.push_back(99);

// No reallocations for next 998 elements

Parameters:

size – Minimum capacity to reserve

Returns:

Reference to this array for chaining

inline Derived &grow(size_t to_add, const T &value = {})#

Grow array by adding initialized elements.

fp::raii::dynarray<int> arr;
arr.push_back(1);
arr.push_back(2);

// Add 5 elements, all initialized to 99
arr.grow(5, 99);

assert(arr.size() == 7);
assert(arr[2] == 99);

Parameters:
  • to_add – Number of elements to add

  • value – Value to initialize new elements with (default-constructed if omitted)

Returns:

Reference to this array for chaining

inline Derived &grow_uninitialized(size_t to_add)#

Grow array with uninitialized elements.

New elements are uninitialized - you must initialize them before use. Useful for performance when you’ll immediately overwrite values.

fp::raii::dynarray<int> arr;
arr.push_back(1);

size_t old_size = arr.size();
arr.grow_uninitialized(10);

// Initialize new elements
for(size_t i = old_size; i < arr.size(); i++)
    arr[i] = i * 10;
Parameters:

to_add – Number of elements to add

Returns:

Reference to this array for chaining

inline Derived &grow_to_size(size_t size, const T &value = {})#

Grow to exact size with initialized elements.

fp::raii::dynarray<float> data;
data.grow_to_size(100, 3.14f);

assert(data.size() == 100);
for(auto& val : data)
    assert(val == 3.14f);

Note

If size is smaller than the current size, this function does nothing!

Parameters:
  • size – Target size

  • value – Value to initialize new elements with (default-constructed if omitted)

Returns:

Reference to this array for chaining

inline Derived &grow_to_size_uninitialized(size_t size)#

Grow to exact size with uninitialized elements.

Parameters:

size – Target size

Returns:

Reference to this array for chaining

inline T &push_back(const T &value)#

Add element to end of array.

fp::raii::dynarray<std::string> names;
names.push_back("Alice");
names.push_back("Bob");

std::string& ref = names.push_back("Charlie");
ref += " Smith";  // Modify in place

assert(names[2] == "Charlie Smith");

Parameters:

value – Value to add

Returns:

Reference to the newly added element

template<typename ...Targs>
inline T &emplace_back(Targs... args)#

Construct element in-place at end of array.

Constructs the element directly in the array without copying/moving.

struct Point {
    int x, y;
    Point(int x_, int y_) : x(x_), y(y_) {}
};

fp::raii::dynarray<Point> points;

// Construct directly in array
points.emplace_back(10, 20);
points.emplace_back(30, 40);

assert(points[0].x == 10);
Template Parameters:

Targs – Constructor argument types

Parameters:

args – Constructor arguments

Returns:

Reference to the newly constructed element

inline T &pop_back_n(size_t count)#

Remove last n elements.

fp::raii::dynarray<int> arr;
for(int i = 0; i < 10; i++) arr.push_back(i);

arr.pop_back_n(3);
assert(arr.size() == 7);

Parameters:

count – Number of elements to remove

Returns:

Reference to first element removed (one after the new last element)

inline T &pop_back()#

Remove last element.

fp::raii::dynarray<int> stack;
stack.push_back(10);
stack.push_back(20);
stack.push_back(30);

stack.pop_back();
assert(stack.size() == 2);
assert(stack.back() == 20);

Returns:

Reference to removed element (now invalid)

inline T &pop_back_to_size(size_t size)#

Pop elements until size reaches target.

fp::raii::dynarray<int> arr;
for(int i = 0; i < 100; i++) arr.push_back(i);

arr.pop_back_to_size(50);
assert(arr.size() == 50);

Parameters:

size – Target size

Returns:

Reference to first element removed (one after the new last element)

inline T &insert(size_t pos, const T &value = {})#

Insert element at position.

Elements after pos are shifted right. O(n) operation.

fp::raii::dynarray<int> arr;
arr.push_back(1);
arr.push_back(2);
arr.push_back(4);

arr.insert(2, 3);
// arr: [1, 2, 3, 4]

assert(arr[2] == 3);
Parameters:
  • pos – Position to insert at

  • value – Value to insert (default-constructed if omitted)

Returns:

Reference to inserted element

template<typename ...Targs>
inline T &emplace(size_t pos, Targs... args)#

Construct element in-place at position.

Elements after pos are shifted right. O(n) operation.

struct Widget {
    int id;
    std::string name;
    Widget(int i, std::string n) : id(i), name(std::move(n)) {}
};

fp::raii::dynarray<Widget> widgets;
widgets.emplace_back(1, "First");
widgets.emplace_back(3, "Third");

widgets.emplace(1, 2, "Second");
// widgets: [(1,"First"), (2,"Second"), (3,"Third")]
Template Parameters:

Targs – Constructor argument types

Parameters:
  • pos – Position to insert at

  • args – Constructor arguments

Returns:

Reference to inserted element

inline T &push_front(const T &value = {})#

Insert element at front.

All current elements are shifted right. O(n) operation.

fp::raii::dynarray<int> deque;
deque.push_front(3);
deque.push_front(2);
deque.push_front(1);

// deque: [1, 2, 3]
assert(deque[0] == 1);
Parameters:

value – Value to insert

Returns:

Reference to inserted element

template<typename ...Targs>
inline T &emplace_front(Targs... args)#

Construct element in-place at front.

All current elements are shifted right. O(n) operation.

fp::raii::dynarray<std::string> list;
list.emplace_front("World");
list.emplace_front("Hello");

// list: ["Hello", "World"]
Template Parameters:

Targs – Constructor argument types

Parameters:

args – Constructor arguments

Returns:

Reference to newly constructed element

inline view<T> insert_uninitialized(size_t pos, size_t count = 1)#

Insert uninitialized elements.

Elements after pos are shifted right. O(n) operation.

fp::dynarray<int> arr;
arr.push_back(1);
arr.push_back(5);

auto view = arr.insert_uninitialized(1, 3);
view[0] = 2;
view[1] = 3;
view[2] = 4;

// arr: [1, 2, 3, 4, 5]
Parameters:
  • pos – Position to insert at

  • count – Number of elements to insert (default: 1)

Returns:

View over the inserted uninitialized elements

inline Derived &delete_range(size_t pos, size_t count)#

Delete range of elements.

Elements after pos + count are shifted left. O(n) operation.

fp::raii::dynarray<int> arr;
for(int i = 0; i < 10; i++) arr.push_back(i);

arr.delete_range(3, 4)
   .push_back(99);

assert(arr.size() == 7);
Parameters:
  • pos – Starting position

  • count – Number of elements to delete

Returns:

Reference to this array for chaining

inline Derived &delete_(size_t pos)#

Delete single element.

Elements after pos are shifted left. O(n) operation.

fp::raii::dynarray<int> arr;
arr.push_back(1).push_back(2).push_back(3);

arr.delete_(1);
// arr: [1, 3]
Parameters:

pos – Position to delete

Returns:

Reference to this array for chaining

inline Derived &remove(size_t pos)#

Alias for delete_.

inline Derived &delete_start_end(size_t start, size_t end)#

Delete range using start and end indices.

Elements after end are shifted left. O(n) operation.

Parameters:
  • start – Starting index (inclusive)

  • end – Ending index (inclusive)

Returns:

Reference to this array for chaining

inline Derived &shrink_delete_range(size_t pos, size_t count)#

Delete range and shrink capacity.

Like delete_range but also reallocates to free unused capacity. All elements are copied. O(n) operation.

Parameters:
  • pos – Starting position

  • count – Number of elements to delete

Returns:

Reference to this array for chaining

inline Derived &shrink_delete(size_t pos)#

Delete element and shrink capacity.

Like delete_ but also reallocates to free unused capacity. All elements are copied. O(n) operation.

Parameters:

pos – Position to delete

Returns:

Reference to this array for chaining

inline Derived &shrink_delete_start_end(size_t start, size_t end)#

Delete range and shrink using start/end indices.

Like delete_start_end but also reallocates to free unused capacity. All elements are copied. O(n) operation.

Parameters:
  • start – Starting index (inclusive)

  • end – Ending index (inclusive)

Returns:

Reference to this array for chaining

inline Derived &resize(size_t size)#

Resize array to new size.

fp::raii::dynarray<int> arr;
for(int i = 0; i < 10; i++) arr.push_back(i);

arr.resize(20);  // Grow
assert(arr.size() == 20);

arr.resize(5);   // Shrink
assert(arr.size() == 5);

Parameters:

size – New size

Returns:

Reference to this array for chaining

inline Derived &shrink_to_fit()#

Shrink capacity to match size.

fp::raii::dynarray<int> arr;
arr.reserve(1000);
for(int i = 0; i < 100; i++) arr.push_back(i);

assert(arr.capacity() == 1000);

arr.shrink_to_fit();
assert(arr.capacity() == 100);

Returns:

Reference to this array for chaining

inline Derived &swap_range(size_t start1, size_t start2, size_t count)#

Swap two ranges of elements.

fp::raii::dynarray<int> arr;
for(int i = 0; i < 10; i++) arr.push_back(i);

arr.swap_range(0, 7, 3);
// Swapped [0,1,2] with [7,8,9]

Parameters:
  • start1 – Start of first range

  • start2 – Start of second range

  • count – Number of elements to swap

Returns:

Reference to this array for chaining

inline Derived &swap(size_t pos1, size_t pos2)#

Swap two elements.

fp::raii::dynarray<int> arr = {10, 20, 30};
arr.swap(0, 2);
// arr: [30, 20, 10]

Parameters:
  • pos1 – First position

  • pos2 – Second position

Returns:

Reference to this array for chaining

inline Derived &swap_delete_range(size_t pos, size_t count)#

Swap range to end and delete.

O(1) deletion that doesn’t preserve order.

fp::dynarray<int> arr;
for(int i = 0; i < 10; i++) arr.push_back(i);

arr.swap_delete_range(2, 2);
// Deleted elements at 2-3 by swapping with end
assert(arr.size() == 8);
Parameters:
  • pos – Starting position

  • count – Number of elements

Returns:

Reference to this array for chaining

inline Derived &swap_delete(size_t pos)#

Swap element to end and delete.

Fast O(1) unordered deletion.

fp::dynarray<int> arr = {0, 10, 20, 30, 40};
arr.swap_delete(1);
// arr: [0, 40, 20, 30]  (40 moved to position 1)
Parameters:

pos – Position to delete

Returns:

Reference to this array for chaining

inline Derived &swap_delete_start_end(size_t start, size_t end)#

Swap range to end and delete using start/end indices.

O(1) deletion that doesn’t preserve order.

Parameters:
  • start – Starting index (inclusive)

  • end – Ending index (inclusive)

Returns:

Reference to this array for chaining

inline T &pop_front()#

Remove and return first element.

Shifts all elements left. O(n) operation.

fp::dynarray<int> queue;
queue.push_back(1).push_back(2).push_back(3);

queue.pop_front();
// queue: [2, 3]
Returns:

Reference to removed element (now invalid)

inline Derived &clear()#

Clear all elements (keeps capacity)

Calls destructors for non-trivial types.

fp::raii::dynarray<std::string> strings;
strings.push_back("Hello");
strings.push_back("World");

assert(strings.size() == 2);

strings.clear();

assert(strings.size() == 0);
assert(strings.capacity() > 0);  // Capacity unchanged
Returns:

Reference to this array for chaining

inline Derived &concatenate_view_in_place(const view<const T> view)#

Concatenate view to this array in place.

fp::raii::dynarray<int> arr;
arr.push_back(1);
arr.push_back(2);

int more[] = {3, 4, 5};
arr.concatenate_view_in_place({more, 3});
// arr: [1, 2, 3, 4, 5]

Parameters:

view – View to append

Returns:

Reference to this array for chaining

inline Derived concatenate_view(const view<const T> view) const#

Concatenate view (returns new array)

fp::raii::dynarray<int> arr;
arr.push_back(1);
arr.push_back(2);

int more[] = {3, 4, 5};
fp::raii::dynarray<int> combined = arr.concatenate_view({more, 3});
// combined: [1, 2, 3, 4, 5]

Parameters:

view – View to append

Returns:

New array with concatenated data

inline Derived &concatenate_in_place(const Derived &other)#

Concatenate other array to this one in place.

fp::raii::dynarray<int> arr1, arr2;
arr1.push_back(1);
arr1.push_back(2);
arr2.push_back(3);
arr2.push_back(4);

arr1.concatenate_in_place(arr2);
// arr1: [1, 2, 3, 4]
// arr2: [3, 4] (unchanged)

Parameters:

other – Array to append

Returns:

Reference to this array for chaining

inline Derived concatenate(const Derived &other) const#

Concatenate two arrays (returns new array)

fp::dynarray<int> arr1, arr2;
arr1.push_back(1);
arr1.push_back(2);
arr2.push_back(3);
arr2.push_back(4);

fp::dynarray<int> combined = arr1.concatenate(arr2);
// combined: [1, 2, 3, 4]
// arr1 and arr2 unchanged

Parameters:

other – Array to append

Returns:

New array with concatenated data

Public Members

T *raw#

Raw pointer to data.

template<typename Derived>
struct string_crtp : public fp::string_crtp_common<Derived, Derived>#

Subclassed by fp::string

template<typename Derived, typename Dynamic>
struct string_crtp_common#

Subclassed by fp::string_crtp< string >, fp::string_crtp< Derived >, fp::wrapped::string

struct string_view : public fp::view<char>#

Unnamed Group

inline char *data()#

Get pointer to the data.

fp::auto_free arr = fp::malloc<int>(10);
fp::view<int> v = arr.view_full();

int* ptr = v.data();
ptr[0] = 42;  // Direct pointer access

// Pass to C functions
qsort(v.data(), v.size(), sizeof(int), compare_func);

Unnamed Group

inline view<std::byte> byte_view()#

Create a byte view of this view’s memory.

struct Point { int x, y; };
fp::auto_free points = fp::malloc<Point>(10);
fp::view<Point> v = points.view_full();

// Access raw bytes
fp::view<std::byte> bytes = v.byte_view();
assert(bytes.size() == 10 * sizeof(Point));

// Useful for serialization
void write_binary(fp::view<std::byte> data) {
    fwrite(data.data(), 1, data.size(), file);
}

write_binary(v.byte_view());

Returns:

View of the underlying bytes

Unnamed Group

inline char &operator*()#

Dereference operator - access first element.

Returns:

Reference to first element

Unnamed Group

inline char *operator->()#

Arrow operator - access first element’s members.

Returns:

Pointer to first element

Unnamed Group

inline char &operator[](size_t i)#

Subscript operator - access element by index.

Parameters:

i – Index

Returns:

Reference to element at index

Public Functions

inline operator view<std::add_const_t<char>>() const#

Implicit conversion to const view.

Allows passing non-const views to functions expecting const views.

inline struct __fp_view &raw()#

Get the underlying C-style view.

fp::view<int> v = ...;
fp_view(int)& c_style = v.raw();
// Can now use C-style fp_view functions
inline size_t length() const#

Get the number of elements in the view.

fp::auto_free arr = fp::malloc<int>(100);
fp::view<int> v1 = arr.view(0, 50);
fp::view<int> v2 = arr.view(50, 50);

assert(v1.length() == 50);
assert(v2.length() == 50);
assert(v1.length() + v2.length() == arr.size());
inline size_t size() const#

Get the number of elements in the view (alias for length)

inline bool empty() const#

Check if the view is empty.

fp::auto_free arr = fp::malloc<int>(10);

fp::view<int> full = arr.view_full();
assert(!full.empty());

fp::view<int> empty = arr.view(5, 0);
assert(empty.empty());

if(!full.empty())
    full[0] = 42;  // Safe to access
inline view<char> subview(size_t start, size_t length) const#

Create a subview (const version)

inline view<char> subview(size_t start) const#

Create a subview from start to end (const version)

inline view<char> subview_max_size(size_t start, size_t length) const#

Create a subview with max size (const version)

inline view<char> subview_start_end(size_t start, size_t end) const#

Create a subview with start/end indices (const version)

inline char *begin()#

Get iterator to beginning.

fp::auto_free arr = fp::malloc<int>(10);
fp::view<int> v = arr.view_full();

// Manual iteration
for(int* it = v.begin(); it != v.end(); ++it)
    *it = 42;

// Use with STL algorithms
std::sort(v.begin(), v.end());
std::reverse(v.begin(), v.end());
inline char *end()#

Get iterator to end.

fp::auto_free arr = fp::malloc<int>(100);
fp::view<int> v = arr.view_full();

// Find first element > 50
auto it = std::find_if(v.begin(), v.end(),
                       [](int x) { return x > 50; });

if(it != v.end())
    std::cout << "Found: " << *it << "\n";
inline size_t compare(const view other) const#

Compare this view with another.

fp::auto_free arr1 = fp::malloc<int>(5);
fp::auto_free arr2 = fp::malloc<int>(5);
arr1.fill(1);
arr2.fill(2);

fp::view<int> v1 = arr1.view_full();
fp::view<int> v2 = arr2.view_full();

auto result = v1.compare(v2);
if(result < 0) std::cout << "v1 < v2\n";
else if(result > 0) std::cout << "v1 > v2\n";
else std::cout << "v1 == v2\n";

Parameters:

other – View to compare with

Returns:

Comparison result (negative, 0, or positive)

inline std::strong_ordering operator<=>(const view o) const#

Three-way comparison operator.

fp::auto_free arr1 = fp::malloc<int>(5);
fp::auto_free arr2 = fp::malloc<int>(5);
arr1.fill(10);
arr2.fill(20);

fp::view<int> v1 = arr1.view_full();
fp::view<int> v2 = arr2.view_full();

if(v1 < v2) std::cout << "v1 is less\n";
if(v1 <= v2) std::cout << "v1 is less or equal\n";
if(v1 > v2) std::cout << "v1 is greater\n";

Parameters:

o – View to compare with

Returns:

std::strong_ordering result

inline bool operator==(const view o) const#

Equality comparison.

fp::auto_free arr1 = fp::malloc<int>(5);
fp::auto_free arr2 = fp::malloc<int>(5);
arr1.fill(42);
arr2.fill(42);

fp::view<int> v1 = arr1.view_full();
fp::view<int> v2 = arr2.view_full();

if(v1 == v2)
    std::cout << "Views contain identical data\n";

Parameters:

o – View to compare with

Returns:

true if views are equal

Public Static Functions

static inline view make(char *p, size_t start, size_t length)#

Create a view from a fat pointer with start index and length.

fp::auto_free arr = fp::malloc<int>(100);

// View elements 10-29 (20 elements starting at index 10)
fp::view<int> v = fp::view<int>::make(arr.data(), 10, 20);
assert(v.size() == 20);

// Modify through view
for(size_t i = 0; i < v.size(); i++) {
    v[i] = 999;
}

assert(arr[10] == 999);  // Original array modified

Parameters:
  • p – Fat pointer

  • start – Starting index

  • length – Number of elements

Returns:

View over the specified range

static inline view make_full(char *p)#

Create a view over an entire fat pointer.

fp::auto_free data = fp::malloc<float>(50);
data.fill(3.14f);

// Create view of entire array
fp::view<float> full = fp::view<float>::make_full(data.data());
assert(full.size() == 50);

// View references original data
full[0] = 2.71f;
assert(data[0] == 2.71f);

Parameters:

p – Fat pointer

Returns:

View covering all elements

static inline view make_start_end(char *p, size_t start, size_t end)#

Create a view using start and end indices.

fp::auto_free arr = fp::malloc<int>(100);

// View elements 20 through 30 (inclusive) - 11 elements total
fp::view<int> v = fp::view<int>::make_start_end(arr.data(), 20, 30);
assert(v.size() == 11);

v.fill(42);
assert(arr[20] == 42);
assert(arr[30] == 42);

Parameters:
  • p – Fat pointer

  • start – Starting index (inclusive)

  • end – Ending index (inclusive)

Returns:

View over the range [start, end]

static inline view from_variable(char &var)#

Create a view from a single variable.

int x = 42;
fp::view<int> v = fp::view<int>::from_variable(x);

assert(v.size() == 1);
assert(v[0] == 42);

v[0] = 99;
assert(x == 99);  // Variable modified through view

// Useful for generic functions
template<typename T>
void process_view(fp::view<T> data) {
    for(auto& val : data) val *= 2;
}

int single = 10;
process_view(fp::view<int>::from_variable(single));
assert(single == 20);

Parameters:

var – Variable to view

Returns:

View with size 1

template<typename T> fp::fp::view : public struct __fp_view
#include <pointer.hpp>

Non-owning view (fp::view) over a contiguous sequence of elements.

A view is a lightweight, non-owning reference to a contiguous block of memory. It stores a pointer and size but does not manage the lifetime of the data. Views support subviews, iteration, and conversion to/from std::span (if previously included).

fp::auto_free arr = fp::malloc<int>(100);

// Create view of entire array
fp::view<int> full = fp::view<int>::make_full(arr.data());

// Create view of subset
fp::view<int> partial = full.subview(10, 20);

// Iterate
for(int& val : partial)
    val = 2;

// Access elements
partial[0] = 999;
Template Parameters:

T – Element type

Unnamed Group

inline T *data()#

Get pointer to the data.

fp::auto_free arr = fp::malloc<int>(10);
fp::view<int> v = arr.view_full();

int* ptr = v.data();
ptr[0] = 42;  // Direct pointer access

// Pass to C functions
qsort(v.data(), v.size(), sizeof(int), compare_func);

Unnamed Group

inline view<std::byte> byte_view()#

Create a byte view of this view’s memory.

struct Point { int x, y; };
fp::auto_free points = fp::malloc<Point>(10);
fp::view<Point> v = points.view_full();

// Access raw bytes
fp::view<std::byte> bytes = v.byte_view();
assert(bytes.size() == 10 * sizeof(Point));

// Useful for serialization
void write_binary(fp::view<std::byte> data) {
    fwrite(data.data(), 1, data.size(), file);
}

write_binary(v.byte_view());

Returns:

View of the underlying bytes

Unnamed Group

inline T &operator*()#

Dereference operator - access first element.

Returns:

Reference to first element

Unnamed Group

inline T *operator->()#

Arrow operator - access first element’s members.

Returns:

Pointer to first element

Unnamed Group

inline T &operator[](size_t i)#

Subscript operator - access element by index.

Parameters:

i – Index

Returns:

Reference to element at index

Public Functions

inline operator view<std::add_const_t<T>>() const#

Implicit conversion to const view.

Allows passing non-const views to functions expecting const views.

inline struct __fp_view &raw()#

Get the underlying C-style view.

fp::view<int> v = ...;
fp_view(int)& c_style = v.raw();
// Can now use C-style fp_view functions
inline size_t length() const#

Get the number of elements in the view.

fp::auto_free arr = fp::malloc<int>(100);
fp::view<int> v1 = arr.view(0, 50);
fp::view<int> v2 = arr.view(50, 50);

assert(v1.length() == 50);
assert(v2.length() == 50);
assert(v1.length() + v2.length() == arr.size());
inline size_t size() const#

Get the number of elements in the view (alias for length)

inline bool empty() const#

Check if the view is empty.

fp::auto_free arr = fp::malloc<int>(10);

fp::view<int> full = arr.view_full();
assert(!full.empty());

fp::view<int> empty = arr.view(5, 0);
assert(empty.empty());

if(!full.empty())
    full[0] = 42;  // Safe to access
inline view subview(size_t start, size_t length)#

Create a subview with specified start and length.

fp::auto_free arr = fp::malloc<int>(100);
fp::view<int> v = arr.view_full();

// Create subview of elements 10-29
fp::view<int> sub = v.subview(10, 20);
assert(sub.size() == 20);

// Subviews can be chained
fp::view<int> subsub = sub.subview(5, 10);
assert(subsub.size() == 10);

// All views reference the same data
subsub[0] = 999;
assert(arr[15] == 999);  // 10 + 5 = index 15 in original

Parameters:
  • start – Starting index

  • length – Number of elements

Returns:

Subview of this view

inline view subview_max_size(size_t start, size_t length)#

Create a subview with maximum available length.

fp::auto_free arr = fp::malloc<int>(100);
fp::view<int> v = arr.view_full();

// Request 30 elements starting at index 80
// Only 20 available, so view will have 20 elements
fp::view<int> clamped = v.subview_max_size(80, 30);
assert(clamped.size() == 20);  // Clamped to available size

// Useful when you don't know exact bounds
for(size_t i = 0; i < v.size(); i += 25) {
    fp::view<int> chunk = v.subview_max_size(i, 25);
    process_chunk(chunk);  // Always valid, size may vary
}

Parameters:
  • start – Starting index

  • length – Maximum number of elements (clamped to available size)

Returns:

Subview of this view

inline view subview(size_t start)#

Create a subview from start to end of view.

fp::pointer<int> arr = fp::malloc<int>(100);
fp::view<int> v = arr.view_full();

// Get everything from index 50 to end
fp::view<int> tail = v.subview(50);
assert(tail.size() == 50);

// Useful for skipping a header
struct Header { int magic; size_t count; };
fp::pointer<char> buffer = fp::malloc<char>(1024);
fp::view<char> v_buf = buffer.view_full();

// Skip header and get remaining data
fp::view<char> data = v_buf.subview(sizeof(Header));

buffer.free();
arr.free();

Parameters:

start – Starting index

Returns:

Subview from start to end

inline view subview_start_end(size_t start, size_t end)#

Create a subview using start and end indices.

fp::auto_free arr = fp::malloc<int>(100);
fp::view<int> v = arr.view_full();

// Elements 20 through 30 (inclusive) - 11 elements
fp::view<int> sub = v.subview_start_end(20, 30);
assert(sub.size() == 11);

// Split view into thirds
size_t third = v.size() / 3;
fp::view<int> first = v.subview_start_end(0, third - 1);
fp::view<int> second = v.subview_start_end(third, 2 * third - 1);
fp::view<int> third_part = v.subview_start_end(2 * third, v.size() - 1);

Parameters:
  • start – Starting index (inclusive)

  • end – Ending index (inclusive)

Returns:

Subview over the range [start, end]

inline view<T> subview(size_t start, size_t length) const#

Create a subview (const version)

inline view<T> subview_max_size(size_t start, size_t length) const#

Create a subview with max size (const version)

inline view<T> subview(size_t start) const#

Create a subview from start to end (const version)

inline view<T> subview_start_end(size_t start, size_t end) const#

Create a subview with start/end indices (const version)

inline T *begin()#

Get iterator to beginning.

fp::auto_free arr = fp::malloc<int>(10);
fp::view<int> v = arr.view_full();

// Manual iteration
for(int* it = v.begin(); it != v.end(); ++it)
    *it = 42;

// Use with STL algorithms
std::sort(v.begin(), v.end());
std::reverse(v.begin(), v.end());
inline T *end()#

Get iterator to end.

fp::auto_free arr = fp::malloc<int>(100);
fp::view<int> v = arr.view_full();

// Find first element > 50
auto it = std::find_if(v.begin(), v.end(),
                       [](int x) { return x > 50; });

if(it != v.end())
    std::cout << "Found: " << *it << "\n";
inline size_t compare(const view other) const#

Compare this view with another.

fp::auto_free arr1 = fp::malloc<int>(5);
fp::auto_free arr2 = fp::malloc<int>(5);
arr1.fill(1);
arr2.fill(2);

fp::view<int> v1 = arr1.view_full();
fp::view<int> v2 = arr2.view_full();

auto result = v1.compare(v2);
if(result < 0) std::cout << "v1 < v2\n";
else if(result > 0) std::cout << "v1 > v2\n";
else std::cout << "v1 == v2\n";

Parameters:

other – View to compare with

Returns:

Comparison result (negative, 0, or positive)

inline std::strong_ordering operator<=>(const view o) const#

Three-way comparison operator.

fp::auto_free arr1 = fp::malloc<int>(5);
fp::auto_free arr2 = fp::malloc<int>(5);
arr1.fill(10);
arr2.fill(20);

fp::view<int> v1 = arr1.view_full();
fp::view<int> v2 = arr2.view_full();

if(v1 < v2) std::cout << "v1 is less\n";
if(v1 <= v2) std::cout << "v1 is less or equal\n";
if(v1 > v2) std::cout << "v1 is greater\n";

Parameters:

o – View to compare with

Returns:

std::strong_ordering result

inline bool operator==(const view o) const#

Equality comparison.

fp::auto_free arr1 = fp::malloc<int>(5);
fp::auto_free arr2 = fp::malloc<int>(5);
arr1.fill(42);
arr2.fill(42);

fp::view<int> v1 = arr1.view_full();
fp::view<int> v2 = arr2.view_full();

if(v1 == v2)
    std::cout << "Views contain identical data\n";

Parameters:

o – View to compare with

Returns:

true if views are equal

Public Static Functions

static inline view make(T *p, size_t start, size_t length)#

Create a view from a fat pointer with start index and length.

fp::auto_free arr = fp::malloc<int>(100);

// View elements 10-29 (20 elements starting at index 10)
fp::view<int> v = fp::view<int>::make(arr.data(), 10, 20);
assert(v.size() == 20);

// Modify through view
for(size_t i = 0; i < v.size(); i++) {
    v[i] = 999;
}

assert(arr[10] == 999);  // Original array modified

Parameters:
  • p – Fat pointer

  • start – Starting index

  • length – Number of elements

Returns:

View over the specified range

static inline view make_full(T *p)#

Create a view over an entire fat pointer.

fp::auto_free data = fp::malloc<float>(50);
data.fill(3.14f);

// Create view of entire array
fp::view<float> full = fp::view<float>::make_full(data.data());
assert(full.size() == 50);

// View references original data
full[0] = 2.71f;
assert(data[0] == 2.71f);

Parameters:

p – Fat pointer

Returns:

View covering all elements

static inline view make_start_end(T *p, size_t start, size_t end)#

Create a view using start and end indices.

fp::auto_free arr = fp::malloc<int>(100);

// View elements 20 through 30 (inclusive) - 11 elements total
fp::view<int> v = fp::view<int>::make_start_end(arr.data(), 20, 30);
assert(v.size() == 11);

v.fill(42);
assert(arr[20] == 42);
assert(arr[30] == 42);

Parameters:
  • p – Fat pointer

  • start – Starting index (inclusive)

  • end – Ending index (inclusive)

Returns:

View over the range [start, end]

static inline view from_variable(T &var)#

Create a view from a single variable.

int x = 42;
fp::view<int> v = fp::view<int>::from_variable(x);

assert(v.size() == 1);
assert(v[0] == 42);

v[0] = 99;
assert(x == 99);  // Variable modified through view

// Useful for generic functions
template<typename T>
void process_view(fp::view<T> data) {
    for(auto& val : data) val *= 2;
}

int single = 10;
process_view(fp::view<int>::from_variable(single));
assert(single == 20);

Parameters:

var – Variable to view

Returns:

View with size 1

Indices and tables#