feat: hectic C: union in debug string constructor
This commit is contained in:
@@ -1,16 +1,7 @@
|
||||
// ------------
|
||||
// -- Colors --
|
||||
// ------------
|
||||
|
||||
// Color mode enumeration
|
||||
typedef enum {
|
||||
COLOR_MODE_AUTO,
|
||||
COLOR_MODE_FORCE,
|
||||
COLOR_MODE_DISABLE
|
||||
} ColorMode;
|
||||
|
||||
// External color mode variable declaration
|
||||
extern ColorMode color_mode;
|
||||
extern ColorMode debug_color_mode;
|
||||
|
||||
const char* color_mode_to_string(ColorMode mode);
|
||||
|
||||
@@ -30,7 +21,7 @@ void set_output_color_mode(ColorMode mode);
|
||||
* DEBUG_COLOR_MODE is the color mode for debug output after USE_COLOR() check.
|
||||
* used for debug colorized output
|
||||
*/
|
||||
#define USE_COLOR_IN_DEBUG() (USE_COLOR() ? DEBUG_COLOR_MODE : COLOR_MODE_DISABLE)
|
||||
#define USE_COLOR_IN_DEBUG() (color_mode == COLOR_MODE_AUTO ? ((debug_color_mode == COLOR_MODE_FORCE) || (debug_color_mode == COLOR_MODE_AUTO && IS_TERMINAL())) : USE_COLOR())
|
||||
|
||||
#define COLOR_RED "\033[1;31m"
|
||||
#define COLOR_GREEN "\033[1;32m"
|
||||
@@ -44,6 +35,5 @@ void set_output_color_mode(ColorMode mode);
|
||||
#define OPTIONAL_COLOR(color) (USE_COLOR() ? color : "")
|
||||
#define DEBUG_COLOR(color) (USE_COLOR_IN_DEBUG() ? color : "")
|
||||
|
||||
OPTIONAL_COLOR(COLOR_RED) "Hello" OPTIONAL_COLOR(COLOR_RESET)
|
||||
|
||||
">>>>"
|
||||
DEBUG_COLOR(COLOR_RED) "Hello" DEBUG_COLOR(COLOR_RESET)
|
||||
|
||||
@@ -288,7 +288,7 @@ void debug_ptrset_add__(CTX_DECLARATION, PtrSet *set, const void *ptr, const cha
|
||||
}
|
||||
|
||||
char *enum_to_debug_str__(CTX_DECLARATION, const char *name, size_t enum_value, const char *enum_str) {
|
||||
return arena_strdup_fmt__(CTX(arena), "%s = %s%s%s %zu ", name, DEBUG_COLOR(COLOR_CYAN), enum_str, DEBUG_COLOR(COLOR_RESET), enum_value);
|
||||
return arena_strdup_fmt__(CTX(arena), "%senum%s %s = %s%s%s %zu ", DEBUG_COLOR(COLOR_GREEN), DEBUG_COLOR(COLOR_RESET), name, DEBUG_COLOR(COLOR_CYAN), enum_str, DEBUG_COLOR(COLOR_RESET), enum_value);
|
||||
}
|
||||
|
||||
char *string_to_debug_str__(CTX_DECLARATION, const char *name, const char *string) {
|
||||
@@ -321,6 +321,53 @@ char *char_to_debug_str__(CTX_DECLARATION, const char *name, char c) {
|
||||
return arena_strdup_fmt__(CTX(arena), "%s = %c", name, c);
|
||||
}
|
||||
|
||||
char *union_to_debug_str__(POSITION_INFO_DECLARATION, Arena *arena, const char *type, const char *name, const void *ptr, size_t active_variant, size_t count, ...) {
|
||||
if (count % 2 == 0) {
|
||||
raise_message(LOG_LEVEL_EXCEPTION, POSITION_INFO, "HECTICLIB ERROR: Union to debug str: count is even");
|
||||
assert(0);
|
||||
}
|
||||
|
||||
va_list args;
|
||||
va_start(args, count);
|
||||
|
||||
char *value = NULL;
|
||||
bool variant_exists = false;
|
||||
|
||||
// Find the matching value for the active variant
|
||||
while (count--) {
|
||||
size_t variant = va_arg(args, size_t);
|
||||
if (variant == (size_t)-1) break; // End marker
|
||||
|
||||
if (variant == active_variant) {
|
||||
variant_exists = true;
|
||||
value = va_arg(args, char*);
|
||||
break;
|
||||
}
|
||||
// Skip the string value for non-matching variants
|
||||
va_arg(args, char*);
|
||||
}
|
||||
|
||||
va_end(args);
|
||||
|
||||
if (!variant_exists) {
|
||||
return arena_strdup_fmt__(file, func, line, arena,
|
||||
"%sunion%s %s %s = {invalid variant %d} %s%p%s",
|
||||
DEBUG_COLOR(COLOR_GREEN), DEBUG_COLOR(COLOR_RESET),
|
||||
type, name, active_variant, DEBUG_COLOR(COLOR_CYAN), ptr, DEBUG_COLOR(COLOR_RESET));
|
||||
}
|
||||
|
||||
if (!value) {
|
||||
return arena_strdup_fmt__(file, func, line, arena,
|
||||
"%sunion%s %s %s = {unknown variant} %s%p%s",
|
||||
DEBUG_COLOR(COLOR_GREEN), DEBUG_COLOR(COLOR_RESET),
|
||||
type, name, DEBUG_COLOR(COLOR_CYAN), ptr, DEBUG_COLOR(COLOR_RESET));
|
||||
}
|
||||
|
||||
return arena_strdup_fmt__(file, func, line, arena,
|
||||
"%sunion%s %s %s = %s %s%p%s",
|
||||
DEBUG_COLOR(COLOR_GREEN), DEBUG_COLOR(COLOR_RESET),
|
||||
type, name, value, DEBUG_COLOR(COLOR_CYAN), ptr, DEBUG_COLOR(COLOR_RESET));
|
||||
}
|
||||
|
||||
/* Private function */
|
||||
char *debug_join_debug_strings_v(CTX_DECLARATION, int count, va_list args) {
|
||||
@@ -363,7 +410,7 @@ char *struct_to_debug_str__(CTX_DECLARATION, const char *type, const char *name,
|
||||
char *joined = debug_join_debug_strings_v(CTX(arena), count, args);
|
||||
va_end(args);
|
||||
|
||||
return arena_strdup_fmt__(CTX(arena), "%s %s = {%s} %s%p%s", type, name, joined, DEBUG_COLOR(COLOR_CYAN), ptr, DEBUG_COLOR(COLOR_RESET));
|
||||
return arena_strdup_fmt__(CTX(arena), "%sstruct%s %s %s = {%s} %s%p%s", DEBUG_COLOR(COLOR_GREEN), DEBUG_COLOR(COLOR_RESET), type, name, joined, DEBUG_COLOR(COLOR_CYAN), ptr, DEBUG_COLOR(COLOR_RESET));
|
||||
}
|
||||
|
||||
// ------------
|
||||
@@ -2099,15 +2146,15 @@ char *template_text_value_to_debug_str__(POSITION_INFO_DECLARATION, Arena *arena
|
||||
return result;
|
||||
}
|
||||
|
||||
char *template_value_to_debug_str__(POSITION_INFO_DECLARATION, Arena *arena, const char *name, const TemplateValue *self, PtrSet *visited) {
|
||||
char *template_value_to_debug_str__(POSITION_INFO_DECLARATION, Arena *arena, const char *name, const TemplateValue *self, TemplateNodeType type, PtrSet *visited) {
|
||||
char *result = arena_alloc(arena, MEM_KiB);
|
||||
|
||||
STRUCT_TO_DEBUG_STR(arena, result, TemplateValue, name, self, visited, 5,
|
||||
template_section_value_to_debug_str__(POSITION_INFO, arena, "section", &self->section, visited),
|
||||
template_interpolate_value_to_debug_str__(POSITION_INFO, arena, "interpolate", &self->interpolate, visited),
|
||||
template_execute_value_to_debug_str__(POSITION_INFO, arena, "execute", &self->execute, visited),
|
||||
template_include_value_to_debug_str__(POSITION_INFO, arena, "include", &self->include, visited),
|
||||
template_text_value_to_debug_str__(POSITION_INFO, arena, "text", &self->text, visited)
|
||||
UNION_TO_DEBUG_STR(arena, result, TemplateValue, name, self, visited, type, 5,
|
||||
TEMPLATE_NODE_SECTION, template_section_value_to_debug_str__(POSITION_INFO, arena, "section", &self->section, visited),
|
||||
TEMPLATE_NODE_INTERPOLATE, template_interpolate_value_to_debug_str__(POSITION_INFO, arena, "interpolate", &self->interpolate, visited),
|
||||
TEMPLATE_NODE_EXECUTE, template_execute_value_to_debug_str__(POSITION_INFO, arena, "execute", &self->execute, visited),
|
||||
TEMPLATE_NODE_INCLUDE, template_include_value_to_debug_str__(POSITION_INFO, arena, "include", &self->include, visited),
|
||||
TEMPLATE_NODE_TEXT, template_text_value_to_debug_str__(POSITION_INFO, arena, "text", &self->text, visited)
|
||||
);
|
||||
return result;
|
||||
}
|
||||
@@ -2116,7 +2163,7 @@ char *template_node_to_debug_str__(POSITION_INFO_DECLARATION, Arena *arena, cons
|
||||
char *result = arena_alloc(arena, MEM_KiB);
|
||||
STRUCT_TO_DEBUG_STR(arena, result, TemplateNode, name, self, visited, 4,
|
||||
string_to_debug_str__(POSITION_INFO, arena, "type", template_node_type_to_string(self->type)),
|
||||
template_value_to_debug_str__(POSITION_INFO, arena, "value", &self->value, visited),
|
||||
template_value_to_debug_str__(POSITION_INFO, arena, "value", &self->value, self->type, visited),
|
||||
template_node_to_debug_str__(POSITION_INFO, arena, "children", self->children, visited),
|
||||
template_node_to_debug_str__(POSITION_INFO, arena, "next", self->next, visited)
|
||||
);
|
||||
|
||||
@@ -438,6 +438,8 @@ char *ptr_to_debug_str__(const char *file, const char *func, int line, Arena *ar
|
||||
|
||||
char *char_to_debug_str__(const char *file, const char *func, int line, Arena *arena, const char *name, char c);
|
||||
|
||||
char *union_to_debug_str__(const char *file, const char *func, int line, Arena *arena, const char *type, const char *name, const void *ptr, size_t active_variant, size_t count, ...);
|
||||
|
||||
char *struct_to_debug_str__(const char *file, const char *func, int line, Arena *arena, const char *type, const char *name, const void *ptr, int count, ...);
|
||||
|
||||
bool debug_ptrset_contains(PtrSet *set, void *ptr);
|
||||
@@ -457,6 +459,21 @@ bool debug_ptrset_contains(PtrSet *set, void *ptr);
|
||||
#define CHAR_TO_DEBUG_STR(arena, name, c) \
|
||||
char_to_debug_str__(__FILE__, __func__, __LINE__, arena, name, c)
|
||||
|
||||
#define UNION_TO_DEBUG_STR(arena, buffer, type, name, ptr, visited, active_variant, count, ...) do { \
|
||||
if (!name) \
|
||||
name = "$1"; \
|
||||
\
|
||||
if (debug_ptrset_contains__(visited, ptr, #type, name)) \
|
||||
return arena_strdup_fmt__(__FILE__, __func__, __LINE__, arena, "%sunion%s %s %s = {cycle detected} %s%p%s", DEBUG_COLOR(COLOR_GREEN), DEBUG_COLOR(COLOR_RESET), #type, name, DEBUG_COLOR(COLOR_CYAN), ptr, DEBUG_COLOR(COLOR_RESET)); \
|
||||
\
|
||||
if (!ptr) \
|
||||
return arena_strdup_fmt__(__FILE__, __func__, __LINE__, arena, "%sunion%s %s %s = NULL", DEBUG_COLOR(COLOR_GREEN), DEBUG_COLOR(COLOR_RESET), #type, name); \
|
||||
\
|
||||
debug_ptrset_add__(__FILE__, __func__, __LINE__, arena, visited, ptr, #type, name); \
|
||||
\
|
||||
buffer = union_to_debug_str__(__FILE__, __func__, __LINE__, arena, #type, name, ptr, active_variant, count, ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
* STRUCT_TO_DEBUG_STR - Converts a structure into a debug string.
|
||||
*
|
||||
@@ -487,19 +504,16 @@ bool debug_ptrset_contains(PtrSet *set, void *ptr);
|
||||
name = "$1"; \
|
||||
\
|
||||
if (debug_ptrset_contains__(visited, ptr, #type, name)) \
|
||||
return arena_strdup_fmt__(__FILE__, __func__, __LINE__, arena, "%s %s = {cycle detected} %p", #type, name, ptr); \
|
||||
return arena_strdup_fmt__(__FILE__, __func__, __LINE__, arena, "%sstruct%s %s %s = {cycle detected} %s%p%s", DEBUG_COLOR(COLOR_GREEN), DEBUG_COLOR(COLOR_RESET), #type, name, DEBUG_COLOR(COLOR_CYAN), ptr, DEBUG_COLOR(COLOR_RESET)); \
|
||||
\
|
||||
if (!ptr) \
|
||||
return arena_strdup_fmt__(__FILE__, __func__, __LINE__, arena, "%s %s = NULL", #type, name); \
|
||||
return arena_strdup_fmt__(__FILE__, __func__, __LINE__, arena, "%sstruct%s %s %s = NULL", DEBUG_COLOR(COLOR_GREEN), DEBUG_COLOR(COLOR_RESET), #type, name); \
|
||||
\
|
||||
debug_ptrset_add__(__FILE__, __func__, __LINE__, arena, visited, ptr, #type, name); \
|
||||
\
|
||||
buffer = struct_to_debug_str__(__FILE__, __func__, __LINE__, arena, #type, name, ptr, count, ##__VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
#define UNION_TO_DEBUG_STR(arena, buffer, type, name, ptr, visited, count, ...) \
|
||||
STRUCT_TO_DEBUG_STR(arena, buffer, type, name, ptr, visited, count, ##__VA_ARGS__)
|
||||
|
||||
// ------------------
|
||||
// -- Logger Rules --
|
||||
// ------------------
|
||||
|
||||
@@ -20,6 +20,18 @@ struct Struct2 {
|
||||
Struct *other;
|
||||
};
|
||||
|
||||
typedef enum TestUnionVariant {
|
||||
TEST_UNION_VARIANT_A,
|
||||
TEST_UNION_VARIANT_B,
|
||||
TEST_UNION_VARIANT_C
|
||||
} TestUnionVariant;
|
||||
|
||||
typedef union TestUnion {
|
||||
int a;
|
||||
char *b;
|
||||
float c;
|
||||
} TestUnion;
|
||||
|
||||
char *struct_to_debug_str(Arena *arena, char *name, Struct *self, PtrSet *visited) {
|
||||
raise_trace("struct_to_debug_str: name: %s, self: %p, visited: %p", name, self, visited);
|
||||
|
||||
@@ -48,6 +60,18 @@ char *struct2_to_debug_str(Arena *arena, char *name, Struct2 *self, PtrSet *visi
|
||||
return result;
|
||||
}
|
||||
|
||||
char *test_union_to_debug_str(Arena *arena, char *name, TestUnion *self, PtrSet *visited, TestUnionVariant active_variant) {
|
||||
raise_trace("test_union_to_debug_str: name: %s, self: %p, visited: %p, active_variant: %zu", name, self, visited, active_variant);
|
||||
|
||||
char *result = arena_alloc(arena, MEM_KiB);
|
||||
UNION_TO_DEBUG_STR(arena, result, TestUnion, name, self, visited, active_variant, 3,
|
||||
TEST_UNION_VARIANT_A, INT_TO_DEBUG_STR(arena, "a", self->a),
|
||||
TEST_UNION_VARIANT_B, STRING_TO_DEBUG_STR(arena, "b", self->b),
|
||||
TEST_UNION_VARIANT_C, FLOAT_TO_DEBUG_STR(arena, "c", self->c)
|
||||
);
|
||||
return result;
|
||||
}
|
||||
|
||||
void test_struct_to_debug_str(Arena *arena) {
|
||||
// Mock a struct with a cycle
|
||||
Struct test_struct = {.a = 1, .b = 2, .next = NULL};
|
||||
@@ -58,7 +82,7 @@ void test_struct_to_debug_str(Arena *arena) {
|
||||
raise_notice("result: %s", result);
|
||||
|
||||
char *check = arena_alloc(arena, MEM_KiB);
|
||||
sprintf(check, "Struct struct = {a = 1, b = 2, Struct next = {a = 1, b = 2, Struct next = {cycle detected} %p} %p} %p", (void*)&test_struct, (void*)&test_struct, (void*)&test_struct);
|
||||
sprintf(check, "struct Struct struct = {a = 1, b = 2, struct Struct next = {a = 1, b = 2, struct Struct next = {cycle detected} %p} %p} %p", (void*)&test_struct, (void*)&test_struct, (void*)&test_struct);
|
||||
raise_notice("check: %s", check);
|
||||
assert(strcmp(result, check) == 0);
|
||||
}
|
||||
@@ -74,7 +98,18 @@ void test_struct2_to_debug_str(Arena *arena) {
|
||||
char *result = struct2_to_debug_str(arena, "struct2", &test_struct2, visited);
|
||||
raise_notice("result: %s", result);
|
||||
char *check = arena_alloc(arena, MEM_KiB);
|
||||
sprintf(check, "Struct2 struct2 = {a = 1, f = 3.140000, c = %p \"hello\", Struct other = {a = 1, b = 2, Struct next = {a = 1, b = 2, Struct next = {cycle detected} %p} %p} %p, Struct2 left = NULL} %p", (void*)test_struct2.c,(void*)&test_struct, (void*)&test_struct, (void*)&test_struct, (void*)&test_struct2);
|
||||
sprintf(check, "struct Struct2 struct2 = {a = 1, f = 3.140000, c = %p \"hello\", struct Struct other = {a = 1, b = 2, struct Struct next = {a = 1, b = 2, struct Struct next = {cycle detected} %p} %p} %p, struct Struct2 left = NULL} %p", (void*)test_struct2.c,(void*)&test_struct, (void*)&test_struct, (void*)&test_struct, (void*)&test_struct2);
|
||||
raise_notice("check: %s", check);
|
||||
assert(strcmp(result, check) == 0);
|
||||
}
|
||||
|
||||
void test_test_union_to_debug_str(Arena *arena) {
|
||||
TestUnion test_union = {.a = 1};
|
||||
PtrSet *visited = ptrset_init(arena);
|
||||
char *result = test_union_to_debug_str(arena, "test_union", &test_union, visited, TEST_UNION_VARIANT_A);
|
||||
raise_notice("result: %s", result);
|
||||
char *check = arena_alloc(arena, MEM_KiB);
|
||||
sprintf(check, "union TestUnion test_union = {a = 1} %p", (void*)&test_union);
|
||||
raise_notice("check: %s", check);
|
||||
assert(strcmp(result, check) == 0);
|
||||
}
|
||||
@@ -92,6 +127,9 @@ int main(void) {
|
||||
printf("%sTesting struct_to_debug_str2%s\n", OPTIONAL_COLOR(COLOR_GREEN), OPTIONAL_COLOR(COLOR_RESET));
|
||||
test_struct2_to_debug_str(&arena);
|
||||
|
||||
printf("%sTesting test_union_to_debug_str%s\n", OPTIONAL_COLOR(COLOR_GREEN), OPTIONAL_COLOR(COLOR_RESET));
|
||||
test_test_union_to_debug_str(&arena);
|
||||
|
||||
arena_free(&arena);
|
||||
logger_free();
|
||||
printf("%sAll tests passed %s%s%s\n", OPTIONAL_COLOR(COLOR_GREEN), OPTIONAL_COLOR(COLOR_CYAN), __FILE__, OPTIONAL_COLOR(COLOR_RESET));
|
||||
|
||||
Reference in New Issue
Block a user