5

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;
Bob
  • 4,576
  • 7
  • 39
  • 107
  • 1
    There is no defaulting from other values. One of the fields in `mp_obj_type_t` is a pointer and it is initialized with a pointer value. – melpomene Sep 12 '17 at 19:53
  • It's the same as: `void * p = &p;` – alk Sep 12 '17 at 19:55
  • @melpomene to be clear: you're saying the first statement `{&mp_type_type}` is merely being assigned to the first member of the type `struct _mp_obj_type_t` ? – Bob Sep 12 '17 at 19:59
  • What is the definition of the `struct _mp_obj_type_t` ? This appears to be creating a struct which contains a pointer to itself for some use. I am curious how this is being used and why there is this kind of pointer to self. I can't think of why this particular self referencing object would be useful but that is probably due to a lack of imagination on my part. – Richard Chambers Sep 12 '17 at 20:01
  • @Adrian Well, presumably the first member is another struct, hence the additional braces. – melpomene Sep 12 '17 at 20:01
  • @RichardChambers I updated with definitions. Yes it all makes sense now. – Bob Sep 12 '17 at 20:04
  • Looks to me like they are doing a kind of object oriented, inheritance type of construct using C. So they begin with a base object shared by all objects and then this base object is extended to provide additional functionality. So this first `struct` member is a kind of virtual method table. – Richard Chambers Sep 12 '17 at 20:20

3 Answers3

2

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, };
syntagma
  • 23,346
  • 16
  • 78
  • 134
0

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.)

Zalman Stern
  • 3,161
  • 12
  • 18
-1
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.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157