Why is this generic expression bugging out? [duplicate] - c

This question already has answers here:
Incompatible pointer types passing in _Generic macro
(2 answers)
Closed 6 years ago.
Recently I answered some question asking how to implement complex const correctness of a structure member. Doing this I used Generic expressions and encountered strange behavior. Here is the sample code:
struct A
{
union {
char *m_ptrChar;
const char *m_cptrChar;
} ;
};
#define ptrChar_m(a) _Generic(a, struct A *: a->m_ptrChar, \
const struct A *: a->m_cptrChar, \
struct A: a.m_ptrChar, \
const struct A: a.m_cptrChar)
void f(const struct A *ptrA)
{
ptrChar_m(ptrA) = 'A'; // NOT DESIRED!!
}
On GCC and Clang the generic expression select the case when the type of a must be struct A which doesn't make any sense. When I comment the last two cases though it's working fine. Why is this - is it some bug?
The exact error messages on clang are:
test.c:17:5: error: member reference type 'const struct A *' is a pointer; did you mean to use '->'?
ptrChar_m(ptrA) = 'A'; // NOT DESIRED!!
^
test.c:12:45: note: expanded from macro 'ptrChar_m'
struct A: a.m_ptrCHar, \
^
test.c:17:5: error: member reference type 'const struct A *' is a pointer; did you mean to use '->'?
ptrChar_m(ptrA) = 'A'; // NOT DESIRED!!
^
test.c:13:51: note: expanded from macro 'ptrChar_m'
const struct A: a.m_cptrChar)
^
test.c:17:21: error: cannot assign to variable 'ptrA' with const-qualified type 'const struct A *'
ptrChar_m(ptrA) = 'A'; // NOT DESIRED!!
^
test.c:15:24: note: variable 'ptrA' declared const here
void f(const struct A *ptrA)
^
test.c:23:18: error: invalid application of 'sizeof' to a function type [-Werror,-Wpointer-arith]
return sizeof(main);
4 errors generated.

Although the non-matching selections are not evaluated, the compiler is still compiling the code. So using the . operator on a pointer to a structure is not allowed.
If your input expression is always a variable, you could take the address of the variable representing the structure instance.
#define AptrChar_m(a) _Generic(a, struct A *: a->m_ptrChar, \
const struct A *: a->m_cptrChar)
#define ptrChar_m(a) AptrChar_m(_Generic(a, struct A *: a, \
const struct A *: a, \
struct A: &a, \
const struct A: &a))

Related

Problem using const together with typedef and function pointer

Here is a MRE
#include <string.h>
typedef char* mytype;
typedef int(*cmp)(const mytype, const mytype);
void foo(cmp f) {}
int main(void) {
foo(strcmp);
}
When I compile I get:
$ gcc mre.c -Wall -Wextra
mre.c: In function ‘foo’:
mre.c:7:14: warning: unused parameter ‘f’ [-Wunused-parameter]
7 | void foo(cmp f) {}
| ~~~~^
mre.c: In function ‘main’:
mre.c:10:9: warning: passing argument 1 of ‘foo’ from incompatible pointer type [-Wincompatible-pointer-types]
10 | foo(strcmp);
| ^~~~~~
| |
| int (*)(const char *, const char *)
mre.c:7:14: note: expected ‘cmp’ {aka ‘int (*)(char * const, char * const)’} but argument is of type ‘int (*)(const char *, const char *)’
7 | void foo(cmp f) {}
| ~~~~^
The first warning is irrelevant. But what do I do about the second? I tried changing the typedef to:
typedef int(*cmp)(mytype const,mytype const);
But that gave the exact same result. When I changed to:
typedef int(*cmp)(const char*, const char*);
it worked, but that's not preferable for obvious reasons. Another thing that worked but also is not preferable is this:
typedef const char* mytype;
typedef int(*cmp)(mytype, mytype);
So what have I missed here?
The problem I'm trying to solve is that I want to create a generic structure where the data is of type mytype. The user of the structure is expected to implement a compare function, but I want it to work with strcmp in case mytype is of type char*.
Note that mytype not necessarily needs to be a pointer. It depends on what data type the user specifies. Also, I know that typedefing pointers is bad practice in general, but I consider this a special case because I want to typedef whatever the type is, and it may be a pointer.
The structure looks like this:
struct node {
struct node *next;
mytype data;
};
I managed to solve it with #define mytype char* but that feels very ugly, and I would prefer if there was another way. I want it to be portable, so I don't want to use gcc extensions.
typedef char* mytype;
mytype is a pointer to char. const mytype is a const pointer to char not pointer to const char.
Unfortunately in C you cant use typedef pointers and the use this type to declare pointer to const object.
you need to
typedef int(*cmp)(const char *, const char *);
void foo(cmp f) {}
int main(void) {
foo(strcmp);
}
or
foo((cmp)strcmp);
BTW it a very bad practice to hide pointers behind the typedefs.

