Question

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.

Was it helpful?

Solution

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
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top