Reputation: 83635
I am working on a program that uses a generic struct
in many places to shuttle around related values. This struct contains a field char* s
.
Many functions modify s
; however, sometimes the struct is used to pass information to functions which will only read it. In these cases often the string used to initialize s
is a const char*
. However, assigning it to s
causes a compiler warning.
Though technically correct, this warning feels wrong, as the function does not modify s
.
Is there a way around this warning, apart from just casting away the const? Is there some way for a function to promise it will treat a struct member as const
?
Example:
#include <stdio.h>
struct mystruct{
int i;
char* s;
};
void i_only_read(const struct mystruct *m){
printf("mystruct: i=%d, s=%s\n", m->i, m->s);
}
int main(int argc, char **argv){
const char* cstr = "Hello";
struct mystruct m;
m.i=99;
/* gcc warning: assignment discards ‘const’ qualifier
* from pointer target type
*/
m.s=cstr;
i_only_read(&m);
}
Notes
const char* s
, because most functions taking pointers to the struct do modify s
.char* s
and one with const char* s
, but this seems very ugly (creates redundancy, needs conversion functions between the two structs).struct attr
. I created a simple example for this question.Upvotes: 3
Views: 797
Reputation: 83635
Building on paddy's and jxh's ideas, I have come up with a solution that looks practical:
const char*
memberCode:
typedef struct const_mystruct {
int i;
const char * s;
} const_mystruct;
typedef union {
struct {
int i;
char *s;
};
const const_mystruct cms;
} mystruct;
This achieves the following goals (analogous to what the const
qualifier does for simple pointers):
const_mystruct*
if they want to promise not to modify the char array, and the compiler will enforce this.mystruct
is available for cases where the char array does need to be modified/free()
d.mystruct
can be converted to a const_mystruct
by reading mystruct.cms
.
The (potentially dangerous) conversion const_mystruct
-> mystruct
by writing mystruct.cms
is not possible, because mystruct.cms
is const
and thus not writable.Code illustrating the use:
#include <stdio.h>
#include <malloc.h>
/* [structs omitted] */
void i_only_read(const const_mystruct *m){
printf("mystruct: i=%d, s=%s\n", m->i, m->s);
}
void i_might_modify(mystruct *m){
printf("noconst: mystruct: i=%d, s=%s\n", m->i, m->s);
}
int main(void){
const char* cstr = "Hello";
const_mystruct cm;
cm.i=99;
cm.s=cstr;
// Method promises not to change the structure: OK.
i_only_read(&cm);
// Pass a "constant" structure into a method that might modify it:
// diagnosed by compiler (warning or error).
i_might_modify(&cm);
// Trying to remove "const" from the pointer: compiler will not allow this...
// m.cms.s=cstr;
mystruct m;
m.i=99;
m.s=malloc(10);
// Struct is not "const", so modification is OK.
i_might_modify(&m);
// Convert to "const" struct, without cast.
i_only_read(&(m.cms));
return 0;
}
This gives the same guarantees as using const
for pointers, which was my goal (sort of a "recursive const").
Potential problems:
struct
.I'll see if I can actually use this...
Upvotes: 2
Reputation: 70472
This solution is a little convoluted. It combines @paddy's suggestion of the union
to allow a const char *
assignment with another union
on a const
-ified version of the data structure to provide strict enforcement.
typedef struct const_mystruct {
const int i;
const char * const s;
} const_mystruct;
typedef union {
struct {
int i;
union {
char *s;
const char *cs;
};
};
const_mystruct cms;
} mystruct;
void i_only_read(const_mystruct *m){
printf("mystruct: i=%d, s=%s\n", m->i, m->s);
}
int main(int argc, char **argv){
const char* cstr = "Hello";
mystruct m;
m.i=99;
m.cs=cstr;
i_only_read(&m.cms);
}
Upvotes: 1
Reputation: 3807
I realize that this answer is what your question is trying to avoid. However, short of using a compiler other than gcc or torquing your code into un-readableness, then sometimes a disciplined approach and few comments in the code solve the problem!
What is the purpose of const
in the following code fragment?
void i_only_read(const struct mystruct *m){
printf("mystruct: i=%d, s=%s\n", m->i, m->s);
}
From C's point of view it implies that this function will NOT modify mystruct
. If somehow this definition is giving gcc
fits, then remove the the const
verbage and enforce the "do NOT update" perspective in the code.
// the programmer can/must only read mystruct
void i_only_read(struct mystruct *m){
printf("mystruct: i=%d, s=%s\n", m->i, m->s);
}
Upvotes: 0
Reputation: 63481
Interestingly, you seem to be able to do this in a union
:
struct mystruct {
int i;
union {
char *s;
const char *cs;
};
};
Now, the rules for union
apply: use only the union member that was assigned to. If the function 'promises' to behave, you can assign the string to cs
without warning.
Specifically, what you should not do is assign to cs
and then pass the struct as a non-const parameter.
Upvotes: 5