Page 1 of 1

How to invoke own method from C?

Posted: Mon Jan 27, 2020 6:13 pm
by jamesbowman
I am porting some code that is structured like this:

Code: Select all

class stem:
    def flush(self):
        self.write(self, <blah>)
class Dumper(stem):
    def write(self, bb):
        print("---> WRITE called", bb)

That is Dumper inherits from stem, while stem.flush() calls method write.

What I am trying to do is implement class stem as a MicroPython native module, for speed.

My question is, how does the C implementation of flush() find the method named write? I have so far failed to find an example of this in the existing code base. My attempts to implement it from scratch also didn't work.

What I have done as a hacky workaround is to implement a register method in stem which stores the write function as a callable. So the Python side looks like this:

Code: Select all

f = Dumper()
And the C side looks like this: ... mpy/stem.c

This works fine, but it would be much better to do this properly.

Any pointers much appreciated, thanks.

Re: How to invoke own method from C?

Posted: Tue Jan 28, 2020 1:27 am
by jimmo
You're right that this isn't done very much in the core firmware (which is partially why deriving from built-in types doesn't work very well in Python). Mostly this is done for efficiency.

However, what you need to do is look up the write method by name, then invoke it using one of the mp_call_method variants.

If you search the codebase for mp_call_method you'll be able to find some examples. One such example is extmod/machine_pinbase.c where the ioctl method calls the type's value() method.

Re: How to invoke own method from C?

Posted: Wed Jan 29, 2020 4:30 am
by jamesbowman
Thanks, I have adjusted that code a little because it's in an external C module:

Code: Select all

      mp_obj_t dest[3];
      mp_fun_table.load_method(self, MP_QSTR_write, dest);
      dest[2] = b;
      mp_fun_table.call_method_n_kw(1, 0, dest);
At run time though it does not find the write method that is in the subclass Dumper:

Code: Select all

AttributeError: 'stem' object has no attribute 'write'
I'm not sure how to dig deeper on this. Do you think it could be related to running in a external module?

Re: How to invoke own method from C?

Posted: Mon Feb 03, 2020 1:25 am
by jimmo
Ahh looks like I gave you a bit of bad advice there. Sorry!

This is exactly the limitation with deriving from built-in types (which includes types defined in dynamic native modules). When dumper.flush is called, the "self" is actually a pointer to the stem. I'm not aware of a way to go backwards to the dumper instance.

However, there might be a workaround which you may be able to use (which is actually what's happening in machine_pinbase.c that I referenced earlier), which is that there's a special case for "protocols". See objtype.c line 1144, where it copies the protocol from the base into the derived.

So you might be able to use this if you only ever invoke flush from C code? (Or you could write a helper method in the same module as the stem class).

Re: How to invoke own method from C?

Posted: Tue Feb 18, 2020 4:42 am
by tannewt
We recently ran this down in CircuitPython because we wanted native code to have access to the instance rather than just the native struct. (Our PixelBuf class calls `_transmit` on the subclass.)

Here is the PR: There was definitely work to make the existing native functions correctly get the native struct as they needed it.

Re: How to invoke own method from C?

Posted: Tue Feb 18, 2020 11:23 am
by Damien
I posted a possible solution to this in This allows the "stem" example to work as originally intended.