discarding qualifier when passing a struct

I am having one of those "discards qualifiers" cases that I cannot figure out. I have code that empirically functions until I try to get rid of the warning. Here's the error:
filter.c:28:21: warning: passing 'const filterentry [1]' to parameter of type
'struct filter_matchbuffer *' discards qualifiers
[-Wincompatible-pointer-types-discards-qualifiers]
filterServiceBuffer(filterinparsermap);
^~~~~~~~~~~~~~~~~
In the code below, I have a series of text string filters that are text that is paired with a function. For instance, when I see the "$GPRIP" string, it copies the message. Anyway, there's whole series of these pairs in filterinparsermap[].
#define FILTER_COPY 3
enum
{
FILTERFUNCTION_COPY = FILTER_COPY,
#define FILTERFUNCTION_COPY FILTERFUNCTION_COPY
};
struct filter_matchbuffer
{
int indexofstring;
int filterbehavior;
const char *const strforcomparison;
};
typedef struct filter_matchbuffer filterentry;
#define FILTERTEXTENTRY(num, behavior, x) \
{ \
num, behavior, #x \
}
const filterentry filterinparsermap[] = {
FILTERTEXTENTRY(1, FILTER_COPY, "$GPRIP")};
void filterServiceBuffer(struct filter_matchbuffer p_filtermap[])
{
}
int main(){
filterServiceBuffer(filterinparsermap);
return(0);
}
I would really like to get rid of the error, but I just cannot seem to get my struct and pointer types to agree and still have the code compile. Any suggestions would be greatly appreciated.
to get rid of the error ("discards qualifiers" )
Edit function signature to accept a pointer to const data.
// void filterServiceBuffer(struct filter_matchbuffer p_filtermap[])
void filterServiceBuffer(const struct filter_matchbuffer p_filtermap[])

Pointer type for nested struct in C

Is it possible to create a pointer to the inner struct of a nested struct in C?
#include <stdio.h>
#include <stdint.h>
typedef struct
{
uint8_t first;
struct
{
uint8_t second;
uint8_t third[8];
} inner_struct;
} outer_struct;
int main()
{
outer_struct foo;
void * p = &foo.inner_struct;
printf("%d", sizeof(p->third));
return 0;
}
Using a void pointer I could point to it, but I get this error
main.c: In function 'main':
main.c:26:26: error: request for member 'third' in something not a structure or union
printf("%d", sizeof(p->third));
^~
when trying to get the size of the 'third' array. I could also use a pointer to outer_struct but the real example is event more nested and contains long variable names, making it hard to read. Is it possible to create a pointer to the inner struct directly?
Using
struct inner_struct * p = &foo.inner_struct; instead of void yields
main.c: In function 'main':
main.c:25:31: warning: initialization from incompatible pointer type [-Wincompatible-pointer-types]
struct inner_struct * p = &foo.inner_struct;
^
main.c:26:26: error: dereferencing pointer to incomplete type 'struct inner_struct'
printf("%d", sizeof(p->inner_struct.third));
^~
The type void * is a generic pointer that can point to anything. But the compiler doesn't actually know what it points to, so you have to tell it by using casting.
So for p->third to work, you need to cast the pointer p to the correct pointer type.
Unfortunately this is not possible with your current code, as the inner structure is an anonymous structure without a known tag (structure name). You need to create a structure tag that you can use for the casting. For example
typedef struct
{
uint8_t first;
struct inner_struct
{
uint8_t second;
uint8_t third[8];
} inner_struct;
} outer_struct;
Now you can cast the pointer p, like ((struct inner_struct *) p)->third.
Or define p to be the correct type immediately:
struct inner_struct *p = &foo.inner_struct;

