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.
Related
What I currently have
#define _CMPLT8 _mm_cmplt_epi8 // int8_t
#define _CMPLT32 _mm_cmplt_epi32 // int32_t
What I want (something similar to the following code)
#define _CMPLT(T) ( \
if(sizeof(T)==1) return _mm_cmplt_epi8 \
else if(sizeof(T)==4) return _mm_cmplt_epi32 \
else #error \
)
How could I this code?
If you want to return a string based on a type I'd go for generics:
#define _CMPLT(T) \
_Generic( (T), \
char: "1", \
int: "4", \
default: "0")
int main(void) {
char a;
int b;
printf("%s%s\n", _CMPLT(a), _CMPLT(b));
}
But I feel you want to call functions depending on arg type, so in that case:
#define _CMPLT(X, Y) _Generic((X), \
int8_t: _mm_cmplt_epi8(X, Y), \
int32_t: _mm_cmplt_epi32(X, Y) \
)
int main(void) {
int8_t a = 0, b = 1;
int32_t c = 2, d = 3;
printf("%d%d\n", _CMPLT(a, b), _CMPLT(c, d));
}
If you really need to use strings and sizeof, and can use compound statements, would this work for you?
#include <stdio.h>
#define _CMPLT(T) ({ \
switch(sizeof(T)) { \
case 1: "1"; \
case 4: "4"; \
}; \
"0"; \
})
int main(void) {
printf("%s%s\n",
_CMPLT(char), _CMPLT(int));
}
I'd like to repeat a block of C code N times, every time just changing just a few words (like variable names, variable types...).
I'd like to obtain a final effect similar to NumPy's preprocessor for .src files. For example in their code you can see that this:
/**begin repeat
* #name = number, integer, signedinteger, unsignedinteger, inexact,
* floating, complexfloating, flexible, character#
* #NAME = Number, Integer, SignedInteger, UnsignedInteger, Inexact,
* Floating, ComplexFloating, Flexible, Character#
*/
NPY_NO_EXPORT PyTypeObject Py#NAME#ArrType_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "numpy.#name#",
.tp_basicsize = sizeof(PyObject)
};
/**end repeat**/
would be turned into something like this:
NPY_NO_EXPORT PyTypeObject PyNumberArrType_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "numpy.number",
.tp_basicsize = sizeof(PyObject)
};
NPY_NO_EXPORT PyTypeObject PyIntegerArrType_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "numpy.integer",
.tp_basicsize = sizeof(PyObject)
};
NPY_NO_EXPORT PyTypeObject PySignedIntegerArrType_Type = {
PyVarObject_HEAD_INIT(NULL, 0)
.tp_name = "numpy.signedinteger",
.tp_basicsize = sizeof(PyObject)
};
// And so on for all the couples of `name` and `NAME`.
Is there a way to do the same thing, but without having to use an external preprocessor? I thought about using macros but I don't think that's the case.
Does an X-macro helps?
#define LIST_OF_TYPES \
X(Number, number) \
X(Integer, integer) \
X(SignedInteger, signedinteger)
#define X(type, name) \
NPY_NO_EXPORT PyTypeObject Py##type##ArrType_Type = { \
PyVarObject_HEAD_INIT(NULL, 0) \
.tp_name = "numpy."#name, \
.tp_basicsize = sizeof(PyObject) \
};
LIST_OF_TYPES
#undef X
or pass the second argument between quotes:
#define LIST_OF_TYPES \
X(Number, "number") \
X(Integer, "integer") \
X(SignedInteger, "signedinteger")
#define X(type, name) \
NPY_NO_EXPORT PyTypeObject Py##type##ArrType_Type = { \
PyVarObject_HEAD_INIT(NULL, 0) \
.tp_name = "numpy."name, \
.tp_basicsize = sizeof(PyObject) \
};
LIST_OF_TYPES
#undef X
or if you prefer, a function-like macro, in this case I will move the semicolon to the macro call:
#define PY_TYPE_OBJECT(type, name) \
NPY_NO_EXPORT PyTypeObject Py##type##ArrType_Type = { \
PyVarObject_HEAD_INIT(NULL, 0) \
.tp_name = "numpy."name, \
.tp_basicsize = sizeof(PyObject) \
}
PY_TYPE_OBJECT(Number, "number");
PY_TYPE_OBJECT(Integer, "integer");
PY_TYPE_OBJECT(SignedInteger, "signedinteger");
You can write a preprocessor macro:
#define PYOBJ(t, n) \
NPY_NO_EXPORT PyTypeObject Py ## t ## ArrType_Type = { /*
*/ PyVarObject_HEAD_INIT(NULL, 0) /*
*/ .tp_name = "numpy." # n, /*
*/ .tp_basicsize = sizeof(PyObject), /*
*/}
PYOBJ(Number, number);
PYOBJ(Integer, integer);
PYOBJ(SignedInteger, signedinteger);
Running gcc -E -CC test.c produces this:
NPY_NO_EXPORT PyTypeObject PyNumberArrType_Type = { /*
*/ PyVarObject_HEAD_INIT(NULL, 0) /*
*/ .tp_name = "numpy." "number", /*
*/ .tp_basicsize = sizeof(PyObject), /*
*/};
# 9 "test.c"
NPY_NO_EXPORT PyTypeObject PyIntegerArrType_Type = { /*
*/ PyVarObject_HEAD_INIT(NULL, 0) /*
*/ .tp_name = "numpy." "integer", /*
*/ .tp_basicsize = sizeof(PyObject), /*
*/};
# 10 "test.c"
NPY_NO_EXPORT PyTypeObject PySignedIntegerArrType_Type = { /*
*/ PyVarObject_HEAD_INIT(NULL, 0) /*
*/ .tp_name = "numpy." "signedinteger", /*
*/ .tp_basicsize = sizeof(PyObject), /*
*/};
See the preprocessor macro documentation on stringizing:
When a macro parameter is used with a leading ‘#’, the preprocessor replaces it with the literal text of the actual argument, converted to a string constant.
and on concatenation:
The ‘##’ preprocessing operator performs token pasting. When a macro is expanded, the two tokens on either side of each ‘##’ operator are combined into a single token, which then replaces the ‘##’ and the two original tokens in the macro expansion.
You need to have two parameters to the macro, even though the second one is the lowercase version of the first one (Integer vs. integer). Because the preprocessor does not allow token transformations. See the question Can you capitalize a pasted token in a macro?.
As to the /* */ comments and the -CC compiler flag I used, it puts newlines in the preprocessor output to make it readable. The idea comes from this question: How to make G++ preprocessor output a newline in a macro?. You could instead write:
#define PYOBJ(t, n) \
NPY_NO_EXPORT PyTypeObject Py ## t ## ArrType_Type = { \
PyVarObject_HEAD_INIT(NULL, 0) \
.tp_name = "numpy." # n, \
.tp_basicsize = sizeof(PyObject), \
}
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.
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.)
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.