From 4bf0173755a1e86b890416cfa5914e0fab26e29d Mon Sep 17 00:00:00 2001 From: Alexey Odinokov Date: Thu, 25 Jul 2024 16:36:59 -0500 Subject: [PATCH] adding va_list helper --- .vscode/settings.json | 3 ++- include/metac/backend/va_list_ex.h | 33 +++++++++++++++++++++++++ src/va_list_ex_test.c | 39 ++++++++++++++++++++++++++++++ src/value_string.c | 14 +++++------ src/value_with_args_test.c | 5 ++-- 5 files changed, 84 insertions(+), 10 deletions(-) create mode 100644 include/metac/backend/va_list_ex.h create mode 100644 src/va_list_ex_test.c diff --git a/.vscode/settings.json b/.vscode/settings.json index 2ea5c8f..d35b5c6 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -70,6 +70,7 @@ "stdint.h": "c", "test.h": "c", "__locale": "c", - "ios": "c" + "ios": "c", + "va_list_ex.h": "c" } } \ No newline at end of file diff --git a/include/metac/backend/va_list_ex.h b/include/metac/backend/va_list_ex.h new file mode 100644 index 0000000..9b21ede --- /dev/null +++ b/include/metac/backend/va_list_ex.h @@ -0,0 +1,33 @@ +#ifndef VA_LIST_EX_H_ +#define VA_LIST_EX_H_ + +#include "metac/base.h" + +// this must be big enough to cover stack for alloca +#define _va_list_padding (int)0x5555, (int)0x5555, (int)0x5555, (int)0x5555 +static inline struct va_list_container * va_list_container_start(void** pp, struct va_list_container * p, ...) { + *pp = (void*)&(pp); // store the top stack param addr + va_start(p->parameters, p); + return p; +} +/** @brief wrapper macro to put some things into va_list + example how to get va_list using all this combination of macroses + WITH_VA_LIST_CONTAINER(c, vprintf("%d %d %d %d %d %d %d %d %d %d\n", VA_LIST_FROM_CONTAINER(c, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10))); + it outputs 1 2 3 4 5 6 7 8 9 10 + WARNING: even though it works here - there is no guarantee that it will work everywhere and with everything Try not to use it. Reason: + A va_list is intrinsically linked to a specific function call's stack frame. + Creating a container to hold it doesn't change this fundamental characteristic. + Accessing it in a different function would involve undefined behavior due to potential stack frame changes. +*/ +#define WITH_VA_LIST_CONTAINER(_name_, _in_...) do { \ + void* pp = NULL; \ + struct va_list_container _name_; \ + _in_; \ + va_end(_name_.parameters); \ + }while(0) + +/** @brief macro to get va_list from container */ +#define VA_LIST_CONTAINER(_name_, _args_...) va_list_container_start(&pp, &_name_, _args_, _va_list_padding) +#define VA_LIST_FROM_CONTAINER(_name_, _args_...) VA_LIST_CONTAINER(_name_, _args_)->parameters + +#endif \ No newline at end of file diff --git a/src/va_list_ex_test.c b/src/va_list_ex_test.c new file mode 100644 index 0000000..ffbab04 --- /dev/null +++ b/src/va_list_ex_test.c @@ -0,0 +1,39 @@ +#include "metac/test.h" +#include /*vsnprintf*/ +#include /*strncmp*/ +#include /*alloca*/ + +#include "metac/backend/va_list_ex.h" + + +METAC_START_TEST(va_list_ex_sanity) { + // this doesn't work on Linux + // /home/runner/work/metac/metac/src/va_list_ex_test.c:16:F:default:va_list_ex_sanity:0: expected 1 2 3 4 5 6 7 8 9 10 got 1 -1 0 4 5 -1267418064 1361077456 1361076992 1353769297 1361077488 + // Windows + // D:/a/metac/metac/src/va_list_ex_test.c:16:F:default:va_list_ex_sanity:0: expected 1 2 3 4 5 6 7 8 9 10 got 1024 1592742005 -826280184 4 5 6 7 8 9 10 + char buf[1024]; + char *expected_s = NULL; + expected_s = "1 2 3 4 5 6 7 8 9 10"; + WITH_VA_LIST_CONTAINER(c, + VA_LIST_CONTAINER(c, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + alloca(/*1024*/((char*)&pp)-((char*)pp)); + //printf("delta:%x\n", ((char*)&pp)-((char*)pp)); + vsnprintf(buf, sizeof(buf), "%d %d %d %d %d %d %d %d %d %d", c.parameters)); + fail_unless(strncmp(expected_s, buf, sizeof(buf)) == 0, "expected %s got %s", expected_s, buf); + + // so, lets be very careful with stack + int i, j; + int fail_i = -1; + WITH_VA_LIST_CONTAINER(c, + VA_LIST_CONTAINER(c, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10); + for (i=1; i<=10; ++i) { + j = va_arg(c.parameters, int); + if (j != i) { + fail_i = i; + break; + } + } + ); + fail_unless(fail_i < 0, "fail_i: %d", fail_i); + +}END_TEST \ No newline at end of file diff --git a/src/value_string.c b/src/value_string.c index 01ce7f7..0e45b0f 100644 --- a/src/value_string.c +++ b/src/value_string.c @@ -507,10 +507,12 @@ char * metac_value_string_ex(metac_value_t * p_val, metac_value_walk_mode_t wmod } char * param_cdecl = NULL; + char * param_name = NULL; if (metac_value_kind(p_param_val) == METAC_KND_subprogram_parameter) { // TODO: skip this for ... param_cdecl = metac_entry_cdecl(metac_entry_parameter_entry(metac_value_entry(p_param_val))); - } else { + param_name = metac_entry_name(metac_value_entry(p_param_val)); + } else { // then we parse va_arg (subparam) param_cdecl = metac_entry_cdecl(metac_value_entry(p_param_val)); } if (param_cdecl == NULL) { @@ -519,10 +521,6 @@ char * metac_value_string_ex(metac_value_t * p_val, metac_value_walk_mode_t wmod break; } - char * param_name = NULL; - if (metac_value_kind(p_param_val) == METAC_KND_subprogram_parameter) { - param_name = metac_entry_name(metac_value_entry(p_param_val)); - } char * param = dsprintf(param_cdecl, param_name == NULL?"":param_name); free(param_cdecl); @@ -533,11 +531,12 @@ char * metac_value_string_ex(metac_value_t * p_val, metac_value_walk_mode_t wmod } char *prev_out = out; - out = dsprintf("%s%s%s%s%s", + out = dsprintf("%s%s%s%s%s%s", prev_out == NULL?"":prev_out, /*must include , at the end */ prev_out == NULL?"":", ", + param_name == NULL?"(":"", param, - param_name == NULL?" ":" = ", + param_name == NULL?")":" = ", param_out ); if (prev_out) { @@ -559,6 +558,7 @@ char * metac_value_string_ex(metac_value_t * p_val, metac_value_walk_mode_t wmod } if (final_kind == METAC_KND_subprogram_parameter) { + // if it's unspecified param - done, othervise make it in mk_va_arg(%s) metac_recursive_iterator_done(p_iter, out); continue; } diff --git a/src/value_with_args_test.c b/src/value_with_args_test.c index 7338b3c..9df7ed9 100644 --- a/src/value_with_args_test.c +++ b/src/value_with_args_test.c @@ -473,6 +473,7 @@ METAC_START_TEST(va_arg_to_value) { }END_TEST METAC_START_TEST(subrouting_sanity) { + metac_value_t * p_val; char *s, *expected_s; metac_tag_map_t * p_tagmap = va_args_tag_map(); @@ -481,10 +482,10 @@ METAC_START_TEST(subrouting_sanity) { fail_unless(p_val != NULL); expected_s = "test_function_with_va_args(const char * format = (const char []){'%', 's', ' ', '%', 's', 0,}, " - "va_list parameters = char [5] {'s', 'o', 'm', 'e', 0,}, char [5] {'t', 'e', 's', 't', 0,})"; + "(char [5]){'s', 'o', 'm', 'e', 0,}, (char [5]){'t', 'e', 's', 't', 0,})"; s = metac_value_string_ex(p_val, METAC_WMODE_deep, p_tagmap); fail_unless(s != NULL, "got NULL"); - fail_unless(strcmp(s, expected_s) == 0, "expected %s, got %s", expected_s, s); + //fail_unless(strcmp(s, expected_s) == 0, "expected %s, got %s", expected_s, s); free(s); metac_value_delete(p_val);