Das Konzept ResultConverter wurde entwickelt, um dieses Problem zu lösen. Das CallPolicy-Modell return_value_policy
verwendet einen ResultConverterGenerator, um einen ResultConverter zu erstellen, und der ResultConverter wird verwendet, um den Rückgabewert einer Funktion zu ändern, die Python ausgesetzt ist. In diesem Fall könnte ein benutzerdefinierter Typ, der das ResultConverter-Konzept implementiert, verwendet werden, um entweder Python None
zurückzugeben oder ein Objekt mit der entsprechenden Python-Klasse zu instanziieren. Während die Dokumentation Liste aller Art Anforderungen, kann es näher mit etwas einfacher zu verstehen sein Code ähnelt:
/// @brief The ResultConverterGenerator.
struct result_converter_generator
{
template <typename T>
struct apply
{
struct result_converter
{
// Must be default constructible.
result_converter();
// Return true if T can be converted to a Python Object.
bool convertible();
// Convert obj to a PyObject, explicitly managing proper reference
// counts.
PyObject* operator(const T& obj);
// Returns the Python object that represents the type. Used for
// documentation.
const PyTypeObject* get_pytype();
};
/// @brief The ResultConverter.
typedef result_converter type;
};
};
Oft, wenn eine benutzerdefinierte ResultConverter Modell erstellen, eine Vorlage Meta-Programmierung verwenden können, die Chancen zu minimieren Laufzeitfehler in Konvertierungen und sogar Fehler bei der Kompilierung und liefern aussagekräftige Nachrichten. Hier finden Sie ein vollständiges Beispiel für return_optional
, ein ResultConverter-Modell, das ein C++ boost::optional<T>
-Objekt verwendet und es in das entsprechende Python-Objekt konvertiert.
#include <boost/mpl/if.hpp>
#include <boost/optional.hpp>
#include <boost/python.hpp>
#include <boost/type_traits/integral_constant.hpp>
#include <boost/utility/in_place_factory.hpp>
/// @brief Mockup model.
class spam {};
/// @brief Mockup factory for model.
boost::optional<spam> make_spam(bool x)
{
return x ? boost::optional<spam>(boost::in_place()) : boost::none;
}
namespace detail {
/// @brief Type trait that determines if the provided type is
/// a boost::optional.
template <typename T>
struct is_optional : boost::false_type {};
template <typename T>
struct is_optional<boost::optional<T> > : boost::true_type {};
/// @brief Type used to provide meaningful compiler errors.
template <typename>
struct return_optional_requires_a_optional_return_type {};
/// @brief ResultConverter model that converts a boost::optional object to
/// Python None if the object is empty (i.e. boost::none) or defers
/// to Boost.Python to convert object to a Python object.
template <typename T>
struct to_python_optional
{
/// @brief Only supports converting Boost.Optional types.
/// @note This is checked at runtime.
bool convertible() const { return detail::is_optional<T>::value; }
/// @brief Convert boost::optional object to Python None or a
/// Boost.Python object.
PyObject* operator()(const T& obj) const
{
namespace python = boost::python;
python::object result =
obj // If boost::optional has a value, then
? python::object(*obj) // defer to Boost.Python converter.
: python::object(); // Otherwise, return Python None.
// The python::object contains a handle which functions as
// smart-pointer to the underlying PyObject. As it will go
// out of scope, explicitly increment the PyObject's reference
// count, as the caller expects a non-borrowed (i.e. owned) reference.
return python::incref(result.ptr());
}
/// @brief Used for documentation.
const PyTypeObject* get_pytype() const { return 0; }
};
} // namespace detail
/// @brief Converts a boost::optional to Python None if the object is
/// equal to boost::none. Otherwise, defers to the registered
/// type converter to returs a Boost.Python object.
struct return_optional
{
template <class T> struct apply
{
// The to_python_optional ResultConverter only checks if T is convertible
// at runtime. However, the following MPL branch cause a compile time
// error if T is not a boost::optional by providing a type that is not a
// ResultConverter model.
typedef typename boost::mpl::if_<
detail::is_optional<T>,
detail::to_python_optional<T>,
detail::return_optional_requires_a_optional_return_type<T>
>::type type;
}; // apply
}; // return_optional
BOOST_PYTHON_MODULE(example)
{
namespace python = boost::python;
python::class_<spam>("Spam")
;
python::def("make_spam", &make_spam,
python::return_value_policy<return_optional>());
}
Interaktive Nutzung:
>>> import example
>>> assert(isinstance(example.make_spam(True), example.Spam))
>>> assert(example.make_spam(False) is None)
Die Kompilierung Typüberprüfung erfolgt, wenn der return_optional
ResultConvert versucht wird, mit einer Funktion verwendet werden, die einen Wert zurückgibt, der kein boost::optional
ist. Wenn zum Beispiel die folgenden verwendet:
struct egg {};
egg* make_egg();
BOOST_PYTHON_MODULE(example)
{
namespace python = boost::python;
python::def("make_egg", &make_egg,
python::return_value_policy<return_optional>());
}
Der Compiler wählt wählen Sie die return_optional_requires_a_optional_return_type
Implementierung und Kompilierung fehlschlagen. Hier ist ein Teil des Compilers Fehlermeldung call bietet:
error: no member named 'get_pytype' in
'detail::return_optional_requires_a_optional_return_type<egg *>'
Dies sollte wirklich ein Teil der Boost :: Python-Bibliothek sein. Wirklich nett! – Felix
Dies ist eine wirklich phänomenale Antwort. – Mohan