Skip to content
Gustav Louw edited this page Jan 13, 2021 · 6 revisions
#include <lst.h>

The CTL lst, analogous to the STL std::list, is a container specializing in element pointer validity. Implemented as a doubly linked list, a lst may be used in place of a vec when all elements are consistently accessed and modified in batch operations (eg. with foreach) or on the head and tail. While slower to iterate than the <vec.h> template, pointers to list elements remain valid as the list grows and shrinks in size, and when the list is sorted.

Example 1: Storing, Sorting, and Summing Random Integers

Similar to Example 1 of the vec tutorial, occurrences of vec have been swapped for lst. The same structure holds, except that per element access is not possible.

Like the vec example, #define P indicates no further freeing is required per lst element.

#include <stdio.h>

#define P
#define T int
#include <lst.h>

int compare(int* a, int* b)
{
    return *b < *a;
}

int main(void)
{
    lst_int a = lst_int_init();
    for(int i = 0; i < 32; i++)
        lst_int_push_back(&a, rand() % 1024);
    lst_int_sort(&a, compare);
    int sum = 0;
    int index = 0;
    foreach(lst_int, &a, it)
    {
        printf("%2d: %4d\n", index, *it.ref);
        sum += *it.ref;
        index += 1;
    }
    printf("%d\n", sum);
    lst_int_free(&a);
}
gcc main.c -I ctl

Example 2: Storing and Sorting 2D Game Entities

Game engines specializing in large scale medieval combat benefit from using a lst when batch operations are consistently modifying elements in the middle of the list. For example, "dead" entities can be removed from the lst with a single foreach statement, where each removal is an O(1) operation:

#include <stdio.h>
#include <str.h>

typedef struct
{
    float x;
    float y;
}
point;

typedef struct
{
    str name;
    point p;
    float health;
    float attack;
    float defense;
}
entity;

void
entity_free(entity* e)
{
    str_free(&e->name);
}

entity
entity_copy(entity* e)
{
    entity other = *e; 
    other.name = str_copy(&e->name);
    return other;
}

#define T entity
#include <lst.h>

int is_dead(entity* e)
{
    return e->health < 2.0f;
}

int main(void)
{
    lst_entity a = lst_entity_init();
    for(int i = 0; i < 64; i++)
    {   
        point p = { 
            32.0f * rand() / RAND_MAX,
            32.0f * rand() / RAND_MAX,
        };  
        entity e = {
            str_init("blue slime"),
            p,
            rand() % 10, 
            rand() % 10,
            rand() % 10,
        };  
        lst_entity_push_back(&a, e); 
    }   
    size_t before = a.size;
    lst_entity_remove_if(&a, is_dead);
    foreach(lst_entity, &a, it)
    {
        entity* e = it.ref;
        printf("%s : {%5.2f %5.2f} %5.2f %5.2f %5.2f\n",
                str_c_str(&e->name), e->p.x, e->p.y, e->health, e->attack, e->defense);
    }  
    printf("size before: %lu\n", before);
    printf("size after: %lu\n", a.size);
    lst_entity_free(&a);
}
gcc main.c -I ctl

Declarations for entity_free and entity_copy can replace their definitions, and the definitions can be placed elsewhere, preferably after main, or in another file, to ease template instantiation readability:

void
entity_free(entity*);

entity
entity_copy(entity*);

#define T entity
#include <lst.h>

int main(void)
{
    ...
}

void
entity_free(entity* e)
{
    str_free(&e->name);
}

entity
entity_copy(entity* e)
{
    entity other = *e; 
    other.name = str_copy(&e->name);
    return other;
}