ead
ead

Reputation: 34367

Cython: How user-defined conversion of C++-classes can be used?

Cython's documentation seems to be silent about how user-defined conversion can be wrapped.

For example, while the following c++-code prints 1 (i.e. true, here live):

#include <iostream>

struct X{
    operator bool() const{ return true;}    
};

int main() {
    X x;
    std::cout << x << "\n";
}

its "equivalent" in Cython:

%%cython  -+
cdef extern from *:
    """
    struct X {
        //implicit conversion
        operator bool() const { return true; }
    };
    """
    cdef cppclass X:
        operator bool()  # ERROR HERE

def testit():
    cdef X x;
    print(x) # implicit cast, "should" print True

doesn't get cythonized with the following error message (in the line marked with ERROR HERE):

'operator' is not a type identifier

How user-defined conversion can be used from Cython and if not what are workarounds?

Upvotes: 1

Views: 709

Answers (2)

ead
ead

Reputation: 34367

This are only some additions to DavidW's answer.

As already pointed out, only operator bool is supported by Cython - other user-defined conversions, like:

cdef cppclass X:
    int operator int()

will result in an error message like

Overloading operator 'int' not yet supported.

A possible workaround is not to wrap the user-defined conversion, but to use explicit cast whenever needed. For example:

%%cython  -+ -a
cdef extern from *:
    """
    struct X {
        //implicit conversion
        operator int() const { return 42; }
    };
    """
    cdef cppclass X:
        pass # leave operator int() out

def testit():
    cdef X x;
    print(<int>x)

which compiles and prints 42 once testit is called. Cython doesn't intervene with explicit cast here.

Ironically, the above workaround would not work for operator bool():

%%cython  -+ -a
cdef extern from *:
    """
    struct X {
        //implicit conversion
        operator bool() const { return true; }
    };
    """
    cdef cppclass X:
        pass # leave operator bool() out

def testit():
    cdef X x;
    if <bint>x:
       print(True)
    else:
       print(False)

leads to error message:

Object of type 'X' has no attribute 'operator bool'

Obviously, this check is a part of the operator bool()-support package..

One could however use cast to int instead of cast to bool/bint to achieve the goal:

...
if <int>x:
...

However, should wrapping the operator bool() should be prefered.


In a nutshell:

  1. use bint operator bool() to wrap C++'s operator bool()
  2. don't wrap and use explicit cast for other operators.

Upvotes: 1

DavidW
DavidW

Reputation: 30917

Looking only at the bool case:

  1. I'm not convinced print(x) should convert it to bool anyway. print(x) looks for a conversion to a Python object (and OK, bool can be converted to a Python object, but it's somewhat indirect). Python itself uses the __bool__ (__nonzero__ in Python 2) only in fairly limited circumstances, like in an if statement, and Cython typically follows Python behaviour as a rule. Therefore I've changed the test-code to

    def testit():
        cdef X x
        if x:
            print("is True")
        else:
            print("if False")
    
  2. operator bool() gives the error

    'operator' is not a type identifier

    I assumed it needed to start with the return type like every other C++ function (i.e. no special case for operator). This works (sort of... see next point...):

    bool operator bool()
    

    and this syntax is the one tested for in Cython's testsuite.

  3. However, you do need to do from libcpp cimport bool at the top of the file to get the C++ bool type.

If you look at the converted source for if x: it ends up as

__pyx_t_1 = __pyx_v_x.operator bool();
if (__pyx_t_1) {

operator bool is called explicitly (which is pretty common for Cython), but is used in the correct place so Cython clearly understands what it's for. Similarly, if you do if x: without defining the operator you get the error

Object of type 'X' has no attribute 'operator bool'

again, suggesting this is a feature of Cython.

There's clearly a bit of a documentation failure here, and I wouldn't be 100% surprised if the syntax changed to match C++ a bit closer in the future, maybe.


For the more general case: it looks like bool is the only type-conversion operator supported at the moment, so you aren't able to define other operators.

Upvotes: 2

Related Questions