Last active
May 15, 2022 13:20
-
-
Save Holt59/cbcc4e98171490d6898f29b78eee8997 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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