ThePhD / sol2

Sol3 (sol2 v3.0) - a C++ <-> Lua API wrapper with advanced features and top notch performance - is here, and it's great! Documentation:
http://sol2.rtfd.io/
MIT License
4.12k stars 500 forks source link

container overriding not working for container with void iterator #1450

Open g-braeunlich opened 1 year ago

g-braeunlich commented 1 year ago

Test case close to usecase (Also includes a Dockerfile): demo.tar.gz

Minimal test case: demo-minimal.tar.gz

Explanation

I am working on a project, where I have to pass an instance of an Eigen::Matrix to lua via sol::table::set.

Problem: Eigen::Matrix<T, Rows, Cols>::iterator is void if Cols != 1 && Rows != 1, begin(), end() are defined. Thus it is detected as a container by sol. My attempt to solve this issue was: container overriding. However, here I still get compile errors:

/usr/local/include/sol/usertype_container.hpp:502:48: error: forming reference to void
  502 |                         typedef decltype(*std::declval<iterator&>()) iterator_return;
      |                                           ~~~~~^~~~~~~~~~~~~~~~~~

...

/usr/local/include/sol/usertype_container.hpp:517:42: error: invalid use of 'sol::container_detail::usertype_container_default<MockEigen::Matrix<double, 2, 2>, void>::iterator' {aka 'void'}
/usr/local/include/sol/usertype_container.hpp:520:33: error: invalid parameter type 'sol::container_detail::usertype_container_default<MockEigen::Matrix<double, 2, 2>, void>::iterator' {aka 'void'}
  520 |                                 iter(lua_State* L_, int stack_index, T& source_, iterator it_) : keep_alive(sol::main_thread(L_, L_), stack_index), source(source_), it(std::move(it_)), index(0) {
      |                                 ^~~~

...

Even though sol/usertype_container.hpp tries to first use overrides from the custom usertype_container, it still has to fallback to usertype_container_default<X,...> which leads to invalid occurrences of void&.

g-braeunlich commented 1 year ago

Using the following patch (updated), I was able to compile the demo project:

diff --git a/sol2-3.3.0/include/sol/usertype_container.hpp b/sol2-3.3.0/include/sol/usertype_container.hpp
index 198ec89..49ae9fa 100644
--- a/sol2-3.3.0/include/sol/usertype_container.hpp
+++ b/sol2-3.3.0/include/sol/usertype_container.hpp
@@ -33,6 +33,11 @@ namespace sol {
    template <typename T>
    struct usertype_container;

+   template <typename T>
+   struct usertype_container_iterator {
+       using iterator = typename T::iterator;
+   };
+
    namespace container_detail {

        template <typename T>
@@ -486,12 +492,13 @@ namespace sol {
            using T = std::remove_pointer_t<meta::unwrap_unqualified_t<container_decay_t<X>>>;

        private:
-           using deferred_uc = usertype_container<X>;
+           using deferred_uc = usertype_container<container_decay_t<X>>;
            using is_associative = meta::is_associative<T>;
            using is_lookup = meta::is_lookup<T>;
            using is_ordered = meta::is_ordered<T>;
            using is_matched_lookup = meta::is_matched_lookup<T>;
            using iterator = typename T::iterator;
+           using uc_iterator = typename usertype_container_iterator<container_decay_t<X>>::iterator;
            using value_type = typename T::value_type;
            typedef meta::conditional_t<is_matched_lookup::value, std::pair<value_type, value_type>,
                 meta::conditional_t<is_associative::value || is_lookup::value, value_type, std::pair<std::ptrdiff_t, value_type>>>
@@ -499,7 +507,7 @@ namespace sol {
            typedef typename KV::first_type K;
            typedef typename KV::second_type V;
            typedef meta::conditional_t<is_matched_lookup::value, std::ptrdiff_t, K> next_K;
-           typedef decltype(*std::declval<iterator&>()) iterator_return;
+           typedef decltype(*std::declval<uc_iterator&>()) iterator_return;
            typedef meta::conditional_t<is_associative::value || is_matched_lookup::value, std::add_lvalue_reference_t<V>,
                 meta::conditional_t<is_lookup::value, V, iterator_return>>
                 captured_type;
@@ -514,10 +522,11 @@ namespace sol {
            struct iter {
                reference keep_alive;
                T& source;
-               iterator it;
+               uc_iterator it;
                std::size_t index;

-               iter(lua_State* L_, int stack_index, T& source_, iterator it_) : keep_alive(sol::main_thread(L_, L_), stack_index), source(source_), it(std::move(it_)), index(0) {
+               iter(lua_State* L_, int stack_index, T& source_, uc_iterator it_)
+               : keep_alive(sol::main_thread(L_, L_), stack_index), source(source_), it(std::move(it_)), index(0) {
                }

                ~iter() {
@@ -641,23 +650,23 @@ namespace sol {
                return get_comparative(meta::supports_op_equal<K, key_type>(), L_, self, key);
            }

-           static detail::error_result set_associative(std::true_type, iterator& it, stack_object value) {
+           static detail::error_result set_associative(std::true_type, uc_iterator& it, stack_object value) {
                decltype(auto) v = *it;
                v.second = value.as<V>();
                return {};
            }

-           static detail::error_result set_associative(std::false_type, iterator& it, stack_object value) {
+           static detail::error_result set_associative(std::false_type, uc_iterator& it, stack_object value) {
                decltype(auto) v = *it;
                v = value.as<V>();
                return {};
            }

-           static detail::error_result set_writable(std::true_type, lua_State*, T&, iterator& it, stack_object value) {
+           static detail::error_result set_writable(std::true_type, lua_State*, T&, uc_iterator& it, stack_object value) {
                return set_associative(is_associative(), it, std::move(value));
            }

-           static detail::error_result set_writable(std::false_type, lua_State*, T&, iterator&, stack_object) {
+           static detail::error_result set_writable(std::false_type, lua_State*, T&, uc_iterator&, stack_object) {
                return detail::error_result(
                     "cannot perform a 'set': '%s's iterator reference is not writable (non-copy-assignable or const)", detail::demangle<T>().data());
            }

However, I dont know if this breaks other parts

g-braeunlich commented 1 year ago

Update: