3

Is there a standardized (or commonly accepted way) to tackle the issue of not being able to overload __cinit__ methods in cython when wrapping C++ classes?

There is the possibility of making __cinit__ take *args and **kwargs to contain parameters and inside the __cinit__ scope select the way to generate the C++ instance according to certain pattern. This approach however has complications:

  • Trying to identify the correct constructor from the arguments might be dangerous in certain circumstances unless the arguments are named and passed as kwargs which might become cumbersome for users.
  • When the arguments are not invoked by their type in the __cinit__ argument list, they are passed as Python objects. In my working case, the arguments are Python wrappers for C++ classes, and trying to retrieve these instances "thisptr" is a task of the devil when they are passed as Python objects from the *args or **kwargs.
  • The implied need of using a sequence of if... elif... else cases to select a C++ constructor from the argument combination ultimately becomes quite messy.

So there has to be a better way. One way may be using the classmethod approach (e.g. see: classmethod to overload __init__ behavior in Python). Nevertheless, as far as I know (and please correct me if I'm wrong), classmethod implies invoking methods with names different of the class name and this does not resemble well the C++ way of constructing instances with just one class name.

Any ideas on this?

Community
  • 1
  • 1
vladimir montealegre
  • 2,010
  • 2
  • 15
  • 17

1 Answers1

2

Personally, I would use classmethods.

WrapperClass.fromSpambar(spambar)

It's not that puzzlin imho. If you wouldn't be able to overload functions in C++, you'd need to fallback to this approach as well.

In case you can accept some heavy weighted operations on initialization, you could implement a method to recognize the way the constructor was called by defining "patterns" or similar. I.e. Regular Expressions for args and kwargs. ;)

I don't see the problem of getting the thisptr from a Python-object.

cdef WrapperClass wrpclsi
if isinstance(instance, WrapperClass):
    wrpclsi = instance
else:
    raise TypeError('expected instance of WrapperClass.')

cdef WrappedClassFromCpp* thisptr = wrpclsi.thisptr 
Niklas R
  • 16,299
  • 28
  • 108
  • 203
  • Thanks a lot Niklas, yes it seems that the way to go about this will be using classmethods, that would also make safer the processing for specific combinations of argument types. Regex would probably be useful in combination with classmethods (for instance, selecting a specific classmethod constructor given a combination of argument types, if it makes sense). – vladimir montealegre Jun 11 '12 at 20:12
  • Regarding the thisptr part... yes, I think this point needed some clarity on what I wanted to say. My point was that declaring something like `def __cinit__(self, PyKlass klass) ` there will be no problem invoking `klass.thisptr` inside the `__cinit__` scope. However, declaring: `def __cinit(self, *args) ` assuming there is a PyKlass instance in args[0], invoking `args[0].thisptr` will give trouble, even if args[0] is an instance of PyClass type. It turns out that is not a big deal and can be worked around by simply casting inside `__cinit__`, like: `klass = args[0]` – vladimir montealegre Jun 11 '12 at 20:28
  • Hi tachys. I didn't actually talk about real Regular Expressions, just a something that is working with a comparable mechanism but matching *args* and *kwargs*. The same way a function maps the passed arguments on the arguments it accepts, you could process *args* and *kwargs* but allowing multiple calling conventions. However, this would involve processing which might slow down massively. I may be able to write some example code the next day. – Niklas R Jun 11 '12 at 20:48
  • Doesn't assigning an Python-object to a *cdeffed* variable already cast it automatically? This is what I've done in the above example. (can't test it right now, and the code above was just from scratch) – Niklas R Jun 11 '12 at 20:49
  • Niklas: Thank you, as a matter of fact it seems that parsing the `__cinit__` arguments will be unescapable, even if using classmethods (any classmethod will eventually resort to invoke `__cinit__`. I keep on looking for the most efficient way to deal with this, hopefully will post soon about it. About the casting note, if the argument is passed as Python object (as it is the case when the instance is an element of an argument list), casting will be necessary (that is, just for clarity, if the argument is not passed with the 'C' type) – vladimir montealegre Jun 15 '12 at 17:00