Hi.

While I haven't really found a way to get reference semantics (hints
appreciated), I have now made my discoveries from
https://blind.guru/boost_python-and-boost_variant.html
into a handy little utility class.  This is the best I was able to
figure out regarding integration of boost::python with boost::variant.
I was hoping to be able to utilize some sort of custom instance_holder,
but it seems the library forces me to declare the C++ type for the
object held.  This prevents me from using a visitor to create the python
object at runtime.  I would basically need something that returns a
boost::python::object, not a concrete C++ type.  I haven't found that in
the docs/code so far.  The best I could find was to_python_converter,
but that (at least to me) seems limited to copy semantics.

Any hint on how to improve the helper below is very welcome.
Maybe someone finds it useful.  There seems to be virtually nothing on
the Net about this topic.

---<variant_adaptor.hpp>---
#if !defined(MLANG_PYTHON_VARIANT_ADAPTOR_HPP)
#define MLANG_PYTHON_VARIANT_ADAPTOR_HPP

#include <boost/python/implicit.hpp>
#include <boost/python/object.hpp>
#include <boost/python/to_python_converter.hpp>
#include <boost/variant.hpp>

namespace mlang {
namespace python {

template<typename Variant> struct variant_adaptor {
  variant_adaptor() {
    boost::python::to_python_converter<Variant, variant_adaptor>{};
    register_convertibles<Variant>::apply();
  }

  template<typename T>
  struct register_convertibles;
  template<typename T>
  struct register_convertibles<boost::variant<T>> {
    static void apply() {
      boost::python::implicitly_convertible<T, Variant>();
    }
  };
  template<typename T, typename... Ts>
  struct register_convertibles<boost::variant<T, Ts...>> {
    static void apply() {
      boost::python::implicitly_convertible<T, Variant>();
      register_convertibles<boost::variant<Ts...>>::apply();
    }
  };

  static PyObject *convert(Variant const &v) {
    return apply_visitor(to_python{}, v);
  }
  struct to_python : boost::static_visitor<PyObject *> {
    template<typename T> result_type operator()(T const &t) const {
      return boost::python::incref(boost::python::object(t).ptr());
    }
  };
};

} // namespace python
} // namespace mlang

#endif // !defined(MLANG_PYTHON_VARIANT_ADAPTOR_HPP)
---<snip>---

A small usage example would be:
#include <boost/python/class.hpp>
#include <boost/python/def.hpp>
#include <boost/python/init.hpp>
#include <boost/python/module.hpp>
#include <boost/python/object.hpp>
#include <boost/python/suite/indexing/vector_indexing_suite.hpp>
#include "variant_adaptor.hpp"
#include <vector>

struct a { int x; };
struct b { std::string y; };
bool operator==(a const &lhs, a const &rhs) { return lhs.x == rhs.x; }
bool operator==(b const &lhs, b const &rhs) { return lhs.y == rhs.y; }

typedef boost::variant<a, b> variant;
typedef std::vector<variant> vector;
variant make_variant() { return variant(); }
vector make_vector() { return vector{a(), b(), a()}; }

BOOST_PYTHON_MODULE(bpva) {
  using namespace boost::python;

  // Define classes for all variant member types
  class_<a>("a", init<a>()).def(init<>()).def_readwrite("x", &a::x);
  class_<b>("b", init<b>()).def(init<>()).def_readwrite("y", &b::y);
  mlang::python::variant_adaptor<variant>{};

  class_<vector>("vector").def(vector_indexing_suite<vector, true>());

  def("make_variant", make_variant);
  def("make_vector", make_vector);
}

As described in the blog post above, the resulting Python module does
work, but it does not allow to assign to members of a container
element.  Replacing a container element with a new one does work though.
So while the Python behaviour is a bit unexpected, it is likely OKish
for most use cases.  I wonder if I am
* missing an obvious technique to get this right, OR
* hitting a fundamental limitation of Boost.Python, OR
* hitting a fundamental limitation of value semantics vs. dynamic languages?

-- 
CYa,
  ⡍⠁⠗⠊⠕
_______________________________________________
Cplusplus-sig mailing list
Cplusplus-sig@python.org
https://mail.python.org/mailman/listinfo/cplusplus-sig

Reply via email to