Reputation: 43
I try to write a wrapper for printf in C++. Of course i found va_list
but dont know how its applicable to me because
the wrapper will not be called directly. I will show later on.
I parse a script that includes a function with an unknown number of arguments like
ASTNode node(Token(PRINT, PRINT));
consume(PRINT);
consume(LPAREN);
node.make_child(variable()); // <-- formatstring (node.child[int])
while(current_token._type() != RPAREN) {
consume(COMMA);
node.make_child(variable()); // <-- the values to replace in formatstring (node.child[int++])
i++;
}
consume(RPAREN);
return node;
The first will be the formatstring and the others will be the values to replace in the format string so the function where i execute it will look like
if(node._token()._type() == PRINT) {
Token formatstring = visit(*node.child[0]);
Token temp;
int i = 1;
while(i < node.child.size()) {
visit(*node.child[i++]); // <-- the values to replace in formatstring
}
}
and doesnt take any "real" parameter. How can i build a dynamic parameter array using va_list or another method?
Thank you
Edit It seems my question is unclear to someone..
printf is called like printf(formatstring, param1, param2, param3...)
and i want to do build the parameters passed after the first parameter (formatstring) in a loop like
// Pseudocode
out("printf(");
out($myformatstring);
int i = 1;
while(i<parameter_count) {
out(parameter[i++]);
out(",");
}
out(")");
Upvotes: 0
Views: 2210
Reputation: 1
this assembly function can call any function pointer with dynamic arguments at runtime provided that the called function has x64 calling convention windows variadic functions expect floats to be doubles
functions that take struct or bigger than 8 bytes types are passed by pointer
return structs usually are passed as a pointer to called function in the first argument
;#define ARG_Type_FLOAT 0
;#define ARG_Type_DOUBLE 0
;#define ARG_Type_SCALAR 2
;extern "C" size_t dynamic_invoke(void* func, size_t arg_count, size_t * arguments, size_t * argument_types);
;extern "C" double dynamic_invoke(void* func, size_t arg_count, size_t * arguments, size_t * argument_types);
;extern "C" float dynamic_invoke(void* func, size_t arg_count, size_t * arguments, size_t * argument_types);
.code
dynamic_invoke PROC
; rcx pointer, rdx arg_count, r8 args_pointer, r9 args_types_pointer
push rbp
mov rbp, rsp
; save non volatile registers
sub rsp, 32
sub rsp, 48
mov qword ptr [rbp - 8], rdi
mov qword ptr [rbp - 16], rsi
mov qword ptr [rbp - 24], r12
mov qword ptr [rbp - 32], r13
mov qword ptr [rbp - 40], r14
mov qword ptr [rbp - 48], r15
; compute stack needed for arguments (overestimated)
mov r15, rdx
imul r15, 8
sub rsp, r15
; store allocated stack size in r15
mov r15, rbp
sub r15, rsp
mov r14, rcx ; pointer
mov r13, rdx ; arg_count
mov r12, r8 ; arg_pointer
mov r11, r9 ; args_types_pointer
mov rdi, 32 ; stack_top_offset
mov rsi, 0
_loop:
mov r10, qword ptr [r11 + rsi * 8]
; arg 0
cmp rsi, 0
jne skip_0
mov rcx, qword ptr [r12 + rsi * 8]
cmp r10, 0
jne skip_f_0
movss xmm0, dword ptr [r12 + rsi * 8]
skip_f_0:
cmp r10, 1
jne skip_d_0
movsd xmm0, qword ptr [r12 + rsi * 8]
skip_d_0:
skip_0:
; arg 1
cmp rsi, 1
jne skip_1
mov rdx, qword ptr [r12 + rsi * 8]
cmp r10, 0
jne skip_f_1
movss xmm1, dword ptr [r12 + rsi * 8]
skip_f_1:
cmp r10, 1
jne skip_d_1
movsd xmm1, qword ptr [r12 + rsi * 8]
skip_d_1:
skip_1:
; arg 2
cmp rsi, 2
jne skip_2
mov r8, qword ptr [r12 + rsi * 8]
cmp r10, 0
jne skip_f_2
movss xmm2, dword ptr [r12 + rsi * 8]
skip_f_2:
cmp r10, 1
jne skip_d_2
movsd xmm2, qword ptr [r12 + rsi * 8]
skip_d_2:
skip_2:
; arg 3
cmp rsi, 3
jne skip_3
mov r9, qword ptr [r12 + rsi * 8]
; float
cmp r10, 0
jne skip_f_3
movss xmm3, dword ptr [r12 + rsi * 8]
skip_f_3:
; double
cmp r10, 1
jne skip_d_3
movsd xmm3, qword ptr [r12 + rsi * 8]
skip_d_3:
skip_3:
; stack args
cmp rsi, 3
jle skip_rest
mov r10, qword ptr [r12 + rsi * 8]
mov qword ptr [rsp + rdi], r10
add rdi, 8
skip_rest:
inc rsi
cmp rsi, r13
jl _loop
call r14
add rsp, r15
; restore scratch
mov rdi, qword ptr [rbp - 8]
mov rsi, qword ptr [rbp - 16]
mov r12, qword ptr [rbp - 24]
mov r13, qword ptr [rbp - 32]
mov r14, qword ptr [rbp - 40]
mov r15, qword ptr [rbp - 48]
pop rbp
ret
dynamic_invoke ENDP
END
Upvotes: 0
Reputation: 188
/*
there is example that explains how to use stdarg for a function
with variable number of parameters
*/
#include <stdio.h>
#include <stdarg.h>
void myfunc(char* fmt, ...);
/* this example folows logic of printf to specify format
%d - integer
%c - single character
%s - string
other character will be copied to output
*/
int main( int argc, char* argv[])
{
myfunc("%s %d ABCD %c", "a string", 4, 'D');
}
void myfunc(char *fmt, ...)
{
va_list ap;
int d;
char c, *s;
va_start(ap, fmt);
while (*fmt)
{
if(*fmt == '%')
{
fmt++;
switch (*fmt) // detect format characters
{
case 's': /* string format character is recognized*/
s = va_arg(ap, char *);
printf("string: %s\n", s); /* this example just print out paramater, you can do what you want */
break;
case 'd': /* integer format character */
d = va_arg(ap, int);
printf("int: %d\n", d);
break;
case 'c': /* char format character*/
c = (char) va_arg(ap, int);
printf("char: %c\n", c);
break;
default:
printf("Just copy non format characters %c\n",*fmt); // copy non format characters after %
}
}
else
printf("just copy '%c'\n", *fmt);
fmt++;
}
va_end(ap);
}
Upvotes: 0
Reputation: 217980
As I understand you have a given format string, and "read/parse" arguments.
You have so 2 problems to handle, handling the format and using correct type for argument.
printf
family doesn't support partial replacement (contrary to Qt for example, which allow QString("%1 %2").arg("Hello")
resulting into QString("Hello %2")
which allow chaining).
So you have to parse the full format string manually:
when %
is encountered, retrieve the flag format (unless it is %%
, in that case display %
directly).
from flag format, switch to appropriate conversion, something like
// flagFormat would "simply" be %i, %010d, %4.2f or %+.0e
switch (format_type) {
case EFormatType::String: // %s
printf(flagFormat, to_string(args[i]).c_str()); break;
case EFormatType::Int: // %i, %d
printf(flagFormat, to_int(args[i])); break;
case EFormatType::String: // %f, %F, %e, %g, %
printf(flagFormat, to_double(args[i])); break;
// ...
}
++i;
Upvotes: 1
Reputation: 188
// Pseudocode
// I would like to propose a way to solve the problem, added some stuff here
out("printf(");
// you should create format string in accordance with types of parameters
$myformatstring = "" // let suppose myformatstring is of type "string"
// and it has defined operator "+=" (concatenation of strings)
for(int j = 0;j < parametar_count; j++)
{
switch(parametar[j].type ) // suppose that you have type information of param's
{
case 'i': myformatstring += " %d "; break;
case 'f': myformatstring += " %f "; break;
case 's': myformatstring += " %s"; break;
... // you should handle all types you use ...
}
}
$myformatstring += "\\n\","; // terminate format string and write a comma before
// other arguments...
out($myformatstring);
int i = 1;
while(i<parameter_count) {
out(parameter[i++]);
if( i < parameter_count -1 ) // you don't need comma after last parameter
out(",");
}
out(");");
Upvotes: 0