This commit is contained in:
2025-12-14 12:40:32 +01:00
parent c9959cdb5b
commit 308cb6e83d
10 changed files with 48 additions and 219 deletions
+1 -1
View File
@@ -17,7 +17,7 @@ jobs:
- name: Install dependencies - name: Install dependencies
run: | run: |
sudo apt-get update sudo apt-get update
sudo apt-get install -y gcc make libcriterion-dev valgrind sudo apt-get install -y gcc make libcriterion-dev valgrind zig
- name: Build and run tests - name: Build and run tests
run: make test run: make test
+2 -4
View File
@@ -1,13 +1,11 @@
#include "cig.h" #include "cig.h"
static void *buffer_allocator_alloc(buffer_allocator_t *this, size_t bytes) { static void *buffer_allocator_alloc(buffer_allocator_t *this, size_t bytes) {
size_t address_of_new_data = (size_t) &this->data[this->size]; bytes = (bytes + MAX_ALIGN - 1) / MAX_ALIGN * MAX_ALIGN;
size_t offset = address_of_new_data % MAX_ALIGN; size_t new_size = this->size + bytes;
size_t new_size = this->size + offset + bytes;
if (new_size > this->capacity) { if (new_size > this->capacity) {
return NULL; return NULL;
} }
this->size += offset;
void *ptr = &this->data[this->size]; void *ptr = &this->data[this->size];
this->size = new_size; this->size = new_size;
return ptr; return ptr;
+6 -15
View File
@@ -13,8 +13,7 @@ typedef union any_align { char c; int i; long l; long long ll; float f; double d
#define KB (1024) #define KB (1024)
#define MB (KB * KB) #define MB (KB * KB)
#define GB (KB * KB * KB) #define GB (KB * KB * KB)
#define OFFSET(STRUCT, FIELD) ((size_t) (&((STRUCT*) NULL)->FIELD)) #define PTR_FROM_FIELD_PTR(STRUCT, FIELD, PTR) ((STRUCT *) (((char *) PTR) - offsetof(STRUCT, FIELD)))
#define PTR_FROM_FIELD_PTR(STRUCT, FIELD, PTR) ((STRUCT *) (((char *) PTR) - OFFSET(STRUCT, FIELD)))
// Contains all operations an allocator can do. Similar interface to sdtlibs // Contains all operations an allocator can do. Similar interface to sdtlibs
// malloc, realloc and free. // malloc, realloc and free.
@@ -144,12 +143,11 @@ typedef struct dyn_array_create_func_args {
} dyn_array_create_func_args_t; } dyn_array_create_func_args_t;
void *dyn_array_create_func(dyn_array_create_func_args_t args); void *dyn_array_create_func(dyn_array_create_func_args_t args);
#define make_arr(ALLOCATOR, TYPE, ...) ((TYPE *)dyn_array_create_func((dyn_array_create_func_args_t){ \ #define make_arr(TYPE, ...) ((TYPE *)dyn_array_create_func((dyn_array_create_func_args_t){ \
.allocator = ALLOCATOR, \
.itemsize = sizeof(TYPE), \ .itemsize = sizeof(TYPE), \
.file = __FILE__, \ .file = __FILE__, \
.line = __LINE__, \ .line = __LINE__, \
__VA_ARGS__ \ .allocator = __VA_ARGS__, \
})) }))
// Always reassign the array. if multiple variables reference the same growing // Always reassign the array. if multiple variables reference the same growing
@@ -180,13 +178,13 @@ size_t arr_cap(void *this);
#define arr_pop(THIS) (arr_shrink(THIS, 1), THIS[arr_len(THIS)]) #define arr_pop(THIS) (arr_shrink(THIS, 1), THIS[arr_len(THIS)])
void dyn_array_bounds_check_func(void *this, size_t index, const char *file, int line); void dyn_array_bounds_check_func(void *this, size_t index, const char *file, int line, bool print_error);
#define arr_get(THIS, INDEX) \ #define arr_get(THIS, INDEX) \
(dyn_array_bounds_check_func(THIS, INDEX, __FILE__, __LINE__), (THIS)[INDEX]) (dyn_array_bounds_check_func(THIS, INDEX, __FILE__, __LINE__, true), (THIS)[INDEX])
#define arr_set(THIS, INDEX, VALUE) \ #define arr_set(THIS, INDEX, VALUE) \
(dyn_array_bounds_check_func(THIS, INDEX, __FILE__, __LINE__), (THIS)[INDEX] = (VALUE)) (dyn_array_bounds_check_func(THIS, INDEX, __FILE__, __LINE__, true), (THIS)[INDEX] = (VALUE))
#define STATIC_ASSERT(expr) ((void)sizeof(char[(expr) ? 1 : -1])) #define STATIC_ASSERT(expr) ((void)sizeof(char[(expr) ? 1 : -1]))
@@ -212,13 +210,6 @@ bool dyn_array_contains_cmp_func(void *this, uint8_t *value, dyn_array_eq_fn eq)
// Comparison function for sorting: returns -1 if a < b, 0 if a == b, 1 if a > b // Comparison function for sorting: returns -1 if a < b, 0 if a == b, 1 if a > b
typedef int (*dyn_array_cmp_fn)(const void *a, const void *b); typedef int (*dyn_array_cmp_fn)(const void *a, const void *b);
void dyn_array_insert_sorted_func(void **this_ptr, const void *value, dyn_array_cmp_fn cmp, const char *file, int line);
#define arr_insert_sorted(THIS, VALUE, CMP_FN) do { \
typeof((THIS)[0]) _tmp_value = (VALUE); \
dyn_array_insert_sorted_func((void**)&(THIS), &_tmp_value, (CMP_FN), __FILE__, __LINE__); \
} while(0)
// CLI ///////////////////////////////////////////////////////////////////////// // CLI /////////////////////////////////////////////////////////////////////////
#define CLI_UNIQUE1 __macro_internal_34bba35b8b9b20a75f9881e3795630e25d36e620d9c9741e2e9141ba82ec6ef7__ #define CLI_UNIQUE1 __macro_internal_34bba35b8b9b20a75f9881e3795630e25d36e620d9c9741e2e9141ba82ec6ef7__
+12 -44
View File
@@ -96,52 +96,20 @@ bool dyn_array_contains_cmp_func(void *this, uint8_t *value, dyn_array_eq_fn eq)
return false; return false;
} }
void dyn_array_bounds_check_func(void *this, size_t index, const char *file, int line) { void dyn_array_bounds_check_func(void *this, size_t index, const char *file, int line, bool print_error) {
size_t len = arr_len(this); size_t len = arr_len(this);
if (index >= len) { if ((index >= len)) {
fprintf( if (print_error) {
stderr, fprintf(
"%s:%d: array index %zu out of bounds (length is %zu)\n", stderr,
file, "%s:%d: array index %zu out of bounds (length is %zu)\n",
line, file,
index, line,
len index,
); len
);
}
exit(1); exit(1);
} }
} }
void dyn_array_insert_sorted_func(void **this_ptr, const void *value, dyn_array_cmp_fn cmp, const char *file, int line) {
void *this = *this_ptr;
dyn_array_header_t *header = PTR_FROM_FIELD_PTR(dyn_array_header_t, bytes, this);
// First append the value
this = dyn_array_grow_func(this, 1, file, line);
*this_ptr = this;
header = PTR_FROM_FIELD_PTR(dyn_array_header_t, bytes, this);
size_t itemsize = header->itemsize;
size_t n_items = header->n_items;
// Copy value to the end
memcpy(&header->bytes[(n_items - 1) * itemsize], value, itemsize);
// Bubble it into the correct position from the back
size_t pos = n_items - 1;
while (pos > 0) {
void *a = &header->bytes[(pos - 1) * itemsize];
void *b = &header->bytes[pos * itemsize];
if (cmp(a, b) <= 0) {
break; // Already in correct position
}
// Swap using a temporary buffer
uint8_t temp[itemsize];
memcpy(temp, a, itemsize);
memcpy(a, b, itemsize);
memcpy(b, temp, itemsize);
pos--;
}
}
+1 -1
View File
@@ -19,7 +19,7 @@ char *read_entire_file(const char *path, allocator_t allocator) {
// Returns dynamic array, of fixed char strings. // Returns dynamic array, of fixed char strings.
char **read_all_file_lines(const char *path, allocator_t allocator) { char **read_all_file_lines(const char *path, allocator_t allocator) {
char *contents = read_entire_file(path, allocator); char *contents = read_entire_file(path, allocator);
char **lines = make_arr(allocator, char*); char **lines = make_arr(char*, allocator);
arr_append(lines, contents); arr_append(lines, contents);
bool just_split = false; bool just_split = false;
for (char *c = contents; (*c)!='\0'; c++) { for (char *c = contents; (*c)!='\0'; c++) {
+1 -1
View File
@@ -1,5 +1,5 @@
CC := gcc CC := zig cc
CFLAGS := -pedantic -Wall -Wextra -Wno-override-init -O0 -g -fno-omit-frame-pointer -fno-inline CFLAGS := -pedantic -Wall -Wextra -Wno-override-init -O0 -g -fno-omit-frame-pointer -fno-inline
LDFLAGS := -lcriterion LDFLAGS := -lcriterion
+5 -5
View File
@@ -186,7 +186,7 @@ Test(arena_allocator, dyn_array_resize_pattern) {
allocator_t arena = arena_allocator_create(backing, 10 * KB); allocator_t arena = arena_allocator_create(backing, 10 * KB);
allocator_reset(arena); allocator_reset(arena);
int *arr = make_arr(arena, int); int *arr = make_arr(int, arena);
// Add 100 elements, forcing multiple resizes // Add 100 elements, forcing multiple resizes
for (int i = 0; i < 100; i++) { for (int i = 0; i < 100; i++) {
@@ -207,8 +207,8 @@ Test(arena_allocator, multiple_arrays_resize) {
allocator_t arena = arena_allocator_create(backing, 20 * KB); allocator_t arena = arena_allocator_create(backing, 20 * KB);
allocator_reset(arena); allocator_reset(arena);
int *arr1 = make_arr(arena, int); int *arr1 = make_arr(int, arena);
int *arr2 = make_arr(arena, int); int *arr2 = make_arr(int, arena);
// Add to arr1 // Add to arr1
for (int i = 0; i < 50; i++) { for (int i = 0; i < 50; i++) {
@@ -267,11 +267,11 @@ Test(arena_allocator, nested_structures_with_resize) {
allocator_reset(arena); allocator_reset(arena);
// Create array of int pointers (like dyn_array of dyn_arrays) // Create array of int pointers (like dyn_array of dyn_arrays)
int **rows = make_arr(arena, int*); int **rows = make_arr(int*, arena);
// Add 5 rows // Add 5 rows
for (int i = 0; i < 5; i++) { for (int i = 0; i < 5; i++) {
int *row = make_arr(arena, int); int *row = make_arr(int, arena);
for (int j = 0; j < 10; j++) { for (int j = 0; j < 10; j++) {
arr_append(row, i * 10 + j); arr_append(row, i * 10 + j);
} }
+1 -1
View File
@@ -11,7 +11,7 @@ static void test_buffer_alloc(buffer_allocator_t impl) {
for (int i = 0; i < n_ints; i++) { for (int i = 0; i < n_ints; i++) {
cr_assert_eq(ints[i], i); cr_assert_eq(ints[i], i);
} }
cr_assert_eq(impl.size, sizeof(int)*n_ints); cr_assert_eq(impl.size, sizeof(int)*n_ints, "%zu != %d", impl.size, n_ints);
ints = allocator_alloc(inter, sizeof(int) * n_ints); ints = allocator_alloc(inter, sizeof(int) * n_ints);
for (int i = 0; i < n_ints; i++) { for (int i = 0; i < n_ints; i++) {
ints[i] = i; ints[i] = i;
+18 -147
View File
@@ -2,25 +2,13 @@
#include <stdbool.h> #include <stdbool.h>
#include "cig.h" #include "cig.h"
static int int_cmp(const void *a, const void *b) {
int ia = *(const int*)a;
int ib = *(const int*)b;
if (ia < ib) return -1;
if (ia > ib) return 1;
return 0;
}
static bool int_eq(const void *a, const void *b) { static bool int_eq(const void *a, const void *b) {
return *(const int*)a == *(const int*)b; return *(const int*)a == *(const int*)b;
} }
static int reverse_cmp(const void *a, const void *b) {
return -int_cmp(a, b);
}
Test(dynamic_arrays, append) { Test(dynamic_arrays, append) {
with_borrow(alloc) { with_borrow(alloc) {
int *numbers = make_arr(alloc, int); int *numbers = make_arr(int, alloc);
arr_append(numbers, 40); arr_append(numbers, 40);
arr_append(numbers, 41); arr_append(numbers, 41);
arr_append(numbers, 42); arr_append(numbers, 42);
@@ -40,7 +28,7 @@ Test(dynamic_arrays, append) {
Test(dynamic_arrays, pop) { Test(dynamic_arrays, pop) {
with_borrow(alloc) { with_borrow(alloc) {
int *numbers = make_arr(alloc, int); int *numbers = make_arr(int, alloc);
arr_append(numbers, 40); arr_append(numbers, 40);
arr_append(numbers, 41); arr_append(numbers, 41);
arr_append(numbers, 42); arr_append(numbers, 42);
@@ -67,7 +55,7 @@ Test(dynamic_arrays, pop) {
Test(dynamic_arrays, contains) { Test(dynamic_arrays, contains) {
with_borrow(alloc) { with_borrow(alloc) {
int *numbers = make_arr(alloc, int); int *numbers = make_arr(int, alloc);
arr_append(numbers, 20); arr_append(numbers, 20);
cr_expect(arr_contains(numbers, &(int){20})); cr_expect(arr_contains(numbers, &(int){20}));
arr_reset(numbers); arr_reset(numbers);
@@ -82,72 +70,12 @@ Test(dynamic_arrays, contains) {
cr_assert_eq(arr_len(numbers), 100); cr_assert_eq(arr_len(numbers), 100);
} }
} }
Test(dynamic_arrays, insert_sorted_ascending) {
with_borrow(alloc) {
int *arr = make_arr(alloc, int, .initial_capacity = 5);
// Insert in random order
arr_insert_sorted(arr, 5, int_cmp);
arr_insert_sorted(arr, 2, int_cmp);
arr_insert_sorted(arr, 8, int_cmp);
arr_insert_sorted(arr, 1, int_cmp);
arr_insert_sorted(arr, 9, int_cmp);
arr_insert_sorted(arr, 3, int_cmp);
// Verify sorted order
cr_assert_eq(arr_len(arr), 6);
cr_assert_eq(arr[0], 1);
cr_assert_eq(arr[1], 2);
cr_assert_eq(arr[2], 3);
cr_assert_eq(arr[3], 5);
cr_assert_eq(arr[4], 8);
cr_assert_eq(arr[5], 9);
}
}
Test(dynamic_arrays, insert_sorted_descending) {
with_borrow(alloc) {
int *arr = make_arr(alloc, int, .initial_capacity = 5);
// Insert in random order with reverse comparison
arr_insert_sorted(arr, 5, reverse_cmp);
arr_insert_sorted(arr, 2, reverse_cmp);
arr_insert_sorted(arr, 8, reverse_cmp);
arr_insert_sorted(arr, 1, reverse_cmp);
// Verify descending order
cr_assert_eq(arr_len(arr), 4);
cr_assert_eq(arr[0], 8);
cr_assert_eq(arr[1], 5);
cr_assert_eq(arr[2], 2);
cr_assert_eq(arr[3], 1);
}
}
Test(dynamic_arrays, insert_sorted_duplicates) {
with_borrow(alloc) {
int *arr = make_arr(alloc, int, .initial_capacity = 5);
// Insert duplicates
arr_insert_sorted(arr, 5, int_cmp);
arr_insert_sorted(arr, 3, int_cmp);
arr_insert_sorted(arr, 5, int_cmp);
arr_insert_sorted(arr, 3, int_cmp);
arr_insert_sorted(arr, 5, int_cmp);
// Verify all elements are present
cr_assert_eq(arr_len(arr), 5);
cr_assert_eq(arr[0], 3);
cr_assert_eq(arr[1], 3);
cr_assert_eq(arr[2], 5);
cr_assert_eq(arr[3], 5);
cr_assert_eq(arr[4], 5);
}
}
Test(dynamic_arrays, contains_found) { Test(dynamic_arrays, contains_found) {
with_borrow(alloc) { with_borrow(alloc) {
int *arr = make_arr(alloc, int, .initial_capacity = 5); int *arr = make_arr(int, alloc, .initial_capacity = 5);
arr_append(arr, 10); arr_append(arr, 10);
arr_append(arr, 20); arr_append(arr, 20);
arr_append(arr, 30); arr_append(arr, 30);
@@ -160,7 +88,7 @@ Test(dynamic_arrays, contains_found) {
Test(dynamic_arrays, contains_not_found) { Test(dynamic_arrays, contains_not_found) {
with_borrow(alloc) { with_borrow(alloc) {
int *arr = make_arr(alloc, int, .initial_capacity = 5); int *arr = make_arr(int, alloc, .initial_capacity = 5);
arr_append(arr, 10); arr_append(arr, 10);
arr_append(arr, 20); arr_append(arr, 20);
arr_append(arr, 30); arr_append(arr, 30);
@@ -173,63 +101,16 @@ Test(dynamic_arrays, contains_not_found) {
Test(dynamic_arrays, contains_empty) { Test(dynamic_arrays, contains_empty) {
with_borrow(alloc) { with_borrow(alloc) {
int *arr = make_arr(alloc, int, .initial_capacity = 5); int *arr = make_arr(int, alloc, .initial_capacity = 5);
cr_assert_not(arr_contains(arr, &(int){10})); cr_assert_not(arr_contains(arr, &(int){10}));
} }
} }
Test(dynamic_arrays, insert_sorted_single_element) {
with_borrow(alloc) {
int *arr = make_arr(alloc, int, .initial_capacity = 5);
arr_insert_sorted(arr, 42, int_cmp);
cr_assert_eq(arr_len(arr), 1);
cr_assert_eq(arr[0], 42);
}
}
Test(dynamic_arrays, insert_sorted_already_sorted) {
with_borrow(alloc) {
int *arr = make_arr(alloc, int, .initial_capacity = 5);
// Insert in already sorted order
arr_insert_sorted(arr, 1, int_cmp);
arr_insert_sorted(arr, 2, int_cmp);
arr_insert_sorted(arr, 3, int_cmp);
arr_insert_sorted(arr, 4, int_cmp);
// Verify order maintained
cr_assert_eq(arr_len(arr), 4);
for (size_t i = 0; i < arr_len(arr); i++) {
cr_assert_eq(arr[i], (int)(i + 1));
}
}
}
Test(dynamic_arrays, insert_sorted_reverse_order) {
with_borrow(alloc) {
int *arr = make_arr(alloc, int, .initial_capacity = 5);
// Insert in reverse order (worst case for bubble sort)
arr_insert_sorted(arr, 5, int_cmp);
arr_insert_sorted(arr, 4, int_cmp);
arr_insert_sorted(arr, 3, int_cmp);
arr_insert_sorted(arr, 2, int_cmp);
arr_insert_sorted(arr, 1, int_cmp);
// Verify correct ascending order
cr_assert_eq(arr_len(arr), 5);
for (size_t i = 0; i < arr_len(arr); i++) {
cr_assert_eq(arr[i], (int)(i + 1));
}
}
}
Test(dynamic_arrays, contains_cmp_found) { Test(dynamic_arrays, contains_cmp_found) {
with_borrow(alloc) { with_borrow(alloc) {
int *arr = make_arr(alloc, int, .initial_capacity = 5); int *arr = make_arr(int, alloc, .initial_capacity = 5);
arr_append(arr, 10); arr_append(arr, 10);
arr_append(arr, 20); arr_append(arr, 20);
arr_append(arr, 30); arr_append(arr, 30);
@@ -242,7 +123,7 @@ Test(dynamic_arrays, contains_cmp_found) {
Test(dynamic_arrays, contains_cmp_not_found) { Test(dynamic_arrays, contains_cmp_not_found) {
with_borrow(alloc) { with_borrow(alloc) {
int *arr = make_arr(alloc, int, .initial_capacity = 5); int *arr = make_arr(int, alloc, .initial_capacity = 5);
arr_append(arr, 10); arr_append(arr, 10);
arr_append(arr, 20); arr_append(arr, 20);
arr_append(arr, 30); arr_append(arr, 30);
@@ -255,7 +136,7 @@ Test(dynamic_arrays, contains_cmp_not_found) {
Test(dynamic_arrays, contains_cmp_empty) { Test(dynamic_arrays, contains_cmp_empty) {
with_borrow(alloc) { with_borrow(alloc) {
int *arr = make_arr(alloc, int, .initial_capacity = 5); int *arr = make_arr(int, alloc, .initial_capacity = 5);
cr_assert_not(arr_contains_cmp(arr, int_eq, &(int){10})); cr_assert_not(arr_contains_cmp(arr, int_eq, &(int){10}));
} }
@@ -275,7 +156,7 @@ static bool kv_eq_by_key(const void *a, const void *b) {
Test(dynamic_arrays, contains_cmp_key_value_map) { Test(dynamic_arrays, contains_cmp_key_value_map) {
with_borrow(alloc) { with_borrow(alloc) {
kv_pair_t *map = make_arr(alloc, kv_pair_t, .initial_capacity = 10); kv_pair_t *map = make_arr(kv_pair_t, alloc, .initial_capacity = 10);
// Add some key-value pairs // Add some key-value pairs
arr_append(map, ((kv_pair_t){.key = 1, .value = "one"})); arr_append(map, ((kv_pair_t){.key = 1, .value = "one"}));
@@ -295,7 +176,7 @@ Test(dynamic_arrays, contains_cmp_key_value_map) {
Test(dynamic_arrays, get_valid_indices) { Test(dynamic_arrays, get_valid_indices) {
with_borrow(alloc) { with_borrow(alloc) {
int *arr = make_arr(alloc, int); int *arr = make_arr(int, alloc);
arr_append(arr, 10); arr_append(arr, 10);
arr_append(arr, 20); arr_append(arr, 20);
arr_append(arr, 30); arr_append(arr, 30);
@@ -310,7 +191,7 @@ Test(dynamic_arrays, get_valid_indices) {
Test(dynamic_arrays, set_valid_indices) { Test(dynamic_arrays, set_valid_indices) {
with_borrow(alloc) { with_borrow(alloc) {
int *arr = make_arr(alloc, int); int *arr = make_arr(int, alloc);
arr_append(arr, 10); arr_append(arr, 10);
arr_append(arr, 20); arr_append(arr, 20);
arr_append(arr, 30); arr_append(arr, 30);
@@ -327,36 +208,26 @@ Test(dynamic_arrays, set_valid_indices) {
Test(dynamic_arrays, get_out_of_bounds, .exit_code = 1) { Test(dynamic_arrays, get_out_of_bounds, .exit_code = 1) {
with_borrow(alloc) { with_borrow(alloc) {
int *arr = make_arr(alloc, int); int *arr = make_arr(int, alloc);
arr_append(arr, 10); arr_append(arr, 10);
arr_append(arr, 20); arr_append(arr, 20);
dyn_array_bounds_check_func( arr, 2, __FILE__, __LINE__, false);
(void)arr_get(arr, 2);
} }
} }
Test(dynamic_arrays, set_out_of_bounds, .exit_code = 1) { Test(dynamic_arrays, set_out_of_bounds, .exit_code = 1) {
with_borrow(alloc) { with_borrow(alloc) {
int *arr = make_arr(alloc, int); int *arr = make_arr(int, alloc);
arr_append(arr, 10); arr_append(arr, 10);
arr_append(arr, 20); arr_append(arr, 20);
arr_set(arr, 2, 999); dyn_array_bounds_check_func( arr, 2, __FILE__, __LINE__, false);
} }
} }
Test(dynamic_arrays, get_empty_array, .exit_code = 1) { Test(dynamic_arrays, get_empty_array, .exit_code = 1) {
with_borrow(alloc) { with_borrow(alloc) {
int *arr = make_arr(alloc, int); int *arr = make_arr(int, alloc);
(void)arr_get(arr, 0); dyn_array_bounds_check_func( arr, 0, __FILE__, __LINE__, false);
} }
} }
Test(dynamic_arrays, set_empty_array, .exit_code = 1) {
with_borrow(alloc) {
int *arr = make_arr(alloc, int);
arr_set(arr, 0, 42);
}
}
+1
View File
@@ -14,6 +14,7 @@ typedef struct args {
#define ARGS_DEFAULT .animal=cat, .is_true=true #define ARGS_DEFAULT .animal=cat, .is_true=true
#define ARGS(...) ((args_t){ ARGS_DEFAULT, __VA_ARGS__ }) #define ARGS(...) ((args_t){ ARGS_DEFAULT, __VA_ARGS__ })
// NOTE: this is actually an extension, not really supported by the C standard.
Test(macro_magic, default_values) { Test(macro_magic, default_values) {
args_t a1 = ARGS(); args_t a1 = ARGS();
args_t a2 = ARGS(.animal=dog); args_t a2 = ARGS(.animal=dog);