Latest commit: 78577ea by Karsten Pedersen on 2023-02-07
Message: Added 'using'. Similar to std::shared_ptr aliasing constructor.
$ git clone https://www.thamessoftware.co.uk/git/stent.git
By borrowing concepts from std::weak_ptr in C++ (or more closely wxWeakRef from wxWidgets), Memory errors in C can appear in a more deterministic manner without severely impacting the design of program code.
Rather than using a raw pointer, the developer uses a pointer wrapper which is guaranteed to return NULL as soon as the original data being pointed to is freed. Rather than return NULL, Stent instead displays a useful debug error and aborts the program, letting the developer know that there is an issue with the code.
Note: Stent is intended to be enabled only during debugging. For release builds, it can be disabled and the program will incur no additional costs to the performance at runtime.
There are a few considerations that a developer needs to make before using Stent. The following instructions should provide a brief overview of the steps required. However, a quick look through the src/tests/ref.c program should also explain these concepts.
Include the "stent.h" header file.
#define STENT_IMPLEMENTATION
#include <stent.h>
Note: Exactly one compilation unit must define STENT_IMPLEMENTATION before including the header in order for the implementation of Stent to be compiled correctly as a header-only library.
Declare a variable to store the pointer:
struct SomeStruct *someStruct = NULL; // Standard C
ref(SomeStruct) someStruct = NULL; // Using Stent
To dereference a pointer:
someStruct->someData = 6; // Standard C
_(someStruct).someData = 6; // Using Stent
To allocate dynamic memory:
someStruct = calloc(1, sizeof(*someStruct)); // Standard C
someStruct = allocate(SomeStruct); // Using Stent
To free dynamic memory:
free(someStruct); // Standard C
release(someStruct); // Using Stent
Note: When using Stent, any time you subsequently dereference the pointer using _(ptr), it will abort since the memory pointed to is no longer valid. This means that without checking, the program will reliably crash in the debug build.
Forward declaring a structure:
struct SomeStruct; // Same for both Standard C and Stent
Defining a structure containing a pointer:
struct Test
{
struct SomeStruct *someStruct; // Standard C
ref(SomeStruct) someStruct; // Using Stent
};
Pass pointer into function (Function prototypes):
void SomeStructProc(struct SomeStruct *someStruct); // Standard C
void SomeStructProc(ref(SomeStruct) someStruct); // Using Stent
Pass pointer into function (Calling function):
SomeStructProc(someStruct); // Same for both Standard C and Stent
Using a similar technique to ref(T) it has been possible for libstent to provide a container implementation, similar to the functionality provided by std::vector from C++. Specifically this provides the following features.
Note: Even if code safety is not your primary concern, the easy to use API of this implementation can greatly simplify the codebase and save on implementation time. For this reason, we have included a standalone version (vec.h). You do not require this if you are already using stent.h as it is already provided
Creating a vector (primitives and structures):
vector(int) v = vector_new(int);
vector(struct Foo) f = vector_new(struct Foo);
Adding elements to a vector:
vector_push(v, 8);
vector_push(v, 5);
vector_push(v, 12);
Accessing the second element of a vector:
printf("%i\n", vector_at(v, 1));
Accessing the size and iterating through elements:
size_t i;
for(i = 0; i < vector_size(v); ++i) { }
Erasing two elements from a vector, starting at the second element:
vector_erase(v, 1, 2);
Cleaning up a vector:
vector_delete(v);
Convenience MACRO for iterating:
foreach(int curr, v,
printf("%i\n", curr);
);
There is also basic support for leak detection. Have a look at the src/tests/leak.c program for more information.
For a simple example, if a leak exists, a message will appear such as:
***** Memory Leak *****
Type: struct Test
File: example/leak/main.c
Line: 14
This is possible because Stent creates an "atexit" hook that upon program termination, will scan internal memory for a list of data yet to be freed.