I am trying to define a debug_log function in a preprocessor #define in order for this function to exist only in Debug mode.
The thing is that I wish to use a variable_argument function :
#ifdef DEBUG
#define DEBUG_ENABLED 1
#else
#define DEBUG_ENABLED 0
#endif
#define debug_log(msg, ...) \
do { \
if (DEBUG_ENABLED) { \
char str[300]; \
int length = -1; \
va_list argList; \
va_start( argList, msg ); \
length = vsnprintf(str, sizeof(str), msg, argList); \
va_end( argList ); \
if (length > 0) \
{ \
fprintf(stderr, "%s, %d ",__func__, __LINE__); \
fprintf(stderr, "%s", str); \
fprintf(stderr,"\n"); \
} \
} \
} while (0) \
The compiler is returning :
error: ‘va_start’ used in function with fixed args
[build] 20 | va_start( argList, msg ); \
Thank you for your help ;)
You're not actually defining a function here. You're defining a block of code that expects to be part of a variadic function. You're most likely not in a variadic function when you call this macro, hence the error.
Instead, define an actual function inside of an #if block, along with a dummy function-like macro in the #else block that does nothing.
#ifdef DEBUG
#define debug_log(...) debug_log_impl(__func__, __LINE__, __VA_ARGS__)
void debug_log_impl(const char *func, int line, const char *msg, ...)
{
char str[300];
int length = -1;
va_list argList;
va_start( argList, msg );
length = vsnprintf(str, sizeof(str), msg, argList);
va_end( argList );
if (length > 0)
{
fprintf(stderr, "%s, %d ", func, line);
fprintf(stderr, "%s", str);
fprintf(stderr,"\n");
}
}
#else
#define debug_log(...) (void)0
#fi
There was a question in the comments regarding why (void)0 should be used in the #else case instead of an empty expression. Suppose you were to use debug_log as the left operand of the comma operator:
while (debug_log("iterating, x=%d",x), x>0)
With the definition above, if DEBUG is not defined this line expands to:
while ((void)0, x>0)
If it were an empty expression, it would expand to:
while (, x>0)
Which is a syntax error.
You are mixing up variable arguments for macros with variable argument access for functions.
The ... in the macro parameter list represents the variable arguments of the macro. The identifier __VA_ARGS__ in the macro replacement text expands to the variable arguments.
The ... in a function parameter list represents the variable arguments of the function. On object of type va_list can be used to access these variable arguments using the va_start, va_arg and va_end macros (and perhaps the va_copy macro if needed). Those are defined by #include <stdarg.h>.
Your debug_log macro is not a function, so it is not a function with variable arguments, so it cannot use va_start etc. to access those arguments. What it can do is pass the macros variable arguments as a whole to something else. For your example, snprintf would be a good choice as a replacement for the vsnprintf you orignally used:
#define debug_log(msg, ...) \
do { \
if (DEBUG_ENABLED) { \
char str[300]; \
int length = -1; \
length = snprintf(str, sizeof(str), msg, __VA_ARGS__); \
if (length > 0) \
{ \
fprintf(stderr, "%s, %d ",__func__, __LINE__); \
fprintf(stderr, "%s", str); \
fprintf(stderr,"\n"); \
} \
} \
} while (0)
The above form requires at least two arguments, so you couldn't use it to print a simple debug message such as "got here". As a workaround, the macro can be defined with no fixed parameters, omitting the msg parameter:
#define debug_log(...) \
do { \
if (DEBUG_ENABLED) { \
char str[300]; \
int length = -1; \
length = snprintf(str, sizeof(str), __VA_ARGS__); \
if (length > 0) \
{ \
fprintf(stderr, "%s, %d ",__func__, __LINE__); \
fprintf(stderr, "%s", str); \
fprintf(stderr,"\n"); \
} \
} \
} while (0)
That would allow you to attempt to call the macro with no parameters at all, such as debug_log();, but that will result in a compiler error at the call to snprintf.
Related
The following code is from better string library header.
While looking at the define macros, I notice that va_list, va_start, and va_end are all used inside the for loop.
Wonder why this is necessary?
By the way, I have looked into the function bvcformata which doesn't modify bstrmp_arglist.
#define bvformata(ret, b, fmt, lastarg) { \
bstring bstrtmp_b = (b); \
const char * bstrtmp_fmt = (fmt); \
int bstrtmp_r = BSTR_ERR, bstrtmp_sz = 16; \
for (;;) { \
va_list bstrtmp_arglist; \
va_start (bstrtmp_arglist, lastarg); \
bstrtmp_r = bvcformata (bstrtmp_b, bstrtmp_sz, bstrtmp_fmt, bstrtmp_arglist); \
va_end (bstrtmp_arglist); \
if (bstrtmp_r >= 0) { /* Everything went ok */ \
bstrtmp_r = BSTR_OK; \
break; \
} else if (-bstrtmp_r <= bstrtmp_sz) { /* A real error? */ \
bstrtmp_r = BSTR_ERR; \
break; \
} \
bstrtmp_sz = -bstrtmp_r; /* Doubled or target size */ \
} \
ret = bstrtmp_r; \
}
#endif
Could the va_list, va_start, and va_end be "moved" out of the for loop? I am still a beginner in C. Just wondering why they have to be used inside the for loop?
#define bvformata(ret, b, fmt, lastarg) { \
bstring bstrtmp_b = (b); \
const char * bstrtmp_fmt = (fmt); \
int bstrtmp_r = BSTR_ERR, bstrtmp_sz = 16; \
va_list bstrtmp_arglist; \ <--------*****
va_start (bstrtmp_arglist, lastarg); \ <--------*****
for (;;) { \
bstrtmp_r = bvcformata (bstrtmp_b, bstrtmp_sz, bstrtmp_fmt, bstrtmp_arglist); \
if (bstrtmp_r >= 0) { /* Everything went ok */ \
bstrtmp_r = BSTR_OK; \
break; \
} else if (-bstrtmp_r <= bstrtmp_sz) { /* A real error? */ \
bstrtmp_r = BSTR_ERR; \
break; \
} \
bstrtmp_sz = -bstrtmp_r; /* Doubled or target size */ \
} \
va_end (bstrtmp_arglist); \ <--------*****
ret = bstrtmp_r; \
}
#endif
You can move the va_list declaration outside the loop. But va_start and va_end have to stay in the loop, so that each call to bvcformata() will process the variable arguments from the beginning again.
#define bvformata(ret, b, fmt, lastarg) { \
bstring bstrtmp_b = (b); \
const char * bstrtmp_fmt = (fmt); \
int bstrtmp_r = BSTR_ERR, bstrtmp_sz = 16; \
va_list bstrtmp_arglist; \
for (;;) { \
va_start (bstrtmp_arglist, lastarg); \
bstrtmp_r = bvcformata (bstrtmp_b, bstrtmp_sz, bstrtmp_fmt, bstrtmp_arglist); \
va_end (bstrtmp_arglist); \
if (bstrtmp_r >= 0) { /* Everything went ok */ \
bstrtmp_r = BSTR_OK; \
break; \
} else if (-bstrtmp_r <= bstrtmp_sz) { /* A real error? */ \
bstrtmp_r = BSTR_ERR; \
break; \
} \
bstrtmp_sz = -bstrtmp_r; /* Doubled or target size */ \
} \
ret = bstrtmp_r; \
}
#endif
Never mind silly me. I just notice that there are "break"s within the for loop. By "moving" the va_list and va_end out will create a memory leak.
I have two programs that use the same tricks and features, and only one of them compiles.
A) This one compiles, and also works as expected:
#include <errno.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
/*
* int mallocs(T **restrict p, ptrdiff_t nmemb);
*/
#define mallocs(ptr, nmemb) ( \
{ \
ptrdiff_t nmemb_ = (nmemb); \
__auto_type ptr_ = (ptr); \
int err_; \
\
err_ = 0; \
if (ptr_ == NULL) { \
errno = EINVAL; \
err_ = EINVAL; \
goto ret_; \
} \
if (nmemb_ < 0) { \
*ptr_ = NULL; \
errno = EOVERFLOW; \
err_ = -EOVERFLOW; \
goto ret_; \
} \
if (nmemb_ > (PTRDIFF_MAX / (ptrdiff_t)sizeof(**ptr_))) { \
*ptr_ = NULL; \
errno = EOVERFLOW; \
err_ = EOVERFLOW; \
goto ret_; \
} \
\
*ptr_ = malloc(sizeof(**ptr_) * nmemb_); \
if (!(*ptr_)) \
err_ = ENOMEM; \
ret_: \
err_; \
} \
)
int main(void)
{
int *b1;
int **p;
int c = getchar();
p = &b1;
if (c == 'a')
p = 0;
printf("%c\n", c);
if (mallocs(p, 47))
goto err;
b1[4] = 52;
printf("Hi: %i\n", b1[4]);
free(b1);
return 0;
err:
perror(NULL);
exit(EXIT_FAILURE);
}
B) This one doesn't even compile (error is shown below):
#include <assert.h>
#include <errno.h>
#include <stdio.h>
#define alx_same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))
#define alx_static_assert_array(a) do \
{ \
static_assert(!alx_same_type((a), &(a)[0]), "Not an array!"); \
} while (0)
/*
* int alx_sbprintf(char buff[restrict], int *restrict written,
* const char *restrict format, ...);
*/
#define alx_sbprintf(buff, written, format, ...) ( \
{ \
__auto_type w_ = (written); \
int len_; \
int err_; \
\
alx_static_assert_array(buff); \
err_ = 0; \
\
len_ = snprintf(buff, sizeof(buff), format, ##__VA_ARGS__); \
if (w_ != NULL) \
*w_ = len_; \
\
if (len_ < 0) { \
err_ = -errno; \
goto ret_; \
} \
if ((unsigned)len_ >= sizeof(buff)) { \
if (w_ != NULL) \
*w_ = sizeof(buff) - 1; \
errno = ENOMEM; \
err_ = ENOMEM; \
goto ret_; \
} \
ret_: \
err_; \
} \
)
int main(void)
{
char b1[10];
char b2[BUFSIZ];
int w1;
int *w2 = NULL;
if (alx_sbprintf(b1, &w1, "testttt%i", 12))
printf("Error 1.1\n");
printf("b1: %s; w1 = %i\n", b1, w1);
if (alx_sbprintf(b2, w2, "test%s", "testtt"))
printf("Error 2.1\n");
printf("b2: %s; w2 = %p\n", b2, w2);
return 0;
}
Error:
$ gcc -std=gnu17 -Wall -Wextra -Werror main.c
main.c: In function ‘main’:
main.c:39:3: error: jump into statement expression
goto ret_; \
^~~~
main.c:70:6: note: in expansion of macro ‘alx_sbprintf’
if (alx_sbprintf(b2, w2, "test%s", "testtt"))
^~~~~~~~~~~~
main.c:48:1: note: label ‘ret_’ defined here
ret_: \
^~~~
main.c:66:6: note: in expansion of macro ‘alx_sbprintf’
if (alx_sbprintf(b1, &w1, "testttt%i", 12))
^~~~~~~~~~~~
main.c:46:3: error: jump into statement expression
goto ret_; \
^~~~
main.c:70:6: note: in expansion of macro ‘alx_sbprintf’
if (alx_sbprintf(b2, w2, "test%s", "testtt"))
^~~~~~~~~~~~
main.c:48:1: note: label ‘ret_’ defined here
ret_: \
^~~~
main.c:66:6: note: in expansion of macro ‘alx_sbprintf’
if (alx_sbprintf(b1, &w1, "testttt%i", 12))
^~~~~~~~~~~~
main.c:48:1: error: duplicate label ‘ret_’
ret_: \
^~~~
main.c:70:6: note: in expansion of macro ‘alx_sbprintf’
if (alx_sbprintf(b2, w2, "test%s", "testtt"))
^~~~~~~~~~~~
main.c:48:1: note: previous definition of ‘ret_’ was here
ret_: \
^~~~
main.c:66:6: note: in expansion of macro ‘alx_sbprintf’
if (alx_sbprintf(b1, &w1, "testttt%i", 12))
^~~~~~~~~~~~
Why does only one of them throw that error?
GNU C does forbid jumping into a statement expression, but your main problem is that expanding the macro results in the ret_ label being duplicated.
You'll want to combine this statement expression with the __label__ extension for declaring scope-local labels:
#include <assert.h>
#include <errno.h>
#include <stdio.h>
#define alx_same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))
#define alx_static_assert_array(a) do \
{ \
static_assert(!alx_same_type((a), &(a)[0]), "Not an array!"); \
} while (0)
/*
* int alx_sbprintf(char buff[restrict], int *restrict written,
* const char *restrict format, ...);
*/
#define alx_sbprintf(buff, written, format, ...) ( \
{ \
__label__ ret_; \
__auto_type w_ = (written); \
int len_; \
int err_; \
\
alx_static_assert_array(buff); \
err_ = 0; \
\
len_ = snprintf(buff, sizeof(buff), format, ##__VA_ARGS__); \
if (w_ != NULL) \
*w_ = len_; \
\
if (len_ < 0) { \
err_ = -errno; \
goto ret_; \
} \
if ((unsigned)len_ >= sizeof(buff)) { \
if (w_ != NULL) \
*w_ = sizeof(buff) - 1; \
errno = ENOMEM; \
err_ = ENOMEM; \
goto ret_; \
} \
ret_: \
err_; \
} \
)
int main(void)
{
char b1[10];
char b2[BUFSIZ];
int w1;
int *w2 = NULL;
if (alx_sbprintf(b1, &w1, "testttt%i", 12))
printf("Error 1.1\n");
printf("b1: %s; w1 = %i\n", b1, w1);
if (alx_sbprintf(b2, w2, "test%s", "testtt"))
printf("Error 2.1\n");
printf("b2: %s; w2 = %p\n", b2, w2);
return 0;
}
(I've only copied the rest of the code but the addition of __label__ ret_; makes the code compile.)
In example B, you invoke the alx_sbprintf macro twice. This causes the ret_ label to be defined twice, leading to the "duplicate label" error.
The labels aren't scoped to the statement expression, they're scoped to the function.
I'm not sure why the same "jump into statement expression" error isn't raised in your first example though.
Theres no reason to use GCC expression statements like this, when a function will work just fine. (Possibly static inline if you want to put it in a header file.)
I am trying to define a macro -
#define macro1(arg1) \
do{ \
int _state = 0; \
if (arg1 && arg1->member_) \
_state = arg1->member_->state_; \
printf("%d", _state); \
} while(0)
A *a = new A():
macro1(a); // Works
macro1(NULL); // Error
The specific error I see is -
"error: base operand of ‘->’ is not a pointer"
Aren't we allowed to pass NULL as an argument to macros?
Macro expansion is just text replacement, so when you passed NULL, it will expand to NULL->member, clearly it is an error. One way is to use a temporary variable for that:
#define macro1(arg1) \
do{ \
A* p = (arg1);
int _state = 0; \
if (p && p->member_) \
_state = p->member_->state_; \
printf("%d", _state); \
} while(0)
A *a = new A():
macro1(a);
macro1(NULL);
This way both cases will work.
You have to understand what's a macro in order to understand your mistake. Except for the compiler, there's an animal called pre-compiler. It replaces all the macros' references by the actual code defined for this macro. So this code:
#define macro1(arg1) \
do{ \
int _state = 0; \
if (arg1 && arg1->member_) \
_state = arg1->member_->state_; \
printf("%d", _state); \
} while(0)
A *a = new A():
macro1(a); // Works
macro1(NULL); // Error
will be replaced with:
A *a = new A():
do{
int _state = 0;
if (a && a->member_)
_state = a->member_->state_;
printf("%d", _state);
} while(0)
do{
int _state = 0;
if (NULL && NULL->member_)
_state = NULL->member_->state_;
printf("%d", _state);
} while(0)
THIS code will be compiled. And now you can see for yourself what's the root cause of the compilation error.
Macros are just a text replacement.
for example, if you have
#define mac(x) x/x
that would work for must numbers but not for 0, because it will be replaced with 0/0 which is not defined.
in your case if you pass NULL it will be replaced with:
do{ \
int _state = 0; \
if (NULL && NULL->member_) \
_state = NULL ->member_->state_; \
printf("%d", _state); \
} while(0)
so what is the meaning of NULL->member_ in this case. No sense, hence it fails.
consider using a regular function, or two macros one for regular pointers and one for NULL pointers, and make your code as this:
if (ptr)
macro1(ptr);
else
macro2;
Small adjust let the pre-compiler know the type size + a forward declaration and it'll work:
#define macro1(arg1) \
do{ \
int _state = 0; \
if ((arg1) && ((A*)arg1)->member_) \
_state = ((A*)arg1)->member_->state_; \
printf("%d", _state); \
} while(0)
Complete code:
#include <stdio.h>
class A;
#define macro1(arg1) \
do{ \
int _state = 0; \
if ((arg1) && ((A*)arg1)->member_) \
_state = ((A*)arg1)->member_->state_; \
printf("%d", _state); \
} while(0)
struct member{
int state_;
};
class A {
public:
member* member_;
};
int main(int n, char** arg) {
A* a = new A();
a->member_ = new member();
a->member_->state_ = 1;
macro1(a);
macro1(NULL);
return 0;
}
The more fundamental problem is that NULL is not a pointer, it is a macro for 0.
Hence when you pass in NULL, it is equivalent to passing in 0, which of course is an error.
As other answers have mentioned, giving the argument an explicit cast will fix it
(A*)arg1
As a side note, you should be using nullptr in C++ for a null pointer.
EDIT: As #AjayBrahmakshatriya pointed out, NULL can be defined as (void*)0 (in C only) which will be a pointer, but the argument still holds true, NULL is not a pointer of your type.
EDIT2: Apparently in C++11 and later, NULL can be defined as nullptr
Im trying to create a preprocessor macro to allocate and free memory for matrix/vector of any data type. Here is what I have so far:
#ifndef H_ARRAY_H
#define H_ARRAY_H
#include "xmalloc.h"
#define make_vector(v,n) (v = xmalloc( (n) * sizeof *(v))
#define free_vector(v) do { free(v) ; v = NULL; } while(0)
#define make_matrx(a , m , n) do { \
size_t make_matrix_loop_counter; \
make_vector(a, (m) + 1); \
for ( make_matrix_loop_counter = 0; make_matrix_loop_counter < (m) ; make_matrix_loop_counter++) \
make_vector((a)[make_matrix_loop_counter], (n)); \
(a)[m] = NULL; \
} while (0)
#define free_matrix(a) do { \
if (a != NULL){ \
size_t make_matrix_loop_counter; \
for (make_matrix_loop_counter = 0 ; (a) [make_matrix_loop_counter] != NULL; make_matrix_loop_counter++) \
free_vector((a)[make_matrix_loop_counter]); \
free_vector(a); \
a != NULL; \
} \
} while (0)
But when I try to construct a matrix it spits out an error "implicit declaration of function ‘make_matrix’".
Any suggestions.
PS: xmalloc.h allocate space
Are you sure your MACRO name should read make_matrx instead of make_matrix?
You need to correct the macro name spelling. Otherwise, when you use make_matrix() in your code, it does not find a corresponding function.
I want to define a macro accepting either 1 or 2 parameters. Both the parameters should be different type. How to use ellipsis and read the arguments passed?
Below is the sample:
void test(char *var2)
{
printf("%s\n",var2);
}
#define PRINT_STRING(...) ( if (!var1) test(var2) )
int main(int argc, _TCHAR argv[]) {
PRINT_STRING(TRUE);
PRINT_STRING(FALSE,"Hello, World!");
return 0;
}
This is known as a Variadic macro.
If your compiler supports __VA_ARGS__, you can do it like this:
#include <stdio.h>
#define NUM_ARGS__(X, \
N64,N63,N62,N61,N60, \
N59,N58,N57,N56,N55,N54,N53,N52,N51,N50, \
N49,N48,N47,N46,N45,N44,N43,N42,N41,N40, \
N39,N38,N37,N36,N35,N34,N33,N32,N31,N30, \
N29,N28,N27,N26,N25,N24,N23,N22,N21,N20, \
N19,N18,N17,N16,N15,N14,N13,N12,N11,N10, \
N09,N08,N07,N06,N05,N04,N03,N02,N01, N, ...) N
#define NUM_ARGS(...) \
NUM_ARGS__(0, __VA_ARGS__, \
64,63,62,61,60, \
59,58,57,56,55,54,53,52,51,50, \
49,48,47,46,45,44,43,42,41,40, \
39,38,37,36,35,34,33,32,31,30, \
29,28,27,26,25,24,23,22,21,20, \
19,18,17,16,15,14,13,12,11,10, \
9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
#define PRINT_STRING_1(var) \
{ if (!(var)) {} }
#define PRINT_STRING_2(var, ...) \
{ if (!(var)) test(__VA_ARGS__); }
#define PRINT_STRINGN__(N, ...) \
PRINT_STRING_##N(__VA_ARGS__)
#define PRINT_STRINGN(N, ...) \
PRINT_STRINGN__(N, __VA_ARGS__)
#define PRINT_STRING(...) \
PRINT_STRINGN(NUM_ARGS(__VA_ARGS__), __VA_ARGS__)
void test(char* var2)
{
printf("%s\n", var2);
}
int main(void)
{
PRINT_STRING(1);
PRINT_STRING(0, "Hello, World!");
PRINT_STRING(1, "You can't see me!");
return 0;
}
Output:
Hello, World!
To do something like that you'd have to implement a series of macros, something like this
#include <stdbool.h>
#define PRINT_STRING0(X, Y) do { if (X && Y) test(Y); } while(false)
#define PRINT_STRING1(X, Y, ...) PRINT_STRING0(X, Y)
#define PRINT_STRING(...) PRINT_STRING1(__VA_ARGS__, 0, 0)
The last of them (user interface) adds a second or third argument of 0. PRINT_STRING1 then ignores all arguments that are more than 2. And PRINT_STRING0 then does the work.
Some more remarks:
be careful that a macro like you want to program it here can be place syntactically just as any other statement. In the example here the do { } while(false) does that trick
since C99 has Boolean type and constants, these are _Bool or bool and false and true
Don't use a macro to do that, use a variadic function!
void print_string( bool should_print, ... )
{
if( should_print )
{
va_list argp;
va_start( argp, should_print);
char *string = va_arg(argp, char *);
if( string) printf("%s", string );
va_end( argp );
}
}
But be very careful when using these sorts of things, because va_arg doesn't check if you really have a 2nd argument.
Also, to use a macro that accepts one or 2 arguments, use the GCC trick (at the very bottom of this page):
#define PRINT_STRING( should , args... ) print_string( should , ##args )
(note the spaces between the commas)