Const qualifier ignored [duplicate]

This question already has answers here:
typedef pointer const weirdness
(6 answers)
Closed 8 years ago.
I have a struct type_s. Then I typedef a pointer to a struct type_s as type.
If I have a const struct type_s* then the compiler will correctly complain if an assignment is made to the struct member, as seen in function Test1(). But if I make a const type, which is a typedef for the same struct pointer the compiler will not complain and compile, function Test2().
typedef struct type_s* type ;
struct type_s
{
int a ;
} ;
void Test1( const struct type_s* t )
{
t->a = 123 ; //complains, ok
}
void Test2( const type t )
{
t->a = 123 ; //doesn't, it should in my oppinion
}
To me they are logically both the same thing. What am I missing?
Is the only solution to create another typedef for a constant pointer to that struct like this:
typedef const struct type_s* ctype ;
which will work correctly as in Test1().
What you're missing is that a const pointer is not the same thing as a pointer to a const object. You have one of each. A const pointer is a pointer whose stored address is not modifiable; a pointer to const is one which cannot be used to modify its referent.
They are not the same thing. const has different semantics when applied to the definition or declaration of a pointer variable and a non-pointer variable.
const struct type_s *t
The above statement defines t to be a pointer to an object of type const struct type_s. Here, const qualifies the type struct type_s of the object t points to, not the pointer t.
typedef struct type_s *type ;
The above statement defines an alias named type for the type struct type_s *.
const type t;
// equivalent to
type const t;
// equivalent to
struct type_s *const t;
The above statement defines t to be a constant object of type type. Here const qualifies the object. You cannot replace the typedef with its original type and reinterpret it as if it were a macro. The pointer information is embedded in the typedef and it will be treated as a non-pointer type.

gcc - error: dereferencing pointer to incomplete type

I have a rather convoluted set of nested structs/unions as shown:
typedef enum {
expr_BooleanExpr,
expr_ArithmeticExpr
} expr_type;
typedef union {
struct BooleanExpr *_bool;
struct ArithmeticExpr *_arith;
} u_expr;
typedef struct {
expr_type type;
u_expr *expr;
} Expression;
typedef struct {
Expression *lhs;
char *op;
Expression *rhs;
} BooleanExpr;
typedef struct {
Expression *lhs;
char *op;
Expression *rhs;
} ArithmeticExpr;
gcc is happy for me to create an Expression struct containing a BoolExpression value in its union field as shown:
Expression *BooleanExpr_init(Expression *lhs, char *op, Expression *rhs) {
BooleanExpr *_bool = safe_alloc(sizeof(BooleanExpr));
_bool->lhs = lhs;
_bool->op = op;
_bool->rhs = rhs;
Expression *the_exp = safe_alloc(sizeof(Expression));
the_exp->type = expr_BooleanExpr;
the_exp->expr->_bool = _bool;
return the_exp;
}
although it gives a warning: assignment from incompatible pointer type [enabled by default] for the line: the_exp->expr->_bool = _bool;
However, when accessing the inner expressions such as lhs and rhs, with an expression like
an_expr->expr->_bool->rhs
where an_expr is a previously created Expression struct, I get the error specified in the title of this post.
Much of what I've read says that this results from the use of the -> operator where the . operator is required. However this is not appropriate since everything is a pointer, so the implicit dereference of the -> operator is required.
Any ideas?
You are mixing typedef identifiers and struct scope identifiers. This can't work. Do something like
typedef struct BooleanExpr BooleanExpr;
before all your struct declarations and have these only as
struct BooleanExpr { ...
without the typedef.
In your code you never defined struct BooleanExp but only an anonymous struct that you alias to the identifier BooleanExp.

Resources