Skip to content

Instantly share code, notes, and snippets.

@mrtrizer
Last active June 2, 2022 11:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mrtrizer/235257cc6f1479bf1fdcc5ff2a9da0b6 to your computer and use it in GitHub Desktop.
Save mrtrizer/235257cc6f1479bf1fdcc5ff2a9da0b6 to your computer and use it in GitHub Desktop.
C++ style containers in C language with just few macro C99 (msvc, gcc, clang). Doesn't work in C++ mainly because of anonymous struct declaration in function arguments.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <assert.h>
// *******************
// CONTAINERS LIBRARY
// *******************
// template type
#define ARRAY(T) struct { size_t size; size_t capacity; T* data; }
// non template type
typedef ARRAY(char) String;
// function
static inline int ArrayRemove(String* array, size_t pos, const size_t itemSize)
{
if (pos >= array->size)
return 2;
for (size_t i = (pos + 1) * itemSize; i < array->size * itemSize; i++)
array->data[i - itemSize] = array->data[i];
array->size--;
return 0;
}
static inline int ArrayFind(ARRAY(char)* arr, void* (func)(void*, void*), void* arg, const size_t itemSize)
{
for (size_t i = 0; i < arr->size * itemSize; i += itemSize)
{
if (func(arr->data + i, arg))
return i / itemSize;
}
return -1;
}
static inline void ArrayClear(ARRAY(char)* arr)
{
arr->capacity = 0;
arr->size = 0;
free(arr->data);
}
static inline void* ArrayAdd(ARRAY(char)* array, const size_t itemSize)
{
if (array->capacity == array->size)
{
array->capacity += 100;
array->data = realloc(array->data, array->capacity * itemSize);
}
return array->data;
}
static inline int StringAdd(String* str1, String* str2)
{
str1->data = (char*)memcpy((char*)realloc(str1->data, str1->size + str2->capacity) + str1->size, str2->data, str2->capacity) - str1->size;
str1->size = str1->size + str2->size;
str1->capacity = str1->capacity + str2->size;
return 0;
}
// template type macro
// init returns anonymous type
#define ARRAY_INIT_CAPACITY(T, SIZE) { 0, SIZE, malloc(sizeof(T) * SIZE)}
#define ARRAY_INIT_ITEMS(COUNT, STATIC_ARRAY) { COUNT, COUNT, memcpy(malloc(COUNT * sizeof((STATIC_ARRAY)[0])), (STATIC_ARRAY), COUNT * sizeof((STATIC_ARRAY)[0]))}
#define ARRAY_INIT_ITEMS_V(COUNT, T, ...) { COUNT, COUNT, memcpy(malloc(COUNT * sizeof(((T[]) { __VA_ARGS__ })[0])), ((T[]) { __VA_ARGS__ }), COUNT * sizeof(T))}
#define ARRAY_INIT_COPY(ARR) {(ARR).size, (ARR).capacity, memcpy(malloc((ARR).capacity * sizeof((ARR).data[0])), (ARR).data, (ARR).size * sizeof((ARR).data[0]))}
#define ARRAY_CLEAR(ARR) ArrayClear((void*)(&ARR));
// return int result 0 - means success
#define ARRAY_ADD(ARR) ((ARR).data = (ArrayAdd((void*)&(ARR), sizeof((ARR).data[0]))))[(ARR).size++]
#define ARRAY_REMOVE(ARR, I) ArrayRemove((void*)&(ARR), I, sizeof((ARR).data[0]))
#define ARRAY_FIND(ARR, FUNC, ARG) ArrayFind((void*)&ARR, (void*)FUNC, (ARG), sizeof((ARR).data[0]))
// non-template type macro
// init returns named type
#define String_INIT_CSTR(STR) (String){sizeof(STR) - 1, sizeof(STR), strcpy(malloc(sizeof(STR)), STR)}
#define String_CLEAR(STR) ArrayClear((void*)(&ARR))
// return int result 0 - means success
#define String_ADD(STR1, STR2) StringAdd(&(STR1), &(STR2))
#define PAIR(KEY_T, VAL_T) struct { KEY_T key; VAL_T value;}
#define MAP(KEY_T, VAL_T) struct { ARRAY(PAIR(KEY_T, VAL_T)) array; void* find; }
typedef int (*FindFuncType)(void*, void*);
#define MAP_ADD(MAP, KEY) (MOVE(&ARRAY_ADD((MAP).array).key, &(KEY)), (MAP).array.data)[(MAP).array.size - 1].value
#define MAP_INIT(MAP, KEY, FIND) { ARRAY_INIT_CAPACITY(PAIR(MAP, KEY), 0), FIND }
#define MAP_FIND(MAP, KEY) ((FindFuncType)(MAP).find)(&(MAP).array, (KEY))
#define MAP_GET(MAP, INDEX) (MAP).array.data[(INDEX)].value
// Utilize inline functions to avoid calling overhead
#define ENABLE_OPTIMIZED_COMPARE(CMP) \
int CMP ## Optimized(ARRAY(char)* array, String* target, int itemSize) \
{\
for (size_t i = 0; i < array->size * itemSize; i += itemSize) \
{ \
void* value = &((PAIR(void*, void*)*)(array->data + i))->key;\
if (CMP((void*)value, (void*)target)) \
return i / itemSize; \
} \
return -1;\
}
// You can't just assign one container to another because they have two anonymous structure types
#define MOVE(TARGET_PTR, SOURCE_PTR) memcpy((void*)TARGET_PTR, (void*)SOURCE_PTR, sizeof(*TARGET_PTR))
// *****************
// EXAMPLE OF USAGE
// *****************
// Modify array in function
void Process(ARRAY(char)* a)
{
int startSize = a->size;
for (int i = 0; i < startSize; i++)
if (a->data[i] >= 'a' && a->data[i] <= 'z')
ARRAY_ADD(*a) = a->data[i] + 'A' - 'a';
for (int i = 0; i < 110; i++)
ARRAY_ADD(*a) = 'b';
}
// Example of copying inside function
void ProcessCopy(const ARRAY(char)* a)
{
ARRAY(char) copy = ARRAY_INIT_COPY(*a);
for (size_t i = 0; i < 110; i++)
ARRAY_ADD(copy) = 'b';
ARRAY_CLEAR(copy);
}
// We have no lambda in C, so please use the second parameter to reuse your compare functions.
bool Compare(char* c1, char* c2)
{
return *c1 == *c2;
}
// You should never pass arrays by value! This is not C++, there is no copy constructor!
void Print(String* str)
{
printf("%s\n", str->data);
}
static inline bool StringCompare(String* key, String* target)
{
return strcmp(key->data, target->data) == 0;
}
ENABLE_OPTIMIZED_COMPARE(StringCompare)
int main(void)
{
typedef struct MyStruct {
int a, b, c;
ARRAY(int) refs;
} MyStruct;
MAP(String, MyStruct) map = MAP_INIT(String, MyStruct, StringCompareOptimized);
MAP_ADD(map, String_INIT_CSTR("Key3")) = (MyStruct){ 1, 2, 3, ARRAY_INIT_CAPACITY(MyStruct, 0) };
MAP_ADD(map, String_INIT_CSTR("Key2")) = (MyStruct){ 1, 2, 3, ARRAY_INIT_ITEMS(3, ((int[]) { 1, 2, 3 })) };
MyStruct myStruct = { 0 };
myStruct.a = 10;
myStruct.b = 20;
myStruct.c = 30;
MOVE(&myStruct.refs, &(ARRAY(int)) ARRAY_INIT_ITEMS(3, ((int[]) { 10, 20, 30 })));
String s = String_INIT_CSTR("Key1");
MAP_ADD(map, s) = myStruct;
int index = StringCompareOptimized(&map.array, &String_INIT_CSTR("Key1"), sizeof(map.array.data[0]));
if (index != -1)
printf("%d %d\n", index, MAP_GET(map, index).a);
// Init string inplace
String str1 = String_INIT_CSTR("Hello, World 1!");
String str2 = String_INIT_CSTR("Hello, World 2!");
Print(&str1);
// Concat strings and put into a new string
String strSum = String_INIT_CSTR("");
String_ADD(strSum, str1);
String_ADD(strSum, str2);
// Print the concatenated string
Print(&strSum);
// Init array
//ARRAY(char) charArray = ARRAY_INIT_ITEMS(3, ((char[]) { '1', '2', '3' }));
ARRAY(char) charArray = ARRAY_INIT_ITEMS_V(3, char, '1', '2', '3' );
// Copy it!
ARRAY(char) charArrayCopy = ARRAY_INIT_COPY(charArray);
// Define type in order to use nested arrays
typedef ARRAY(int) MyIntArray;
// Init array of arrays!
ARRAY(MyIntArray) myIntArray = ARRAY_INIT_CAPACITY(MyIntArray, 5);
// Add another array into array (int[]) says to the compiller the type of items (for compartability with compilelrs without typeof)
ARRAY_ADD(myIntArray) = (MyIntArray)ARRAY_INIT_ITEMS(3, ((int[]) { 1, 2, 3, 4 }));
// Empty array of strings
ARRAY(String) strArray = ARRAY_INIT_CAPACITY(String, 0);
// Add few strings
ARRAY_ADD(strArray) = String_INIT_CSTR("A");
ARRAY_ADD(strArray) = String_INIT_CSTR("Test");
// Print strings from the array
for (size_t i = 0; i < strArray.size; i++)
Print(&strArray.data[i]);
char b = ARRAY_ADD(charArray) = 'a';
ARRAY_ADD(charArray) = 'a';
ARRAY_ADD(charArray) = 'a';
// Cast array to void* is required because function expects anonymous array.
// You can define it with typedef if you consider this ugly and unsafe (see an example few lines above)
ProcessCopy((void*)&charArray);
// Or you can use ARRAY_REF + REF + UNREF that is more verbose but generates warning in case of wrong types
Process((void*)&charArray);
// Add some items into arrays
ARRAY_ADD(charArrayCopy) = charArray.data[0];
ARRAY_ADD(charArrayCopy) = '\0';
ARRAY_ADD(charArray) = b;
ARRAY_ADD(charArray) = 'a';
ARRAY_ADD(charArray) = '\0';
printf("charArray = %s\n", charArray.data);
// Remove couple items from the array
ARRAY_REMOVE(charArray, 0);
ARRAY_REMOVE(charArray, 0);
printf("charArrayCopy = %s\n", charArrayCopy.data);
// Print before removal of 'b' symbols
printf("charArray = %s\n", charArray.data);
// Remove all 'b' symbols
int pos = 0;
while ((pos = ARRAY_FIND(charArray, Compare, (char[]) { 'b' })) != -1)
{
ARRAY_REMOVE(charArray, pos);
}
// Print after removal
printf("charArray = %s\n", charArray.data);
return EXIT_SUCCESS;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment