2014-10-21 11 views
5

boost::optional<T> döndüren bir işlevi sarmak istiyorum. Yani, verilir:Destek artıran bir C++ işlevi nasıl yapılır :: İsteğe bağlı <T>?

class Foo { 
    boost::optional<T> func(); 
}; 

Python ya bir değerle T veya None alır böylece her nasılsa o sarmak istiyorum: I could, sadece T döndü Normalde

class_<Foo>("Foo") 
    .def("func", func, return_value_policy<return_boost_optional???>); 

class_<Foo>("Foo") 
    .def("func", func, return_value_policy<return_by_value>()); 

Ama boost::optional<T> döndürür çünkü, o da ben Python enolarak bitirmek istediğiniz, boost::none geri dönebilirler: kullanmak.

Bunu, mevcut dönüştürücülerle yapmanın bir yolu var mı? Değilse, aynı etkiye ulaşmak için bazı geçici çözüm var mı?

cevap

10

ResultConverter konsepti bu sorunu çözmek için tasarlanmıştır. return_value_policy CallPolicy modeli, bir ResultConverter oluşturmak için bir ResultConverterGenerator kullanır ve ResultConverter, Python'a maruz bırakılan bir işlevin dönüş değerini değiştirmek için kullanılır. Bu durumda, ResultConverter konseptini uygulayan özel bir tür Python None'u döndürmek veya bir nesneyi uygun Python sınıfıyla başlatmak için kullanılabilir. Özel bir ResultConverter model oluştururken

/// @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; 
    }; 
}; 

Çoğu zaman, bir şansını en aza indirmek için şablon meta-programlama kullanabilirsiniz: dokümantasyon liste tüm türü şartları da, daha yakın kod benzeyen bir şeyle anlaşılan daha kolay olabilir dönüşümlerde çalışma zamanı hataları, hatta derleme zamanında hataları yakalayın ve anlamlı mesajlar verin. İşte bir C++ boost::optional<T> nesnesi alır ve uygun Python nesnesine dönüştüren bir ResultConverter modeli return_optional tam bir örneğidir.

#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>()); 
} 

etkileşimli kullanım:

>>> import example 
>>> assert(isinstance(example.make_spam(True), example.Spam)) 
>>> assert(example.make_spam(False) is None) 

tip kontrolü return_optional ResultConvert bir boost::optional olmayan bir değer veren bir fonksiyonu ile birlikte kullanılmak üzere çalışılır oluşan derleme zamanı. Örneğin, aşağıdaki durumlarda uygulanır:

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>()); 
} 

derleyici return_optional_requires_a_optional_return_type uygulanmasını seçmek seçin ve derleme başarısız olur. Burada derleyici hata mesajının bir kısmı verilmiştir:

 
error: no member named 'get_pytype' in 
'detail::return_optional_requires_a_optional_return_type<egg *>' 
+0

Bu gerçekten boost :: python kütüphanesinin bir parçası olmalıdır. Gerçekten hoş! – Felix

+0

Bu gerçekten olağanüstü bir cevaptır. – Mohan

İlgili konular