How can I wrap a function with variable length arguments? - c++

I am looking to do this in C/C++. I came across Variable Length Arguments, but this suggests a solution with Python and C using libffi.
Now, if I want to wrap the printf function with myprintf.
I do it like below:
void myprintf(char* fmt, ...)
{
va_list args;
va_start(args, fmt);
printf(fmt, args);
va_end(args);
}
int _tmain(int argc, _TCHAR* argv[])
{
int a = 9;
int b = 10;
char v = 'C';
myprintf("This is a number: %d and \nthis is a character: %c and \n another number: %d\n", a, v, b);
return 0;
}
But the results are not as expected!
This is a number: 1244780 and
this is a character: h and
another number: 29953463
What did I miss?

The problem is that you cannot use 'printf' with va_args. You must use vprintf if you are using variable argument lists. vprint, vsprintf, vfprintf, etc. (there are also 'safe' versions in Microsoft's C runtime that will prevent buffer overruns, etc.)
You sample works as follows:
void myprintf(char* fmt, ...)
{
va_list args;
va_start(args, fmt);
vprintf(fmt, args);
va_end(args);
}
int _tmain(int argc, _TCHAR* argv[])
{
int a = 9;
int b = 10;
char v = 'C';
myprintf("This is a number: %d and \nthis is a character: %c and \n another number: %d\n", a, v, b);
return 0;
}

In C++11, this is one possible solution using variadic templates:
template<typename... Args>
void myprintf(const char* fmt, Args... args)
{
std::printf(fmt, args...);
}
As rubenvb points out, there are trade-offs to consider. For example, you will be generating code for each instance which will lead to code bloat.

I am also unsure what you mean by pure.
In C++ we use:
#include <cstdarg>
#include <cstdio>
class Foo
{
void Write(const char* pMsg, ...);
};
void Foo::Write( const char* pMsg, ...)
{
char buffer[4096];
std::va_list arg;
va_start(arg, pMsg);
std::vsnprintf(buffer, 4096, pMsg, arg);
va_end(arg);
...
}

Actually, there's a way to call a function that doesn’t have a va_list version from a wrapper. The idea is to use assembler, do not touch arguments on the stack, and temporarily replace the function return address.
An example for Visual C x86. call addr_printf calls printf():
__declspec( thread ) static void* _tls_ret;
static void __stdcall saveret(void *retaddr) {
_tls_ret = retaddr;
}
static void* __stdcall _getret() {
return _tls_ret;
}
__declspec(naked)
static void __stdcall restret_and_return_int(int retval) {
__asm {
call _getret
mov [esp], eax ; /* replace current retaddr with saved */
mov eax, [esp+4] ; /* retval */
ret 4
}
}
static void __stdcall _dbg_printf_beg(const char *fmt, va_list args) {
printf("calling printf(\"%s\")\n", fmt);
}
static void __stdcall _dbg_printf_end(int ret) {
printf("printf() returned %d\n", ret);
}
__declspec(naked)
int dbg_printf(const char *fmt, ...)
{
static const void *addr_printf = printf;
/* prolog */
__asm {
push ebp
mov ebp, esp
sub esp, __LOCAL_SIZE
nop
}
{
va_list args;
va_start(args, fmt);
_dbg_printf_beg(fmt, args);
va_end(args);
}
/* epilog */
__asm {
mov esp, ebp
pop ebp
}
__asm {
call saveret
call addr_printf
push eax
push eax
call _dbg_printf_end
call restret_and_return_int
}
}

Are you using C or C++? The next C++ version, C++0x, will support variadic templates which provide a solution to that problem.
Another workaround can be achieved by clever operator overloading to achieve a syntax like this:
void f(varargs va) {
BOOST_FOREACH(varargs::iterator i, va)
cout << *i << " ";
}
f(args = 1, 2, 3, "Hello");
In order to get this to work, the class varargs has to be implemented to override operator = that returns a proxy object which, in turn, overrides operator ,. However, making this variant type safe in current C++ isn't possible as far as I know since it would have to work by type erasure.

How do you mean a pure C/C++ solution?
The rest parameter (...) is supported cross platform in the C runtime.
va_arg, va_copy, va_end, va_start

void myprintf(char* fmt, ...)
{
va_ list args;
va_ start(args, fmt);
printf(fmt, args); // This is the fault. "vprintf(fmt, args);"
// should have been used.
va_ end(args);
}
If you're just trying to call printf,
there's a printf variant called vprintf that takes
the va_list directly: vprintf(fmt, args);

Related

How to overload a function taking variable number of arguments

I need to distinguish (overload) between two functions - one takes a single const char* argument and the other takes at least two arguments - a const char* followed by one or more arguments.
i.e. basically:
void f(const char *str);
void f(const char *format, ...)
I want the first version to be called for f("hello") and the second version for f("hello %d", 10). The above overload won't work because the compiler finds f("hello") ambiguous.
So I tried this:
void f(const char *str);
template<typename T>
void f(const char *str, T tt, ...);
This makes the overload resolution work correctly. But I end up with another problem. The second function is supposed to forward the arguments for printf-style usage. So I have something like:
template <typename T>
void f ( const char *format, T tt, ... )
{
(T)tt;
va_list varlist;
va_start(varlist, format);
vprintf(format, varlist);
va_end(varlist);
}
Now the second argument tt is no longer part of the variable argument list and calling va_start() with format does not seem to work.
Is there any way to achieve what I want?
If you use a vararg template you can accomplish what you want:
#include <iostream>
void f(const char* str)
{
std::cout << "single arg: " << str << "\n";
}
template <typename ...T>
void f(const char *format, T... args)
{
printf(format, args...);
}
int main()
{
f("hello");
f("formatted %d", 10);
}
Maybe using templates is not the best idea, since the second error seems more difficult to fix than the first. The overload of the functions is fine, it is more if you try in a separate file you will see that it works.
#include <iostream>
bool f(const char* str) {
return true;
}
bool f(const char* str,int n) {
return false;
}
int main(int argc,char* argv[]) {
std::cout << f("Hi") << std::endl; //Gives 1
std::cout << f("Hi",10) << std::endl; //Gives 0
return 0;
}
You could also try creating a namespace and putting the functions in there, thus making sure that no other files share that function name that can cause that error.
Q: why compile error?
A: becauz scope of "void f(const char *format, ...)" had cover "void f(const char *str);".
so the cute solution is you should better using arg num to distinguish 2 func.
like this:
void func_with_one_arg(const char *str) {
printf("func_with_one_arg");
}
void f(int arg_num, const char *format, ...) {
if (arg_num==1) return func_with_one_arg(format);
printf("func_with_multi_arg\n");
va_list ap;
va_start(ap, format);
va_end(ap);
}
if you want to automately coumpute variable arg_num, use this macro to help computing.
#define VA_LENGTH_(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, _16, _17, _18, _19, _20, N, ...) N
#define VA_LENGTH(...) VA_LENGTH_(0, ## __VA_ARGS__, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
#define ExecVF(Func, ...) Func(VA_LENGTH(__VA_ARGS__), __VA_ARGS__)
now you should use ExecVF like this:
int
main(int argc, char **argv) {
ExecVF(f, "foo", "bar");
ExecVF(f, "foo");
return 0;
}

Incorrect function call with variable parameters

I am trying to make a nested function call with a variable number of parameters without retrieving them. And I get the wrong result.
Here is my simple c++ program:
extern "C" {
#include <stdio.h>
}
#include <cstdarg>
class ctty {
public:
ctty();
int logger(int prior, const char* format, ...);
private:
};
ctty::ctty(){};
int ctty::logger(int prior, const char* format, ...)
{
va_list ap;
va_start(ap,format);
printf(format, ap);
va_end(ap);
return 0;
}
int main(int argc, char** argv)
{
ctty tty;
tty.logger(0, "Test %d %d %d\n", 7, 5, 5);
return 0;
}
result:
Test -205200 7 5
I expect a result
Test 7 5 5
I don’t understand what am I doing wrong?
Thanks in advance.
You can't directly pass va_list to printf. va_list is a wrapper around the actual list of arguments (whatever its representation is). Although C way to do should be to use vprintf, in C++ there are safer alternatives, like variadic templates allowing to create a safer version of formatted printing, e.g. (a fictional format string for brevity of example):
#include <iostream>
#include <cstdlib>
class ctty {
public:
ctty();
template<typename T, typename... Args>
int logger(int prior, const char* format, T value, Args... args);
private:
void logger(int prior, const char *s);
};
ctty::ctty(){};
void ctty::logger(int prior, const char *s)
{
while (*s) {
if (*s == '%') {
if (*(s + 1) == '%') {
++s;
}
else {
throw std::runtime_error("invalid format string: missing arguments");
}
}
std::cout << *s++;
}
}
template<typename T, typename... Args>
int ctty::logger(int prior, const char* format, T value, Args... args)
{
while (*format) {
if (*format == '%') {
std::cout << value;
logger(prior, format + 1, args...);
return 0;
}
std::cout << *format++;
}
throw std::logic_error("extra arguments provided to logger");
}
int main(int argc, char** argv)
{
ctty tty;
tty.logger(0, "Test % % %\n", 7.55f, "Tada!", 888);
return 0;
}
This part of your code:
extern "C" {
#include <stdio.h>
}
Is technically an undefined behavior, while it may compile and not have adverse effect in some cases, it's not portable. You have to use C++ headers, e.g. <cstdio>

Can't compile lambda when I want to catch something outside

When I want to catch something outside the lambda, the compiling errors occurs. here is the code:
int caller(int (*callback)(void *arg), void * arg = NULL) {
return callback(arg);
}
int main(int argc, char **argv) {
const char *str = "world";
caller([&](void *arg) {
printf("hello %s\n", str);
return 0;
}, NULL);
return 0;
}
But it's ok if I change the printf inside lambda like this (not using the outside variables):
printf("hello %s\n", str)
I tried to compile the source with apple g++ 4.2 and gnu g++ 4.6
Lambdas can only be converted to function pointers when they are stateles, ie have no captures defined.
Have a look here and read about ClosureType::operator ret(*)(params)()
EDIT:
If it is up to you to define the callback parameter, define it using std::function, which can take any type of callable object, including lambdas. See below, it should compile fine with it (#include <functional> though).
int caller(std::function<int(void*)> callback, void * arg = NULL) {
return callback(arg);
}
If you can't change the callback, then you should just pass str as the second parameter to the callback and print it out in lambda, as in here:
caller([](void *arg) {
printf("hello %s\n", (const char*)arg);
return 0;
}, str);
(I know this actually won't compile due to const non const conversion, but you get what I mean).

Using a #define substitution to standardized return arguments and function arguments

I am going to have many functions with the same signatures (return argument and function parameters) and these parameters are long and may change over time. But I will have potentially hundreds of these functions and I thought it would convenient to use a #define macro to state what the arguments should be so that I only have to change them in one place, like so:
#define ARGS const Object& ref, float x, float y
#define RET QVector<Object>
Such that I could use it like this:
RET func1(ARGS); //extern header declaration
But using #define like this doesn't compile. Is there a trick to getting #define to do this, or is it simply not possible?
Suppose all of your functions accept two float arguments and return an int, you can have:
#define FUN(name) int (name)(float x, float y)
FUN(func1)
{
std::cout << "func1" << std::endl;
return 1;
}
FUN(func2)
{
std::cout << "func2" << std::endl;
return 2;
}
// use of functions
// func1(1.0, 1.0);
// func2(2.0, 2.0);
#include<stdio.h>
#define RET void
#define ARGS (int a,int b)
RET func ARGS;
int main()
{
func(10,20);
}
RET func ARGS
{
printf("a and b is %d and %d",a,b);
return;
}
This is what you want?
This works for me.

va_arg with a list of void*

I would like to create a function that takes a variable number of void pointers,
val=va_arg(vl,void*);
but above doesn't work, is there portable way to achieve this using some other type instead of void*?
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
void
myfunc(void *ptr, ...)
{
va_list va;
void *p;
va_start(va, ptr);
for (p = ptr; p != NULL; p = va_arg(va, void *)) {
printf("%p\n", p);
}
va_end(va);
}
int
main() {
myfunc(main,
myfunc,
printf,
NULL);
return 0;
}
I'm using Fedora 14..
Since you have a C++ tag, I'm going to say "don't do it this way". Instead, either use insertion operators like streams do OR just pass a (const) std::vector<void*>& as the only parameter to your function.
Then you don't have to worry about the issues with varargs.