This question already has answers here:
What are the applications of the ## preprocessor operator and gotchas to consider?
(13 answers)
Closed 6 years ago.
I am having a code that says:
#include<stdio.h>
typedef struct string{
char *ch_ptr;
}str_t;
#define newdef(a,b) \
char a ## sumthing[b * sizeof(str_t)]; \
str_t *a = (str_t *)a ## sumthing
main(){
newdef(input,5);
/* some lines of code */
}
Optional changes to code:
#include<stdio.h>
typedef struct string{
char *ch_ptr;
}str_t;
#define newdef(a,b) \
char a ## sumthing[b * sizeof(str_t)]; \
str_t *var1 = (str_t *)a ## sumthing
main(){
newdef(input,5)="Hello";
printf("%s\n",input);
/* some lines of code */
}
Can anyone explain what this code segment means? Is input in this code a string (hope not) or a variable? If a variable then why doesn't the compiler throw an undeclared variable error?
Its a preprocessor concatenation operator, and can only be used when defining preprocessor macros.
Lets take a simple example
#define CONCAT(a, b) a ## b
int CONCAT(foo, bar);
In the above code, the invocation of CONCAT(foo, bar) will be replaced by
int foobar;
input is neither a string nor a variable, it's a preprocessing token.
## is the "token-pasting" operator.
The first macro expands newdef(input,5); into
char inputsumthing[5 * sizeof(str_t)]; str_t *input = (str_t *) intputsumthing;
That is, it expands into a declaration of a variable named like the first parameter.
Your "optional changes" would unconditionally name the declared pointer variable "var1" and make it impossible to use the macro more than once in the same scope.
Also, newdef(input,5)="Hello"; would expand to an error:
char inputsumthing[5 * sizeof(str_t)];
str_t *var1 = (str_t *)inputsumthing = "Hello";
As a side note, the original macro seems to mostly be an obfuscation of
str_t inputs[5];
str_t* input = inputs;
Related
I am trying to write a series of C macros to provide some generic data type capability for a struct, or grouping of structs that manages a dynamically allocated array. At this point I have written several structs for each data type and am just starting to write a Macro function that can initialize the struct variables and allocated a user defined amount of memory. The idea is to create one init function that can initialize any type of struct, so as long as it has the write form. For this implementation I first have to instantiate a copy of the struct, which is then passed to the macro. The macro ensures that the variables allocated_length and active_length is of type size_t. Then it determines the type of array by de-referencing it and using thetypeof operator. Finally it allocates memory to ptr and then checks to ensure the allocation was not returned as NULL. However, in this example, I do not pass anything back to the main program, and I am not passing variables as a pointer. Yet, somehow, the struct in the structs in the main program are able to see the modifications I made in the Macro. How is this?
#define init_vector(vec_struct, aloc_length) ({size_t _a = (aloc_length); \
size_t _b = 0; \
typeof(vec_struct.array) ptr = malloc(aloc_length * sizeof(&vec_struct.array)); \
if (ptr == NULL) { \
perror("WARNING: "); \
exit(0); \
} \
vec_struct.allocated_length = _a; \
vec_struct.active_length = _b; \
vec_struct.array = ptr; \
})
typedef struct
{
int *array;
size_t allocated_length;
size_t active_length;
} IntVector;
typedef struct
{
float *array;
size_t allocated_length;
size_t active_length;
} FltVector;
int main() {
IntVector int_vec;
init_vector(int_vec, 30);
printf("%ld\n", int_vec.allocated_length);
FltVector float_vec;
init_vector(float_vec, 20);
printf("%ld\n", float_vec.allocated_length);
return 0;
}
You need to understand that macros are not functions. They simply replace text (or more precise tokens) before the actual C compilation starts.
The compiler will compile this code:
int main() {
IntVector int_vec;
({size_t _a = (30); size_t _b = 0; typeof(int_vec.array) ptr = malloc(30 * sizeof(&int_vec.array)); if (ptr ==
# 32 "/app/example.c" 3 4
((void *)0)
# 32 "/app/example.c"
) { perror("WARNING: "); exit(0); } int_vec.allocated_length = _a; int_vec.active_length = _b; int_vec.array = ptr; });
printf("%ld\n", int_vec.allocated_length);
FltVector float_vec;
({size_t _a = (20); size_t _b = 0; typeof(float_vec.array) ptr = malloc(20 * sizeof(&float_vec.array)); if (ptr ==
# 36 "/app/example.c" 3 4
((void *)0)
# 36 "/app/example.c"
) { perror("WARNING: "); exit(0); } float_vec.allocated_length = _a; float_vec.active_length = _b; float_vec.array = ptr; });
printf("%ld\n", float_vec.allocated_length);
return 0;
}
https://godbolt.org/z/ezvKfdn33
Is it something you have expected?
Macros have to be used with great caution and as little as possible.
From https://en.cppreference.com/w/c/preprocessor/replace:
Function-like macros
#define identifier( parameters ) replacement-list
Function-like macros replace each occurrence of a defined identifier with replacement-list, additionally taking a number of arguments, which then replace corresponding occurrences of any of the parameters in the replacement-list.
The syntax of a function-like macro invocation is similar to the syntax of a function call: each instance of the macro name followed by a ( as the next preprocessing token introduces the sequence of tokens that is replaced by the replacement-list. The sequence is terminated by the matching ) token, skipping intervening matched pairs of left and right parentheses.
...
That means (based on your example) that every occurrence of the identifier init_vector is replaced with the code after the last parentheses of the parameter list.
And each occurrence of the parameters vec_struct, aloc_length will also replaced accordingly.
At the end, it is not about functions and function calls, but replacement.
"The preprocessor supports text macro replacement and function-like text macro replacement."
So, I am familiar with nested macros.
Now, I want to change a macro first changed by _Generic with some other macro like:
#include<stdio.h>
#define some_func(X) _Generic((X), \
char* : some_func_char, \
default : some_func_default)(X)
#define some_func_char(X) some_func_char(X, sizeof(X)/ sizeof(char))
void (some_func_char)(char *blah, size_t len_blah)
{
// do something
}
void some_func_default(double blah)
{
// code
}
int main()
{
some_func("hello");
return 0;
}
but it is raising a error as
main.c: In function ‘main’:
main.c:5:22: error: too few arguments to function ‘some_func_char’
5 | #define some_func(X) _Generic((X), \
| ^~~~~~~~
main.c:22:3: note: in expansion of macro ‘some_func’
22 | some_func("hello");
| ^~~~~~~~~
main.c:10:7: note: declared here
10 | void (some_func_char)(char *blah, size_t len_blah)
| ^~~~~~~~~~~~~~
some_func_char is calling the function not the macro in the _Generic call (even trying to stop the expansion with the parenthesis), on the other hand, you can not ommit the second parameter in some_func_default if some_func_char expects two parameters, switch to:
#include <stdio.h>
#define some_func(X) _Generic((X), \
char *: some_func_char, \
default: some_func_default)(X, sizeof X)
void some_func_char(char *blah, size_t len_blah)
{
// do something
}
void some_func_default(double blah, size_t dummy)
{
(void)dummy;
// code
}
int main()
{
some_func("hello");
return 0;
}
or better yet:
#include <stdio.h>
#include <string.h>
#define some_func(X) _Generic((X), \
char *: some_func_char, \
default: some_func_default)(X)
void some_func_char(char *blah)
{
size_t len = strlen(blah);
// do something
}
void some_func_default(double blah)
{
// code
}
int main()
{
some_func("hello");
return 0;
}
This second version allows you to pass and compute the correct length also for a pointer to char, don't worry for the performance, strlen is very fast.
Also, notice that sizeof(char) is always 1
It is important to understand that although generic selection is not really useful except in conjunction with macros, it is not itself interpreted by the preprocessor. Consider, then, this statement:
some_func("hello");
Where that appears in the example code, a definition of some_func as the identifier of a function-like macro is in scope, and the expansion of that macro produces this, prior to rescanning:
_Generic(("hello"), char *: some_func_char, default: some_func_default)("hello")
The preprocessor then scans that for further macro replacements, but again, _Generic is not a macro, and it has no other special significance to the preprocessor. There is an in-scope definition of some_func_char as the identifier of a function-like macro, but the appearance of that identifier in the above line does not match it because it is not followed by an open parenthesis. Nothing else in that line is significant (in context) to the preprocessor either, so that's in fact the final preprocessed form.
Now observe that the expression ("hello") matches the char * alternative of the generic selection expression, so the function identifier some_func_char is the selected result, but the parenthesized argument list ("hello") does not contain the correct number of arguments for that function. The overall expression is a more complicated variation on trying to call (some_func_char)("hello"). The some_func_char() macro never comes into play.
It should be clear, now, that you cannot use generic selection to select function identifiers of functions that take different numbers of arguments. But of course you can use it to select different function calls. For example,
#define some_func(X) _Generic( \
(X), \
char *: some_func_char((X), sizeof (X)), \
default: some_func_default(X) \
)
void some_func_char(char *s, size_t z) { }
void some_func_default(void *p) { }
int main(void) {
some_func("hello");
}
I have a flat C file including ctype.h where i cant figure out how a macro works. There is this macro
#define da_dim(name, type) type *name = NULL; \
int _qy_ ## name ## _p = 0; \
int _qy_ ## name ## _max = 0
I thought it should define the type of a given value. So for example i could write
int a;
da_dim(a,"char");
to convert it to a char but it doesnt do that. I can imagine what '## name ##' is/does (like a placeholder) but i dont understand what 'qy' is and where it came from. So what is this macro for, how tu use it and (maybe) how does it work?
A macro, in C is just a simple token replacement mechanism.
Your example:
int a;
da_dim(a,"char");
Will expand to:
int a;
"char" *a = NULL;
int _qy_a_p = 0;
int _qy_a_max = 0;
So, if will expand to errors because you will have two a identifiers and "char" is not expected where you are placing it.
If you are using gcc, you can "see" macro expansions by doing:
$ gcc -E your_program.c
This question already has answers here:
What does ## in a #define mean?
(6 answers)
Closed 6 years ago.
I am new to C programming and I have difficulty to understand what following code does? What does '##' in macro mean, also I don't quite understand how in macro definitions we have for example f1, but without arguments.
My question is different because I also have multiple defines
#include <stdio.h>
int a=1, b=2;
#define M(a,b) a ## b(a)
#define a t(f1,t(f1,f2(b)))
#define b(x) t(f2,t(f2,f1(a)))
#define ab(x) a+b(b)
typedef int (*i2i)(int);
int f1(int x) { return (++x); }
int f2(int x) { return (--x); }
int t(i2i f,int x) {return(f(f(x)));}
int main()
{
printf("%d\n%d", M(a,b), ab(5));
return (0);
}
The double-octothorpe "##" is known as the token-pasting operator. Whatever is on either side of it will be concatenated to form a single string. It lets you combine an argument with a fixed string, or combine two arguments. For example:
#define ADD_UNDERLINE(x) _ ## x ## _
ADD_UNDERLINE(foo) // generates: _foo_
In your case, you have:
#define M(a,b) a ## b(a)
M(foo,bar) // generates: foobar(foo)
Regarding your question about f1:
The macros use bare function names, but they're used as parameters to the function t. The first parameter of t is type i2i, which is defined (via typedef) as a pointer to a function. Using just the bare function name here is generally equivalent to a pointer to that function (note: this isn't standard and would be better written in the macro as "&f1").
Is there a way to use the _Generic keyword multiple times in the same expression to create a single string literal?
What I am looking for is a way to for example generate a single format string to pass to printf, with all the conversion specifiers adapted to the proper types.
When writing this answer I ended up with a rather ugly work-around:
#include <stdio.h>
typedef struct {
int a;
char b;
long c;
} ABC;
// printf conversion specifiers:
#define CS(x) \
_Generic((x), \
int: "%d", \
char: "%c", \
long: "%ld")
int main (void)
{
ABC abc = {1, 'a', 2};
printf(CS(abc.a), abc.a); printf(" ");
printf(CS(abc.b), abc.b); printf(" ");
printf(CS(abc.c), abc.c); printf(" ");
return 0;
}
6 printf calls instead of 1, hardly ideal.
The problem is that I can't find a way to combine _Generic and string literal concatenation by the pre-processor, like this:
printf(CS(abc.a) " ", abc.a); // doesnt work
printf(CS(abc.a) CS(abc.b), abc.a, abc.b); // doesnt work either
Because apparently generic macros don't count as string literals in the pre-processor, so string literal concatenation isn't possible. I toyed around with "stringification" macros but no luck there.
I'm going to say that the answer is NO.
First, the _Generic keyword is not (and cannot possibly be) a pre-processor directive. A generic-selection is a primary expression, as defined in section 6.5.1. Given the input
printf(CS(abc.a) "hello", abc.a);
the output from the preprocessor (generated by the -E compiler option) is:
printf(_Generic((abc.a), int: "%d", char: "%c", long: "%ld") "hello", abc.a);
Notice that string concatenation is not possible because the generic-selection has not been evaluated. Also note that it's impossible for the pre-processor to evaluate since it requires knowledge that abc is a structure of type ABC, that has member a. The pre-processor does simple text substitution, it has no knowledge of such things.
Second, the compiler phases defined in section 5.1.1.2 don't allow evaluation of _Generic keywords before string concatenation. The relevant phases, quoted from the spec, are
Adjacent string literal tokens are concatenated.
White-space characters separating tokens are no longer significant. Each preprocessing token is converted into a token. The resulting
tokens are syntactically and semantically analyzed and translated as a
translation unit.
The _Generic keyword must be evaluated in phase 7, since it requires knowledge that is only available after tokens have been syntactically and semantically analyzed, e.g. that abc is a structure with member a. Hence, multiple _Generic keywords cannot take advantage of string concatenation to produce a single string literal.
Nice question, you can paste a string passing another parameter:
#include <stdio.h>
typedef struct {
int a;
char b;
long c;
} ABC;
// printf conversion specifiers:
#define CS2(x, y) \
_Generic((x), \
int: "%d" y, \
char: "%c" y, \
long: "%ld" y)
int main (void)
{
ABC abc = {1, 'a', 2};
printf(CS2(abc.a, "Hello"), abc.a);
return 0;
}
Just for the record, it turns out it is possible to generate a string constant based on _Generic at compile-time, by using other dirty tricks than those available from the pre-processor.
The solution I came up with is so ugly that I barely dare to post it, but I'll do so just to prove it possible.
Don't write code like this!
#include <stdio.h>
typedef struct {
int a;
char b;
long c;
} ABC;
// printf conversion specifiers:
#define CS(x) \
_Generic((x), \
int: "%d", \
char: "%c", \
long: "%ld")
#pragma pack(push, 1)
#define print2(arg1,arg2) \
{ \
typedef struct \
{ \
char arr1 [sizeof(CS(arg1))-1]; \
char space; \
char arr2 [sizeof(CS(arg2))-1]; \
char nl_nul[2]; \
} struct_t; \
\
typedef union \
{ \
struct_t struc; \
char arr [sizeof(struct_t)]; \
} cs2_t; \
\
const cs2_t cs2 = \
{ \
.struc.arr1 = CS(arg1), \
.struc.space = ' ', \
.struc.arr2 = CS(arg2), \
.struc.nl_nul = "\n" \
}; \
\
printf(cs2.arr, arg1, arg2); \
}
#pragma pack(pop)
int main (void)
{
ABC abc = {1, 'a', 2};
print2(abc.a, abc.b);
print2(abc.a, abc.c);
print2(abc.b, abc.c);
return 0;
}
Output:
1 a
1 2
a 2
Explanation:
The macro print2 is a wrapper around printf and prints exactly 2 arguments, no matter type, with their correct conversion specifiers.
It builds up a string based on a struct, to which the conversion specifier string literals are passed. Each array place-holder for such a conversion specifier was purposely declared too small to fit the null termination.
Finally, this struct is dumped into a union which can interpret the whole struct as a single string. Of course this is quite questionable practice (even though it doesn't violate strict aliasing): if there is any padding then the program will fail.