I want to write a variadic macro that somehow knows the names of the arguments passed.
For example:
The code:
int x = 2;
float f = 4.6;
char c = 'A';
char* str = "Bla bla";
PRINT("%d %f %c %s", x, f, c, str); // calling the macro
shall produce the output
x=2 f=4.6 c=A str=Bla bla.
Hope someone knows the answer to that.
Close but not exactly (only works for single expression) what the asker required:
#define PRINT(fmt, var) printf(#var " = " fmt, (var))
Here is an example:
#include <stdio.h>
#include <stdlib.h>
#define PRINT(fmt, var) printf(#var " = " fmt, (var))
int
main(int argc, char *argv[])
{
int x = 2, y = 3;
float f = 4.6;
char c = 'A';
char *str = "Bla bla";
PRINT("%d\n", x);
PRINT("%f\n", f);
PRINT("%c\n", c);
PRINT("%s\n", str);
PRINT("%d\n", x+y);
exit(EXIT_SUCCESS);
}
I don't think you can achieve what you want.
But something like this might be yours:
#define PRINT(fmt, val) fprintf(stderr, "%s=" fmt, #val, val)
...
int x = 2;
float f = 4.6;
char c = 'A';
char* str = "Bla bla";
// calling the macro:
PRINT("%d ", x);
PRINT("%f ", f);
PRINT("%c ", c);
PRINT("%s\n", str);
Read carefully the documentation of cpp. In particular, about macro arguments, stringification, concatenation, and variadic macros. It is well explained there.
You probably might not be able to achieve exactly what you want, because you need to split the format string.
Perhaps lower your goals (e.g. accept only one argument for PRINT, see this or that answers) or consider using a more powerful preprocessor like GPP.
You could also perhaps customize GCC (by adding your builtins) with e.g. MELT but that is probably not worth the weeks of efforts (for a newbie) required to do so.
Slightly what you may want:
#include <stdio.h>
#define STRINGIFY(x) #x, (x)
#define FPRINTF(file, fmt, ...) fprintf(file, fmt, __VA_ARGS__)
#define PRINTF(fmt, ...) FPRINTF(stdout, fmt, __VA_ARGS__)
int main(void)
{
int i = 42;
char ch = 'a';
char str[4] = "alk";
PRINTF("%s=%s, %s=%c, %s=%d\n",
STRINGIFY(str),
STRINGIFY(ch),
STRINGIFY(i)
);
/* of just use printf directly: */
printf("%s=%s, %s=%c, %s=%d\n",
STRINGIFY(str),
STRINGIFY(ch),
STRINGIFY(i)
);
return 0;
}
/Puts on Indiana Jones` hat/
I may be a tad too late, but I`m here to say that this problem does have a proper(-ish) solution.
First, some prerequisite defines (explanation here):
#define L(c, ...) \
L4(c,1,0,,,,,,,,,,,,,##__VA_ARGS__) L4(c,0,1,,,,,,,,,##__VA_ARGS__) \
L4(c,0,2,,,,, ##__VA_ARGS__) L4(c,0,3, ##__VA_ARGS__)
#define L4(c, f, n, ...) \
L3(c,f,n##0,,,,__VA_ARGS__) L3(c,0,n##1,,,__VA_ARGS__) \
L3(c,0,n##2,, __VA_ARGS__) L3(c,0,n##3, __VA_ARGS__)
#define L3(...) L2(__VA_ARGS__, \
1,1,1,1, 1,1,1,1, 1,1,1,1, 1,1,1,1, 0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, )
#define L2(c, f, \
n00,n01,n02,n03, n04,n05,n06,n07, n08,n09,n0A,n0B, n0C,n0D,n0E,n0F, \
a00,a01,a02,a03, a04,a05,a06,a07, a08,a09,a0A,a0B, a0C,a0D,a0E,a0F, \
s, ...) L##s(c, f, n00, a00)
#define L1(c, f, n, a) c##f(n, a)
#define L0(c, f, n, a)
Then the code, which is actually an extension of #alk`s answer:
#include <stdio.h>
#define STRING1(n, a) #a, (a)
#define STRING0(n, a) , STRING1(n, a)
#define PRINTF(fmt, ...) printf(fmt, L(STRING, __VA_ARGS__))
int main(int argc, char *argv[]) {
int i = 42;
char ch = 'a';
char str[4] = "alk";
/** every var must be preceded with '%s' for its name to be shown **/
PRINTF("%s=%s, %s=%c, %s=%d\n", str, ch, i);
return 0;
}
This version is only suited for [0..16] arguments, but it can be easily extended to any argument count, especially to powers of 2. However, the more arguments it supports, the less elegant it looks.
P.S.: #BasileStarynkevitch has already provided all the right links to clarify how this works.
The next works for me in gcc 4.7 :
#include <stdio.h>
#define PRINT(...) fprintf (stderr, __VA_ARGS__)
int main()
{
int x = 2;
float f = 4.6;
char c = 'A';
char* str = "Bla bla";
PRINT("%d %f %c %s", x, f, c, str); // calling the macro
}
(please note that i have edited the macro invocation changing a %s for a %c)
Regards
Related
okay this sounds weird, the title is kinda
confusing but heres what i mean basically :
#define make_name(id) cool_name_##id
#define my_cool_macro(id, type) void fun_##make_name(id)(type arg1)
my_cool_macro(hello, char);
this should expand to
/*
* passed through my_cool_macro
* vvvvv
*/
void fun_cool_name_hello(char arg1);
/* ~~~~^^^^^^^^^^^^^^^ */
~ -- my_cool_macro
^ -- make_name
i dont have a better way to explain this,
any way to make this work ?
basically :
define a function-like macro which can generate names
define another function-like macro which calls to the defined name creating macro with an argument passed to the function creator macro
why am i doing this
im kinda trying to implement templaces in c using macros and as you know templated functions can have different types and i want to mangle the names but not repeat the cool_name_... because what if i want to change the cool_name to some_name
the solution should also work similarly to my example, so fun_##make_name(hello) should work as should hello_##make_name(hello) and ewuihewuifh_##make_name(hello) without me needing to define a macro for every change
You can do:
#include <stdio.h>
#define join(x, y) x##y
#define make_name_l(fn, id, arg_type) fn##id(arg_type)
#define make_name(fn, id, arg_type) make_name_l(fn, id, arg_type)
#define my_cool_macro(ret_type, id, arg_type) ret_type make_name(join (fun_, cool_name_), id, arg_type)
my_cool_macro(void, hello, char);
int main (void) {
fun_cool_name_hello('a');
return 0;
}
void fun_cool_name_hello (char a) {
printf ("In function : %s, arg : %c\n", __func__, a);
}
Additional:
In case, if you want to make it work with any type or number of arguments then you can use ellipsis (...) and __VA_ARGS__.
include <stdio.h>
#define join(x, y) x##y
#define make_name_l(fn, id, ...) fn##id(__VA_ARGS__)
#define make_name(fn, id, ...) make_name_l(fn, id, __VA_ARGS__)
#define my_cool_macro(ret_type, id, ...) ret_type make_name(join (fun_, cool_name_), id, __VA_ARGS__)
my_cool_macro(void, hello, char);
my_cool_macro(int, multiple_args, char, int, const char *);
my_cool_macro(void, no_args, void);
int main (void) {
const char * str = "test";
fun_cool_name_hello('a');
fun_cool_name_multiple_args('x', 5, str);
fun_cool_name_no_args();
return 0;
}
void fun_cool_name_hello (char a) {
printf ("In function : %s, arg : %c\n", __func__, a);
}
int fun_cool_name_multiple_args (char c, int i, const char * pstr) {
printf ("In function : %s, c : %c, i : %d, str : %s\n", __func__, c, i, pstr);
return i;
}
void fun_cool_name_no_args (void) {
printf ("In function : %s\n", __func__);
}
Output:
# ./a.out
In function : fun_cool_name_hello, arg : a
In function : fun_cool_name_multiple_args, c : x, i : 5, str : test
In function : fun_cool_name_no_args
https://godbolt.org/z/d67MWjasz
You could have a macro that creates the prototype along with its type, as well as one that makes your custom name.
#define name(f) cool_name_##f
#define make_function(type, name, arg) type fun_name(arg)
#include <stdio.h>
#define name(f) cool_name_##f
#define make_function(type, name, arg) type fun_name(arg)
make_function(void, name(hello), int);
void cool_name_fun_hello(int f) {
printf("%d\n", f);
}
int main(void) {
cool_name_fun_hello(1);
}
Having this #define PRINTF(...) printf(__VA_ARGS__) I want to create a macro that calls PRINTF but that adds prefix to the printed string, for example:
#define PRINTF_P(<args>) PRINTF(<whatever>)
// let's suppose that the desired prefix is 'prefix - '
PRINTF_P("Hello world\n");
PRINTF_P("Hello world, num = %d\n", 25);
// Result:
prefix - Hello world
prefix - Hello world, num = 20
How can I do this?
What I have tried
The following works for calls like PRINTF_P("string with argument %d\n", arg), but it doesn't for calls like `PRINTF_P("string with no argument\n");
#include <stdio.h>
#include <stdarg.h>
#define PRINTF(...) printf(__VA_ARGS__)
#define A(fmt, ...) fmt
#define B(fmt, ...) __VA_ARGS__
#define PRINTF_P(...) printf( "prefix - " A(__VA_ARGS__), B(__VA_ARGS__))
int main(void)
{
PRINTF_P("No number\n"); // This fails
PRINTF_P("Number = %d\n", 20); // This works
return 0;
}
EDIT
I'm referring only to the case of string literals, not char *.
It is as easy as:
#include <stdio.h>
#include <stdarg.h>
#define PRINTF(...) printf(__VA_ARGS__)
#define PRINTF_P(...) printf( "prefix " __VA_ARGS__)
int main(void)
{
PRINTF("No number\n");
PRINTF("Number = %d\n", 20);
PRINTF_P("No number\n");
PRINTF_P("Number = %d\n", 20);
return 0;
}
I'm not sure why you should use a macro at all.
Just write an encapsulating function using vprintf and variable args.
IDEOne Link
#include <stdarg.h>
int prefix_printf(const char* fmt, ...)
{
printf("prefix "); // Print desired Prefix
va_list args; // Declare a variable for the "..."
va_start(args, fmt); // Initialize the args as everything after parameter "fmt"
int result = vprintf(fmt, args); // Variant of printf that accepts args instead of ...
va_end(args); // Clean up the args
return result; // Return the same value printf would've returned
}
int main(void) {
prefix_printf("No number\n");
prefix_printf("Number = %d\n", 20);
return 0;
}
Result:
Success #stdin #stdout 0.01s 5536KB
prefix No number
prefix Number = 20
I'm trying to pass mixed types to printf(). In the actual code (at the bottom) I also pass the format placeholder so that printf() knows how to format.
While accidently using these void* pointers directly as call by values, I discovered that my code was not working as expected.
So I created a test program, searched and poked around until I came up with this solution.
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
void test1(void* a) {
printf("value: %d\n", *(int*)a);
}
void test2(void* a) {
printf("value: %f\n", *(float*)a);
}
int main(int argc, char** argv) {
int* aip = &(int){1};
test1(aip);
int ai = 1;
test1(&ai);
float af = 1.75;
test2(&af);
return 0;
}
But I don't want hardcoded *(int*) or *(float*) dereferencing and casting but actually write something like this:
void test(void* a) {
printf("value: %VARIES\n", a);
}
VARIES is passed into test() and concatenated into the final formatter string.
The actual function looks like this:
void fn(void* value, char* fmtChar) {
char fmtHelper[50];
// fill fmtChar in formatter string's %s placeholder
snprintf(fmtHelper, sizeof(fmtHelper), "value : %s\n", fmtChar);
// actually print value varying argument
printf(fmtHelper, value);
}
My curent workaround uses switch/case statements and determines the casting and formatter placeholders programmatically.
If you're targeting current C (C11), you can use a type-generic expression with a macro to make a type-generic function:
#include <stdio.h>
void print_int(int x) {
printf("an int: %d\n", x);
}
void print_double(double x) {
printf("a double: %f\n", x);
}
#define print(X) _Generic((X), \
float: print_double, \
double: print_double, \
default: print_int \
)(X)
int main(void) {
print(1); // an int: 1
print(1.0); // a double: 1.00000
}
This solution uses vprintf and va_list macros, so they're resolved during the preprocessing of the source.
#include <stdio.h>
#include <stdarg.h>
void print(char* msg, char* fmtChars, ...) {
// format string and write the result to fmtHelper
// %s is replaced with the value placeholder types
// (fixed length for simplicity)
char fmtHelper[50];
snprintf(fmtHelper, sizeof(fmtHelper), "%s; value 1: %s, value 2: %s\n", msg, fmtChars, fmtChars);
va_list args;
// treat all parameters _after_ this named as variable arg
va_start(args, fmtChars);
vprintf(fmtHelper, args);
va_end(args);
}
int main(void) {
char* msg = "Test";
int in1 = 1;
int in2 = 2;
float fl1 = 4.21;
float fl2 = 5.89;
print(msg, "%f", fl1, fl2);
print(msg, "%d", in1, in2);
}
This was a hint #rici gave in the comments section.
I am attempting to implement function overloading in C, and I am very close. I am using C99 so the _Generic keyword introduced in C11 is not available to me. I have developed some working code, but when I compile it I get a couple warnings.
Working example:
#include <stdio.h>
#define print(x) \
__builtin_choose_expr(__builtin_types_compatible_p(typeof(x), int ), print_int(x) , \
__builtin_choose_expr(__builtin_types_compatible_p(typeof(x), char[]), print_string(x), \
(void)0))
void print_int(int i) {
printf("int: %d\n", i);
}
void print_string(char* s) {
printf("char*: %s\n", s);
}
int main(int argc, char* argv[]) {
print(1);
print("this");
return 0;
}
Compiling creates the following warnings:
gcc overload.c -o main
overload.c: In function 'main':
overload.c:19: warning: passing argument 1 of 'print_string' makes pointer from integer without a cast
overload.c:20: warning: passing argument 1 of 'print_int' makes integer from pointer without a cast
For a little more debugging information, here is what the main function looks like after the preprocessor does its work:
int main(int argc, char* argv[]) {
__builtin_choose_expr(__builtin_types_compatible_p(typeof(1), int ), print_int(1) , __builtin_choose_expr(__builtin_types_compatible_p(typeof(1), char[]), print_string(1), (void)0));
__builtin_choose_expr(__builtin_types_compatible_p(typeof("this"), int ), print_int("this") , __builtin_choose_expr(__builtin_types_compatible_p(typeof("this"), char[]), print_string("this"), (void)0));
return 0;
}
How can I make the compilation warnings go away and still have working code?
In theory, this should work:
#define print(x) \
(__builtin_choose_expr(__builtin_types_compatible_p(typeof(x), int ), print_int , \
__builtin_choose_expr(__builtin_types_compatible_p(typeof(x), char[]), print_string, \
(void)0))(x))
It chooses either print_int or print_string, then applies the chosen function to x.
The GNU page on the build-ins says that the branches not chosen may still generate syntax errors, but I fail to see how mismatched types are syntax errors.
Anyway, you can get rid of the warnings when you move the arguments to the function out of the type-dependent choice. So (in pseudocode in order to make it more readable), instead of
choose_type_of(x, int, print_int(x),
choose_type_of(x, char[], print_string(x), (void) 0))
do
choose_type_of(x, int, print_int,
choose_type_of(x, char[], print_string, pass))(x)
That's what user2357112 suggested in a comment. I was working on a similar solution, but I had a hard time to get the default part (the pass above) to work. When I use (void), which should then expand to (void)(x), I get an error about an unmatched parenthesis.
The solution below creates a default printing function that doesn't use its arguments. It could probably be a non-existent function, so that there are problems when linking or something else that produces an error.
#include <stdio.h>
#define print(x) \
__builtin_choose_expr(__builtin_types_compatible_p(typeof(x), \
int), print_int, \
__builtin_choose_expr(__builtin_types_compatible_p(typeof(x), \
const char[]), print_string, \
print_any))(x)
void print_int(int i) {
printf("int: %d\n", i);
}
void print_string(const char *s) {
printf("char[]: %s\n", s);
}
void print_any() {
printf("unknown\n");
}
int main(void)
{
int n = 9;
const char str[] = "hello";
print(n);
print(str);
print(1);
print("this");
print(3.2);
return 0;
}
Here's an example with several methods that achieve function overloading.
One poster mentioned this
The GNU page on the build-ins says that the branches not chosen may
still generate syntax errors, but I fail to see how mismatched types
are syntax errors.
The syntax errors they're referring to I believe are the compiler warnings you get because after preprocessing the compiler can see that the types of the arguments to some functions, even though they can never be called are wrong. The solution (which I think is quite neat) is to find a way to hide the types from the compiler. The obvious way is varargs, the less obvious way is is the current answer to the OP's question.
Caveats apply ie not all solutions are type safe and this is entirely specific to GNU...
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#define print(x) \
(__builtin_choose_expr(__builtin_types_compatible_p(typeof(x), int ), print_int , \
__builtin_choose_expr(__builtin_types_compatible_p(typeof(x), char[]), print_string, \
(void)0))(x))
#define print1(x) \
__builtin_choose_expr(__builtin_types_compatible_p(typeof(x), int ), print_int1(1,x) , \
__builtin_choose_expr(__builtin_types_compatible_p(typeof(x), char[]), print_string1(1,x), \
(void)0))
#define print2(x) \
__builtin_choose_expr(__builtin_types_compatible_p(typeof(x), int ), printer(1,x), \
__builtin_choose_expr(__builtin_types_compatible_p(typeof(x), char[]), printer(2,x), \
(void)0))
#define TYPE_ID(x) __builtin_types_compatible_p(typeof(x), int ) * 1 \
+ __builtin_types_compatible_p(typeof(x), char[]) * 2
#define print3(x) printer(TYPE_ID(x), x)
#define STATIC_ASSERT(COND,MSG) typedef char static_assertion_##MSG[(COND)?1:-1]
#define print4(x) \
STATIC_ASSERT(TYPE_ID(x), __LINE__); \
printer(TYPE_ID(x), x)
void printer(int i, ...) {
va_list v;
va_start(v, i);
switch(i) {
case 1:{
int arg = va_arg(v, int);
printf("int: %d\n", arg);
va_end(v);
break;
}
case 2:{
char * arg = va_arg(v, char*);
printf("char*: %s\n", arg);
va_end(v);
break;
}
default: {
fprintf(stderr, "Unknown type, abort\n");
abort();
}
}
}
void print_int(int i) {
printf("int: %d\n", i);
}
void print_string(char* s) {
printf("char*: %s\n", s);
}
void print_int1(int i, ...) {
va_list v;
va_start(v, i);
int arg = va_arg(v, int);
printf("int: %d\n", arg);
va_end(v);
}
void print_string1(int i, ...) {
va_list v;
va_start(v, i);
char * arg = va_arg(v, char*);
printf("char*: %s\n", arg);
va_end(v);
}
int main(int argc, char* argv[]) {
int var = 1729;
double var1 = 1729;
//Type safe
//print(var1);//Comple time error
print(var);
print("print");
/* Following are not Type Safe */
print1(var1);// BAD... Does nothing.
print1(var);
print1("print1");
print2(var1);// BAD... Does nothing.
print2(var);
print2("print2");
//print3(var1);//Evil... Runtime error
print3(var);
print3("print3");
//Type Safe
//print4(var1);//Comple time error
print4(var);
print4("print4");
return 0;
}
Source is on github...
https://github.com/harryjackson/doc/blob/master/c/overload_c_functions.c
The switch method with multiple arguments can be found here...
http://locklessinc.com/articles/overloading/
The whole
The warning can be suppressed by doing some type casting in the #define section, but I feel like this probably is not the best or even a good solution at all...
Change the function calls in the #define section to this:
print_string((char*)x)
print_int((int)x)
I really hope someone comes up with a better solution though, because this just feels wrong...
Say I have a C function which takes a variable number of arguments: How can I call another function which expects a variable number of arguments from inside of it, passing all the arguments that got into the first function?
Example:
void format_string(char *fmt, ...);
void debug_print(int dbg_lvl, char *fmt, ...) {
format_string(fmt, /* how do I pass all the arguments from '...'? */);
fprintf(stdout, fmt);
}
To pass the ellipses on, you initialize a va_list as usual and simply pass it to your second function. You don't use va_arg(). Specifically;
void format_string(char *fmt,va_list argptr, char *formatted_string);
void debug_print(int dbg_lvl, char *fmt, ...)
{
char formatted_string[MAX_FMT_SIZE];
va_list argptr;
va_start(argptr,fmt);
format_string(fmt, argptr, formatted_string);
va_end(argptr);
fprintf(stdout, "%s",formatted_string);
}
There's no way of calling (eg) printf without knowing how many arguments you're passing to it, unless you want to get into naughty and non-portable tricks.
The generally used solution is to always provide an alternate form of vararg functions, so printf has vprintf which takes a va_list in place of the .... The ... versions are just wrappers around the va_list versions.
Variadic Functions can be dangerous. Here's a safer trick:
void func(type* values) {
while(*values) {
x = *values++;
/* do whatever with x */
}
}
func((type[]){val1,val2,val3,val4,0});
In magnificent C++11 you could use variadic templates:
template <typename... Ts>
void format_string(char *fmt, Ts ... ts) {}
template <typename... Ts>
void debug_print(int dbg_lvl, char *fmt, Ts... ts)
{
format_string(fmt, ts...);
}
Though you can solve passing the formatter by storing it in local buffer first, but that needs stack and can sometime be issue to deal with. I tried following and it seems to work fine.
#include <stdarg.h>
#include <stdio.h>
void print(char const* fmt, ...)
{
va_list arg;
va_start(arg, fmt);
vprintf(fmt, arg);
va_end(arg);
}
void printFormatted(char const* fmt, va_list arg)
{
vprintf(fmt, arg);
}
void showLog(int mdl, char const* type, ...)
{
print("\nMDL: %d, TYPE: %s", mdl, type);
va_list arg;
va_start(arg, type);
char const* fmt = va_arg(arg, char const*);
printFormatted(fmt, arg);
va_end(arg);
}
int main()
{
int x = 3, y = 6;
showLog(1, "INF, ", "Value = %d, %d Looks Good! %s", x, y, "Infact Awesome!!");
showLog(1, "ERR");
}
Hope this helps.
You can try macro also.
#define NONE 0x00
#define DBG 0x1F
#define INFO 0x0F
#define ERR 0x07
#define EMR 0x03
#define CRIT 0x01
#define DEBUG_LEVEL ERR
#define WHERESTR "[FILE : %s, FUNC : %s, LINE : %d]: "
#define WHEREARG __FILE__,__func__,__LINE__
#define DEBUG(...) fprintf(stderr, __VA_ARGS__)
#define DEBUG_PRINT(X, _fmt, ...) if((DEBUG_LEVEL & X) == X) \
DEBUG(WHERESTR _fmt, WHEREARG,__VA_ARGS__)
int main()
{
int x=10;
DEBUG_PRINT(DBG, "i am x %d\n", x);
return 0;
}
You can use inline assembly for the function call. (in this code I assume the arguments are characters).
void format_string(char *fmt, ...);
void debug_print(int dbg_level, int numOfArgs, char *fmt, ...)
{
va_list argumentsToPass;
va_start(argumentsToPass, fmt);
char *list = new char[numOfArgs];
for(int n = 0; n < numOfArgs; n++)
list[n] = va_arg(argumentsToPass, char);
va_end(argumentsToPass);
for(int n = numOfArgs - 1; n >= 0; n--)
{
char next;
next = list[n];
__asm push next;
}
__asm push fmt;
__asm call format_string;
fprintf(stdout, fmt);
}
Ross' solution cleaned-up a bit. Only works if all args are pointers. Also language implementation must support eliding of previous comma if __VA_ARGS__ is empty (both Visual Studio C++ and GCC do).
// pass number of arguments version
#define callVardicMethodSafely(...) {value_t *args[] = {NULL, __VA_ARGS__}; _actualFunction(args+1,sizeof(args) / sizeof(*args) - 1);}
// NULL terminated array version
#define callVardicMethodSafely(...) {value_t *args[] = {NULL, __VA_ARGS__, NULL}; _actualFunction(args+1);}
Short answer
/// logs all messages below this level, level 0 turns off LOG
#ifndef LOG_LEVEL
#define LOG_LEVEL 5 // 0:off, 1:error, 2:warning, 3: info, 4: debug, 5:verbose
#endif
#define _LOG_FORMAT_SHORT(letter, format) "[" #letter "]: " format "\n"
/// short log
#define log_s(level, format, ...) \
if (level <= LOG_LEVEL) \
printf(_LOG_FORMAT_SHORT(level, format), ##__VA_ARGS__)
usage
log_s(1, "fatal error occurred");
log_s(3, "x=%d and name=%s",2, "ali");
output
[1]: fatal error occurred
[3]: x=2 and name=ali
log with file and line number
const char* _getFileName(const char* path)
{
size_t i = 0;
size_t pos = 0;
char* p = (char*)path;
while (*p) {
i++;
if (*p == '/' || *p == '\\') {
pos = i;
}
p++;
}
return path + pos;
}
#define _LOG_FORMAT(letter, format) \
"[" #letter "][%s:%u] %s(): " format "\n", _getFileName(__FILE__), __LINE__, __FUNCTION__
#ifndef LOG_LEVEL
#define LOG_LEVEL 5 // 0:off, 1:error, 2:warning, 3: info, 4: debug, 5:verbose
#endif
/// long log
#define log_l(level, format, ...) \
if (level <= LOG_LEVEL) \
printf(_LOG_FORMAT(level, format), ##__VA_ARGS__)
usage
log_s(1, "fatal error occurred");
log_s(3, "x=%d and name=%s",2, "ali");
output
[1][test.cpp:97] main(): fatal error occurred
[3][test.cpp:98] main(): x=2 and name=ali
custom print function
you can write custom print function and pass ... args to it and it is also possible to combine this with methods above. source from here
int print_custom(const char* format, ...)
{
static char loc_buf[64];
char* temp = loc_buf;
int len;
va_list arg;
va_list copy;
va_start(arg, format);
va_copy(copy, arg);
len = vsnprintf(NULL, 0, format, arg);
va_end(copy);
if (len >= sizeof(loc_buf)) {
temp = (char*)malloc(len + 1);
if (temp == NULL) {
return 0;
}
}
vsnprintf(temp, len + 1, format, arg);
printf(temp); // replace with any print function you want
va_end(arg);
if (len >= sizeof(loc_buf)) {
free(temp);
}
return len;
}
Let's say you have a typical variadic function you've written. Because at least one argument is required before the variadic one ..., you have to always write an extra argument in usage.
Or do you?
If you wrap your variadic function in a macro, you need no preceding arg. Consider this example:
#define LOGI(...)
((void)__android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__))
This is obviously far more convenient, since you needn't specify the initial argument every time.
I'm unsure if this works for all compilers, but it has worked so far for me.
void inner_func(int &i)
{
va_list vars;
va_start(vars, i);
int j = va_arg(vars);
va_end(vars); // Generally useless, but should be included.
}
void func(int i, ...)
{
inner_func(i);
}
You can add the ... to inner_func() if you want, but you don't need it. It works because va_start uses the address of the given variable as the start point. In this case, we are giving it a reference to a variable in func(). So it uses that address and reads the variables after that on the stack. The inner_func() function is reading from the stack address of func(). So it only works if both functions use the same stack segment.
The va_start and va_arg macros will generally work if you give them any var as a starting point. So if you want you can pass pointers to other functions and use those too. You can make your own macros easily enough. All the macros do is typecast memory addresses. However making them work for all the compilers and calling conventions is annoying. So it's generally easier to use the ones that come with the compiler.