I've newly come across an example of C-struct initialization that was explained by this question.
What I don't understand is what appears to be recursive definition; this is from MicroPython/objtype.c
typedef struct _mp_obj_type_t mp_obj_type_t;
const mp_obj_type_t mp_type_type = { // <-- SAME SYMBOL DEFINED HERE . . .
{ &mp_type_type }, // <-- IS USED HERE
.name = MP_QSTR_type,
.print = type_print,
.make_new = type_make_new,
.call = type_call,
.unary_op = mp_generic_unary_op,
.attr = type_attr,
};
The fields specified by .<some_field> I understand (see link in first sentence).
But about the "recursive" initialization?
There are other instances in the MicroPython code that use this syntax:
const mp_obj_type_t pyb_led_type = {
{ &mp_type_type }, <-- SAME SYMBOL AS ABOVE
.name = MP_QSTR_LED,
.print = led_obj_print,
.make_new = led_obj_make_new,
.locals_dict = (mp_obj_t)&led_locals_dict,
};
This makes more sense: the struct pyb_led_type is initialized with defaults set in struct mp_type_type and certain fields are changed from the default.
But what about const mp_obj_type_t mp_type_type?
The struct mp_type_type is defaulted to the values of . . . struct mp_type_type . . . ???
The pre-processed output is identical to .c.
What's going on here?
Here's few fields of the struct
struct _mp_obj_type_t {
// A type is an object so must start with this entry, which points to mp_type_type.
mp_obj_base_t base;
// The name of this type.
qstr name;
// Corresponds to __repr__ and __str__ special methods.
mp_print_fun_t print;
...
};
struct _mp_obj_base_t {
const mp_obj_type_t *type MICROPY_OBJ_BASE_ALIGNMENT;
};
typedef struct _mp_obj_base_t mp_obj_base_t;
Self-referencing structs in C
The MicroPython's code you are quoting is simply creating a self-referential struct instance, which is perfectly fine in C. Consider this example, which is pretty much you example stripped of some unnecessary parts:
#include "stdio.h"
// const base
struct A {
const struct A* base;
};
// non-const base
struct B {
const struct B* base;
};
const struct A a = { &a };
const struct B b = { &b };
int main() {
printf("%p %p\n", (void*) &a, (void*)a.base);
printf("%p %p\n", (void*) &b, (void*)b.base);
return 0;
}
Specific use in the instantiation of mp_obj_type_t structs in MicroPython's code
MicroPython project is using base pointer to implement (multiple) inheritance in Python. The base reference is a pointer to another type which is a base type ("parent" in the type hierarchy), looking at the definition of this struct:
struct _mp_obj_type_t {
// A type is an object so must start with this entry, which points to mp_type_type.
mp_obj_base_t base;
// .. many more fields
}
The case you are mentioning is mp_type_type const variable seems to be base type of all types, thus the self-reference but it makes much more sense when you look at the types that "inherit" from mp_type_type, like pyb_led_type:
const mp_obj_type_t pyb_led_type = {
{ &mp_type_type },
.name = MP_QSTR_LED,
.print = led_obj_print,
.make_new = led_obj_make_new,
.locals_dict = (mp_obj_t)&led_locals_dict, };
Objects in this system (MicroPython) start with a pointer to their type. MicroPython Types are represented as objects of type mp_type_type. An mp_type_type is a type itself so its type field points to itself. (Concept is perhaps best illustrated in the Smalltalk literature. cf: http://pharo.gforge.inria.fr/PBE1/PBE1ch14.html perhaps.)
const mp_obj_type_t mp_type_type = { // <-- SAME SYMBOL DEFINED HERE . . .
{ &mp_type_type }, // <-- IS USED HERE
.name = MP_QSTR_type,
...
};
The fields specified by . I understand (see link in first
sentence).
But about the "recursive" initialization?
There nothing there that could reasonably be characterized as recursive initialization. That would be initializing an object or one of its members with the value of that object, which C indeed forbids, but no such thing is in evidence.
Your example shows a member of an object being initialized with the address of that object (that's what the & operator computes). The address of an object does not depend in any way on that object's value, and it can safely be computed before the object is initialized. Such practice is not even all that uncommon, in a general sense, though it is unusual specifically to initialize part of an object with a pointer to that object.
Related
I'd like to keep my keywords in a struct:
typedef const char* label;
const struct keywords_t
{
label init;
label moveUnit;
} keywords;
But I'd like to be able to check whether a keyword is valid with a for loop, so I tried to initialize the struct like this:
const label allowed_keywords[] =
{
"INIT",
"MOVE"
};
const struct keywords_t keywords =
{
allowed_keywords[0],
allowed_keywords[1]
};
This gives me an
error: initializer element is not constant
What am I doing wrong? The whole array is constant, though.
In C constvariables are not constants, they are simply variables that cannot be changed. Thus their value cannot be used in constant expressions, like in struct initializer.
One work-around could be to use preprocessor defines for initializers:
#define KEYWORD_INIT "INIT"
#define KEYWORD_MOVE "MOVE"
const label allowed_keywords[] =
{
KEYWORD_INIT,
KEYWORD_MOVE
};
const struct keywords_t keywords =
{
KEYWORD_INIT,
KEYWORD_MOVE
};
Other approach could be to use combination of enumeration and array:
typedef enum {
KEYWORD_INIT,
KEYWORD_MOVE
} label;
const char * const keyword_strings[] = {
"INIT",
"MOVE"
};
const struct keywords_t keywords =
{
KEYWORD_INIT,
KEYWORD_MOVE
};
// Getting keyword string would be like this:
// keyword_strings[keywords.moveUnit]
I might be the best resource for this, but both Arrays and Structs are simple memory mapping tools.
An array of char * and a struct containing the same number of char * should have the same memory structure and the same memory footprint.
So, in practice, all you need is to cast the array into the struct rather then anything else (this will also save you memory, although this is probably not so important).
i.e.
const label allowed_keywords[] =
{
"INIT",
"MOVE"
};
typedef const char* label;
const struct keywords_t
{
label init;
label moveUnit;
};
// struct keywords_t * keywords = (struct keywords_t *) allowed_keywords;
#define keywords ((struct keywords_t *) allowed_keywords)
A few things:
this needs to be tested.
I would avoid using the _t for the struct keywords_t, as _t is reserved for POSIX types. I use _s for structs and _pt for pointers... but that's up to you.
I am really not able to solve this issue.
error: initializer element is not constant
error: (near initialization for tca6507_leds1.leds.num_leds)
I think the problem is related to struct led_info *leds inside led_platform_data. Is this somehow not a const since it is a pointer? I am particularly baffled since led_platform_data and led_info are part of the linux kernel. tca6507_platform_data is also a part of a driver that is included in the kernel source.
Here is my initialization:
static struct led_info tca6507_led_infos[] = {
{
.name = "left_blue",
.default_trigger = "heartbeat",
},
};
static struct led_platform_data tca6507_leds2 = {
.leds = tca6507_led_infos,
};
struct tca6507_platform_data tca6507_leds1 = {
.leds = tca6507_leds1
};
All the structs are defined in header files that I did not write.
struct led_info {
const char *name;
const char *default_trigger;
int flags;
};
struct led_platform_data {
int num_leds;
struct led_info *leds;
};
struct tca6507_platform_data {
struct led_platform_data leds;
#ifdef CONFIG_GPIOLIB
int gpio_base;
void (*setup)(unsigned gpio_base, unsigned ngpio);
#endif
};
The problem is right in the middle of this three line bit:
struct tca6507_platform_data tca6507_leds1 = {
.leds = tca6507_leds1
};
What is the (compile time) value of tca6507_leds1? Note that it is a structure, not an array.
Compare that with the (valid):
static struct led_platform_data tca6507_leds2 = {
.leds = tca6507_led_infos,
};
What is the (compile time) value of tca6507_led_infos? (Note that it is an array, and the "value" of an array is the address of its first element.) And most importantly (perhaps I should not have buried the lede :-) ), compare with the (also valid):
static struct led_info tca6507_led_infos[] = {
{
.name = "left_blue",
.default_trigger = "heartbeat",
},
};
Note that instead of using the value of a variable, the initializer here is a brace-enclosed list of items. This (like the failing case) is an instance of struct led_info, which must contain one actual struct led_platform_data instance (not a pointer to one-or-more, as is used in struct led_platform_data).
(The above is meant as an exercise. The answer is, the value of a structure is the value of the structure—but that is not a compile-time constant. Moreover, you are trying to use the value of the structure that you have not yet finished initializing in the first place. You need X to set X but you have to find X first, which you do by finding X, and there's no end to the recursion.)
Can someone tell me the difference between these two versions of a declaration of a structure?
struct S
{
uint8_t a;
};
and
const struct S
{
uint8_t a;
}
Followed by:
void main(void)
{
struct S s = {1};
s.a++;
}
Hint, i've tried both versions for S in Visual Studio C++ 2010 Express so I know that both compile with errors.
Is the "const struct" doing nothing at all? "const struct S s = {1};" certainly does, but that's not the issue at the moment.
Regards
Rich
/********************************************/
I've just worked out what
const struct <typename> <{type}> <variable instances a, b, .., z>;
is doing:
When const is present before the "struct", all variable instances are const, as though they'd be defined with:
const struct <typename> a, b, z;
So it does do something, but not when there's no instance definitions in-line with the struct declaration.
Rich
A declaration of structure just defines the data type.
const qualifier appies to a variable not a data type. So adding const preceeding a struct declaration should be redundant at the most.
With:
const struct S
{
uint8_t a;
};
The const qualifier there is nonsense, and may even cause a compilation error with some C compilers. gcc issues a warning.
The intent appears to be to declare the data type struct S. In this case, the proper syntax is:
struct S
{
uint8_t a;
};
const struct S
{
uint8_t a;
};
is not a valid construct.
This
const struct S
{
uint8_t a;
} x;
could possibly be valid as you're declaring a variable x that is now const, meaning it cannot change.
The const qualifier applies to variables or members.
To instantiate a const variable, just specify const during instantiation.
What const does, is:
during compilation, verify that only reads are performed on the const variables
if the const variable is created with a value which can be resolved during compilation, put the variable in the program memory
When const is applied to members, like in:
struct T {
int const i;
int j;
};
You can only (legally) assign the value i during the creation of the structure.
You may be able to modify a const value (if the program memory sits in RAM and not ROM) by casting it to a non-const type (const-cast) but this is something you shouldn't do.
The typical usage of const-cast is when you use a library which does not specify the constness in function declarations, and your code does. At this point if you want to use it you have to trust it and cast parameters before calling its functions.
It is nonsense to use const keyword before struct.
If you are using gcc compiler, it shows you the following warning:
warning: useless type qualifier in empty declaration [enabled by default]
This is the only use I can think of:
const struct S {
int a;
int b;
} s;
This declares a struct and immediately creates an instance for it named s and at this point, a and b in s are initialized to 0 (please note that at this point s is a global variable in the translation unit which it has been declared in and can be externally linked to).
printf("a = %d\t b = %d\n", s.a, s.b); // a = 0 b = 0
If you try to set members of s, you will fail:
s.a = 1; //error: assignment of member ‘a’ in read-only object
So, s is not really useful here...unless you do something like:
const struct S {
int a;
int b;
} s = { 1, 2 };
Now let's create another instance of the same struct (declaration is still same as above):
struct S other;
other.a = 1;
other.b = 2;
printf("a = %d\t b = %d\n", other.a, other.b); // a = 1 b = 2
The compiler will not complain anymore as other is not const! only s is const!
Now, what that const do, you may ask? let's try to change s:
s = other; // error: assignment of read-only variable ‘s’
That is all to it. If you did not need the compiler to allocate storage for s at the point of declaration and still needed an instance of s to be const you would just add const at the point of instantiating struct S (note the capital S!!)
Bonus 1
const struct S {
int a;
int b;
};
note that there is no small s anymore. At this point, GCC will warn you that const qualifier does not do anything!!!!
Bonus 2
If you want every instance of the struct to be const, that is its members can only be initialized at the point of definition you can do like (using typedef):
typedef const struct S {
int a;
int b;
} s;
// The wrong way
s thisIsAnS;
thisIsAnS.a = 1; //error: assignment of member ‘a’ in read-only object
// The correct way
s thisIsAnS = { 1 , 2 }; //compiles fine, but you can not change a or b anymore
Conclusion
To me, this is just syntactic sugar and only adds unnecessary complexity to the code. But who am I to judge...
When you declare
const var;
then it allocate the some memory space for it but
struct var;
it was just an declaration compiler does not allocate any space for it.
so it shows the error and in const struct you didn't declare any varible see the code so it shows error.
This is strictly in C.
Say you're given one base struct, and 2 other derived structs. These derived structs are not derived in the classical sense (ie: A : B), rather, they only contain structs of the base type. So if struct A is the base and B is one of the 2 derived structs, B would have a member of type A. Like this:
struct A {
// blah blah...
};
struct B {
A part_a;
// more stuff...
}
struct C {
A part_a;
// SO MUCH STUFF
}
Say you have a function, A_downcast_B, something like this:
B * A_downcast_B(A * a)
{
// downcast the A* here somehow
}
You want this function to return 0 or -1 if 'a' could not be successfully downcasted to a struct of type B. So for example, if a 'derived' struct of type C had a pointer to it of type A*, and that pointer were passed to this function, the function would return 0, -1 or null.
Is there a way to do this? I've been thinking about this for a few hours now and it's got me stumped.
It's impossible to do without embedding some sort of runtime type information in struct A. For example, you could store a pointer to some sort of type information, and you must initialize that any time you create any 'derived' struct.
struct RTTI {
const char *typename;
// etc.
};
struct A {
const RTTI *rtti;
// rest of A
};
struct B {
A part_a;
...
};
const RTTI RTTI_B = {"B"};
struct C {
A part_a;
...
};
const RTTI RTTI_C = {"C"};
void make_B(B *b)
{
b->part_a.rtti = &RTTI_B;
// make the rest of B
}
void make_C(C *c)
{
c->part_a.rtti = &RTTI_C;
// make the rest of C
}
B * A_downcast_B(A * a)
{
return (a->rtti == &RTTI_B) ? (B*)a : NULL;
}
Note that this is really just a simplified implementation of C++'s dynamic_cast operator, which only works with polymorphic data types. The reason that for that is that the runtime can only access the runtime type information if the object has some sort of type information embedded in it—its vtable.
While reading a code I came across, the following definition and initialization of a struct:
// header file
struct foo{
char* name;
int value;
};
//Implementation file
struct foo fooElmnt __foo;
// some code using fooElmnt
struct foo fooElmnt __foo = {
.name = "NAME";
.value = some_value;
}
What does this mean in C and how is it different from usual declarations?
It's called designated initialization,
In a structure initializer, specify
the name of a field to initialize with
.fieldname = before the element
value. For example, given the
following structure,
struct point { int x, y; };
the following initialization
struct point p = { .y = yvalue, .x = xvalue };
is equivalent to
struct point p = { xvalue, yvalue };
If you read on, it explains that .fieldname is called a designator.
UPDATE: I'm no C99 expert, but I couldn't compile the code. Here's the changes I had to make:
// header file
struct foo{
char* name;
int value;
};
//Implementation file
//struct foo fooElmnt __foo;
// some code using fooElmnt
struct foo fooElmnt = {
.name = "NAME",
.value = 123
};
Were you able to compile it? I used TCC.
Those are designated initializers, introduced in c99. You can read more here
Without them, you'd use
struct foo fooElmnt __foo = {
"NAME",
some_value
};
While in this case it doesn't matter much - other than the c99 way is more verbose, and its easier to read which element is initialized to what.
It does help if your struct has a lot of members and you only need to initialize a few of them to something other than zero.
This is a designated initialization. This also initializing the fields by their name, which is more readable than anomynous initialization when the structures are getting large. This has been introduced by the C99 standard.