Skip to content

Instantly share code, notes, and snippets.

@Holt59
Last active May 15, 2022 13:20
Show Gist options
  • Save Holt59/cbcc4e98171490d6898f29b78eee8997 to your computer and use it in GitHub Desktop.
Save Holt59/cbcc4e98171490d6898f29b78eee8997 to your computer and use it in GitHub Desktop.
#include <memory>
#include <iostream>
#include <vector>
#include <pybind11/pybind11.h>
#include <pybind11/embed.h>
#include <pybind11/stl.h>
#include <pybind11/stl_bind.h>
namespace py = pybind11;
PYBIND11_MAKE_OPAQUE(std::vector<int>);
class X
{
std::vector<int> v_;
public:
X(std::vector<int> v) : v_{std::move(v)} {}
void set_v(std::vector<int> v)
{
v_ = std::move(v);
}
const auto &v() const { return v_; }
};
template <class Container>
class wrapped_for_const
{
const Container *pv_;
public:
using value_type = const typename Container::value_type;
using allocator_type = typename Container::allocator_type;
using size_type = typename Container::size_type;
using difference_type = typename Container::difference_type;
using iterator = typename Container::const_iterator;
using const_iterator = typename Container::const_iterator;
wrapped_for_const(const Container *pv) : pv_{pv}
{
}
wrapped_for_const(wrapped_for_const const &) = default;
wrapped_for_const(wrapped_for_const &&) = default;
wrapped_for_const &operator=(wrapped_for_const const &) = delete;
wrapped_for_const &operator=(wrapped_for_const &&) = delete;
auto empty() const { return pv_->empty(); }
auto size() const { return pv_->size(); }
auto begin() const { return pv_->begin(); }
auto end() const { return pv_->end(); }
decltype(auto) operator[](size_type const &s) const
{
return pv_->operator[](s);
}
friend bool operator==(wrapped_for_const const& lhs, wrapped_for_const const& rhs) {
return (*lhs.pv_) == (*rhs.pv_);
}
friend bool operator!=(wrapped_for_const const &lhs, wrapped_for_const const &rhs)
{
return (*lhs.pv_) != (*rhs.pv_);
}
};
template <typename Vector, typename holder_type = std::unique_ptr<Vector>, typename... Args>
py::class_<Vector, holder_type> bind_const_vector(py::handle scope, std::string const &name, Args &&...args)
{
using Class_ = py::class_<Vector, holder_type>;
// If the value_type is unregistered (e.g. a converting type) or is itself registered
// module-local then make the vector binding module-local as well:
using vtype = typename Vector::value_type;
auto *vtype_info = py::detail::get_type_info(typeid(vtype));
bool local = !vtype_info || vtype_info->module_local;
Class_ cl(scope, name.c_str(), pybind11::module_local(local), std::forward<Args>(args)...);
// Declare the buffer interface if a buffer_protocol() is passed in
py::detail::vector_buffer<Vector, Class_, Args...>(cl);
// Register copy constructor (if possible)
py::detail::vector_if_copy_constructible<Vector, Class_>(cl);
// Register comparison-related operators and functions (if possible)
using T = typename Vector::value_type;
cl.def(py::self == py::self);
cl.def(py::self != py::self);
cl.def(
"count",
[](const Vector &v, const T &x)
{ return std::count(v.begin(), v.end(), x); },
py::arg("x"),
"Return the number of times ``x`` appears in the list");
cl.def(
"__contains__",
[](const Vector &v, const T &x)
{ return std::find(v.begin(), v.end(), x) != v.end(); },
py::arg("x"),
"Return true the container contains ``x``");
// Register stream insertion operator (if possible)
py::detail::vector_if_insertion_operator<Vector, Class_>(cl, name);
// Modifiers require copyable vector value type
// detail::vector_modifiers<Vector, Class_>(cl);
// Accessor and iterator; return by value if copyable, otherwise we return by ref + keep-alive
py::detail::vector_accessor<Vector, Class_>(cl);
cl.def(
"__bool__",
[](const Vector &v) -> bool
{ return !v.empty(); },
"Check whether the list is nonempty");
cl.def("__len__", &Vector::size);
return cl;
}
PYBIND11_EMBEDDED_MODULE(cpp_module, m)
{
py::bind_vector<std::vector<int>>(m, "VectorInt");
bind_const_vector<wrapped_for_const<std::vector<int>>>(m, "ConstVectorInt");
py::class_<X>(m, "X")
.def(py::init<std::vector<int>>())
.def_property("v", [](X const& x) {
return wrapped_for_const<std::vector<int>>{&x.v()};
}, &X::set_v, py::return_value_policy::reference_internal);
}
int main()
{
std::vector<double> V{1, 2, 3};
py::scoped_interpreter guard{};
try {
py::exec(R"(
from cpp_module import VectorInt, X
x = X(VectorInt([1, 2, 3]))
print(x.v)
x.v = VectorInt([3, 5, 9])
print(x.v)
x.v[1] = 45
print(x.v)
)");
}
catch (py::error_already_set const& ex) {
std::cout << ex.what();
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment