Last active
June 2, 2022 11:25
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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