Domanda

I would like to return an instance of the following python class, which mimics boost::tribool for three state logic, from my C++ extension module:

class Tribool(object):

def __init__(self, value = None):
    if any(value is v for v in (False, True, None)):
        self.value = value
    else:
        raise ValueError("Tribool must be False, True or None")

def __nonzero__(self):
    raise TypeError("Tribool may not be used as an implicit bool")

def __eq__(self, other):
    if isinstance(other, Tribool):
        return self.value is other.value
    if any(other is v for v in (False, True, None)):
        return self.value is other
    raise TypeError("Tribool can only be compared to another Tribool or False, True, or None")

def __ne__(self, other):
    return not (self == other)

def __invert__(self):
    if self.value is False:
        return Tribool(True)
    elif self.value is True:
        return Tribool(False)
    elif self.value is None:
        return Tribool(None)
    raise ValueError("Tribool must be False, True or None")

def __and__(self, other):
    if (self.value is False) or (other.value is False):
        return Tribool(False)
    if (self.value is None) or (other.value is None):
        return Tribool(None)
    return Tribool(True)

def __or__(self, other):
    if (self.value is True) or (other.value is True):
        return Tribool(True)
    if (self.value is None) or (other.value is None):
        return Tribool(None)
    return Tribool(False)

def __xor__(self, other):
    if (self.value is None) or (other.value is None):
        return Tribool(None)
    if self == other:
        return Tribool(False)
    return Tribool(True)

def __str__(self):
    if any(self.value is v for v in (False, True, None)):
        return str(self.value)
    raise ValueError("Tribool must be False_, True_ or Maybe_")

def __repr__(self):
    return "Tribool(%s)" % str(self)

I believe I need to write a to_python_converter, but I cannot figure out how to write a converter for a user defined python type. I have read the Boost Python documentation and searched with Google without finding a good example from which to draw. Any help is appreciated.


Update:

The following C++ code will convert a tribool into the Python values True, False and None which is a partial solution:

struct tribool_to_Tribool
{
    static PyObject *convert(tribool const& value)
    {
        if (value == true) return boost::python::incref(boost::python::object(true).ptr());
        else if (value == false) return boost::python::incref(boost::python::object(false).ptr());
        else return boost::python::incref(boost::python::object().ptr());
    }
};

to_python_converter<tribool,tribool_to_Tribool>();

The part I cannot figure out is how to return an instance of the purely Python Tribool class instead of the values True, False and None from the conversion routine.

È stato utile?

Soluzione

This can be accomplished in the same manner that one would return Tribool from a Python method. In either language, one needs to obtain a handle to the Tribool Python class object, and then invoke it. The object returned will be an instance of the Tribool type.

For example:

from tribool import Tribool
x = Tribool(False)

is equivalent to:

namespace python = boost::python;
python::object tribool_class = python::import("tribool").attr("Tribool");
python::object x = tribool_class(false);

Below is a simplified example where a user defined Python module (spam) contains a Python class (Spam), and a C++ Python extension module (example) provides a make_spam function that creates spam.Spam objects.

The spam.py Python module:

class Spam(object):

    def __init__(self, value):
        self.value = value

    def __str__(self):
        return self.__repr__() + " has a value of " + str(self.value)

The example.cpp extension module:

#include <boost/python.hpp>

boost::python::object make_spam(boost::python::object value)
{
  // from spam import Spam
  // return Spam(value)
  return boost::python::import("spam").attr("Spam")(value);
}

BOOST_PYTHON_MODULE(example)
{
  boost::python::def("make_spam", &make_spam);
}

Interactive Python:

>>> import example
>>> print example.make_spam(False)
<spam.Spam object at 0xb74be1ac> has a value of False
>>> print example.make_spam(True)
<spam.Spam object at 0xb74be10c> has a value of True
>>> print example.make_spam(None)
<spam.Spam object at 0xb74be1ac> has a value of None
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top