Welcome to LibFP’s Documentorial!#

This documentation page is designed less like a direct reference and more like a tutorial.
If you would instead prefer a reference… full generated documentation can be found here.

LibFP pointers are designed to be fully compliant C pointers.

int* x = fp_malloc(int, 10);
fp_free_and_null(x);

Additionally we provide a C++ wrapper around our C apis:

template<typename T>
struct pointer : public fp::wrapped::pointer<T>, public fp::pointer_crtp<T, pointer<T>>

Main fat pointer class 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_and_null(); // Preferred over .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_and_null(); // ERROR: Using 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_and_null();

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()#

Free the allocated memory.

fp::pointer<int> arr = fp::malloc<int>(10);
arr.free();
// arr is now invalid - do not use!

Warning

Pointer becomes invalid after this call. Accessing it is undefined behavior. Consider using free_and_null() for safer cleanup.

inline void free_and_null()#

Free the allocated memory and set pointer to null.

Safer than free() as it prevents use-after-free by nulling the pointer.

fp::pointer<int> arr = fp::malloc<int>(10);
arr.free_and_null();
assert(arr.data() == nullptr);
assert(!arr);  // Boolean conversion returns false
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_and_null();
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_and_null();
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.

If desirable there is also a stack allocated version (comparable to std::array):

template<typename T, size_t N>
struct array : public fp::pointer_crtp<T, array<T, N>>

Fixed-size 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 Integration#

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.

Indices and Tables#