Skip to content

Commit

Permalink
adding va_list helper
Browse files Browse the repository at this point in the history
  • Loading branch information
aodinokov committed Jul 26, 2024
1 parent db4500e commit 4bf0173
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 10 deletions.
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
"stdint.h": "c",
"test.h": "c",
"__locale": "c",
"ios": "c"
"ios": "c",
"va_list_ex.h": "c"
}
}
33 changes: 33 additions & 0 deletions include/metac/backend/va_list_ex.h
Original file line number Diff line number Diff line change
@@ -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
39 changes: 39 additions & 0 deletions src/va_list_ex_test.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
#include "metac/test.h"
#include <stdio.h> /*vsnprintf*/
#include <string.h> /*strncmp*/
#include <stdlib.h> /*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
14 changes: 7 additions & 7 deletions src/value_string.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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);
Expand All @@ -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) {
Expand All @@ -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;
}
Expand Down
5 changes: 3 additions & 2 deletions src/value_with_args_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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);
Expand Down

0 comments on commit 4bf0173

Please sign in to comment.