why does .make_new take as arg mp_obj_type_t *type?

C programming, build, interpreter/VM.
Target audience: MicroPython Developers.
Post Reply
jickster
Posts: 629
Joined: Thu Sep 07, 2017 8:57 pm

why does .make_new take as arg mp_obj_type_t *type?

Post by jickster » Sat May 12, 2018 5:47 pm

Is this for inheritance?

Code: Select all

STATIC mp_obj_t type_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) {
    // instantiate an instance of a class

    mp_obj_type_t *self = MP_OBJ_TO_PTR(self_in);

    if (self->make_new == NULL) {
        if (MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE) {
            mp_raise_TypeError("cannot create instance");
        } else {
            nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError,
                "cannot create '%q' instances", self->name));
        }
    }

    // make new instance
    mp_obj_t o = self->make_new(self, n_args, n_kw, args);

    // return new instance
    return o;
}
Please give an example how/when you'd use that arg in a "make_new" function.

All examples I can find don't use that arg like "list_make_new"

Code: Select all

STATIC mp_obj_t list_make_new(const mp_obj_type_t *type_in, size_t n_args, size_t n_kw, const mp_obj_t *args) {
    (void)type_in;
    mp_arg_check_num(n_args, n_kw, 0, 1, false);

    switch (n_args) {
        case 0:
            // return a new, empty list
            return mp_obj_new_list(0, NULL);

        case 1:
        default: {
            // make list from iterable
            // TODO: optimize list/tuple
            mp_obj_t list = mp_obj_new_list(0, NULL);
            return list_extend_from_iter(list, args[0]);
        }
    }
}

stijn
Posts: 735
Joined: Thu Apr 24, 2014 9:13 am

Re: why does .make_new take as arg mp_obj_type_t *type?

Post by stijn » Sat May 12, 2018 6:04 pm

It's to let the C code know what type is being made upon __init__ etc. So yes inheritance comes to mind, and reusing make_new implementations, possibly other things. Search the whole codebase for 'make_new' and you'll find a bunch of usages, see e.g. dict_make_new, set_make_new, mp_obj_exception_make_new, ...

jickster
Posts: 629
Joined: Thu Sep 07, 2017 8:57 pm

Re: why does .make_new take as arg mp_obj_type_t *type?

Post by jickster » Mon Jul 02, 2018 11:35 pm

stijn wrote:
Sat May 12, 2018 6:04 pm
It's to let the C code know what type is being made upon __init__ etc. So yes inheritance comes to mind, and reusing make_new implementations, possibly other things. Search the whole codebase for 'make_new' and you'll find a bunch of usages, see e.g. dict_make_new, set_make_new, mp_obj_exception_make_new, ...
There's 6 matches for \Wmake_new\(

You already know what type is being made because of function you're executing in.
If you're in .make_new = X, then you're making a Y: (float_make_new, float), (list_make_new, list), etc
So you don't need to pass in the type because the function called to make a new type is specific to the type to be made.

Give an example IN THE CODE where the first argument is of a different type than that of the .make_new's value.

Code: Select all

STATIC mp_obj_t type_call(mp_obj_t self_in, size_t n_args, size_t n_kw, const mp_obj_t *args) {
    // instantiate an instance of a class

    mp_obj_type_t *self = MP_OBJ_TO_PTR(self_in);

    if (self->make_new == NULL) {
        if (MICROPY_ERROR_REPORTING == MICROPY_ERROR_REPORTING_TERSE) {
            mp_raise_TypeError("cannot create instance");
        } else {
            nlr_raise(mp_obj_new_exception_msg_varg(&mp_type_TypeError,
                "cannot create '%q' instances", self->name));
        }
    }

    // make new instance
    mp_obj_t o = self->make_new(self, n_args, n_kw, args);

    // return new instance
    return o;
}
By the definition of this code, first arg will ALWAYS be type that contains the .make_new function that is called.

Code: Select all

mp_obj_t o = self->make_new(self, n_args, n_kw, args);
In fact, in all 6 uses of .make_new the first arg IS the type that contains the .make_new function that is called.

In mp_obj_instance_make_new - where a new .py instance is created - the same formula is used:
The type passed as arg[0] is the type whose .make_new is called

Code: Select all

o->subobj[0] = native_base->make_new(native_base, n_args, n_kw, args);

User avatar
dhylands
Posts: 3821
Joined: Mon Jan 06, 2014 6:08 pm
Location: Peachland, BC, Canada
Contact:

Re: why does .make_new take as arg mp_obj_type_t *type?

Post by dhylands » Tue Jul 03, 2018 1:42 am

jickster wrote:
Mon Jul 02, 2018 11:35 pm
Give an example IN THE CODE where the first argument is of a different type than that of the .make_new's value.
Perhaps you missed static_class_method_make_new? Which is called here: https://github.com/micropython/micropyt ... pe.c#L1167 and referenced twice with different types here:
https://github.com/micropython/micropyt ... 1390-L1400

Damien
Site Admin
Posts: 647
Joined: Mon Dec 09, 2013 5:02 pm

Re: why does .make_new take as arg mp_obj_type_t *type?

Post by Damien » Tue Jul 03, 2018 5:46 am

As @stijn mentioned already, objdict.c and objset.c use this feature to support both dict/OrderedDict and set/frozenset types with the same make_new function.

Post Reply