A header-only container and algorithm template library in C.
Pottery is a collection of templates for instantiating strongly typed containers and container algorithms in C.
It's called "Pottery" because it doesn't exactly give you containers you can use out-of-the-box. It gives you the tools, the materials, the instructions to build them but there is still some assembly required. You have to instantiate these templates manually, and often you'll want to wrap one or more templates with your own interface to build a practical data structure.
Pottery is modern C code written in the ultra-portable intersection of C11, gnu89 and C++11 with no mandatory dependencies (not even libc.) Pottery supports many compilers including GCC, Clang, MSVC, TinyCC and more. It supports (or intends to support) any modern C platform from microcontrollers to OS kernels to WebAssembly.
Pottery does not use void pointer casts, function pointers, code block macros, compiler-dependent hacks, or any other inefficiencies or messyness of typical C containers. Pottery's templates are clean, composable, fast, strongly typed, and highly configurable.
- Features
- How It Works
- Examples
- Integration Guide
- Lifecycle Style
- Glossary
- Template Listing
- Meta-templates
- C++ Bindings
- Testing
- FAQ
Suppose you want a growable array of int
. Let's call it int_vector
. Instantiate it like this:
#define POTTERY_VECTOR_PREFIX int_vector
#define POTTERY_VECTOR_VALUE_TYPE int
#define POTTERY_VECTOR_LIFECYCLE_BY_VALUE 1
#include "pottery/vector/pottery_vector_static.t.h"
This gives you a type int_vector_t
:
int_vector_t vector;
int_vector_init(&vector);
int_vector_insert_first(&vector, 10);
int_vector_insert_last(&vector, 20);
int_vector_insert_at(&vector, 1, 15);
int_vector_remove_at(&vector, 0);
for (size_t i = 0; i < int_vector_count(&vector); ++i)
printf("%i\n", *int_vector_at(&vector, i));
int_vector_destroy(&vector);
See the full example here.
Suppose you want a map of string names to person pointers, i.e. const char*
to person_t*
:
typedef struct person_t {
char* name;
// other stuff
} person_t;
#define POTTERY_ARRAY_MAP_PREFIX person_map
#define POTTERY_ARRAY_MAP_KEY_TYPE const char*
#define POTTERY_ARRAY_MAP_COMPARE_THREE_WAY strcmp
#define POTTERY_ARRAY_MAP_VALUE_TYPE person_t*
#define POTTERY_ARRAY_MAP_REF_KEY(person) (*person)->name
#define POTTERY_ARRAY_MAP_LIFECYCLE_MOVE_BY_VALUE 1
#include "pottery/array_map/pottery_array_map_static.t.h"
This gives you a person_map_t
:
person_map_t map;
person_map_init(&map);
person_map_insert(&map, person_new("alice"));
person_map_insert(&map, person_new("bob"));
person_t** eve = person_map_find(&map, "eve");
if (!person_map_entry_exists(&map, eve)) {
// not found!
}
// for-each loop works on any container
person_t** ref;
POTTERY_FOR_EACH(ref, person_map, &map) {
printf("%s\n", (*ref)->name);
}
See the full example here.
Suppose you want to sort an array of C strings:
#define POTTERY_INTRO_SORT_PREFIX sort_strings
#define POTTERY_INTRO_SORT_VALUE_TYPE const char*
#define POTTERY_INTRO_SORT_LIFECYCLE_MOVE_BY_VALUE 1
#define POTTERY_INTRO_SORT_COMPARE_THREE_WAY(x, y) strcmp(*x, *y)
#include "pottery/intro_sort/pottery_intro_sort_static.t.h"
This gives us a function called sort_strings()
:
const char* players[] = {
"fred", "quincy", "alice", "eve", "zack", "ned", "paul", "bob", "gary",
"ursula", "yves", "carl", "olivia", "steve", "rob", "mike", "wade", "dave",
"jake", "helen", "xavier", "karen", "tammy", "laura", "isaac", "vick",
};
size_t count = sizeof(players) / sizeof(*players);
sort_strings(players, count);
for (size_t i = 0; i < count; ++i)
puts(players[i]);
See the full example here.
Suppose you want a growable priority queue of request pointers:
typedef struct request_t {
double priority;
// other stuff
} request_t;
#define POTTERY_PRIORITY_QUEUE_PREFIX request_queue
#define POTTERY_PRIORITY_QUEUE_VALUE_TYPE request_t*
#define POTTERY_PRIORITY_QUEUE_LIFECYCLE_MOVE_BY_VALUE 1
#define POTTERY_PRIORITY_QUEUE_COMPARE_GREATER(x, y) (*x)->priority > (*y)->priority
#include "pottery/priority_queue/pottery_priority_queue_static.t.h"
This gives us a type request_queue_t
:
request_queue_t queue;
request_queue_init(&queue);
// insert some requests
request_queue_insert(&queue, request_new(/*...*/));
request_queue_insert(&queue, request_new(/*...*/));
request_queue_insert(&queue, request_new(/*...*/));
// handle requests in priority order
while (!request_queue_is_empty(&queue))
request_handle(request_queue_extract_first(&queue));
request_queue_destroy(&queue);
See the priority_queue documentation for more.
There are more examples in the examples/
folder and many more that still need to be written. Have a look at what's there so far to learn more ways you can use Pottery.
In particular, take a look at Clayfish to see various uses of Pottery in a "real" application.