Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C struct initialization done "recursively"

Tags:

c

gcc

struct

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;
like image 577
Bob Avatar asked Nov 07 '22 16:11

Bob


1 Answers

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, };
like image 63
syntagma Avatar answered Nov 15 '22 05:11

syntagma