1 // Copyright (c) 2017 Commissariat à l'énergie atomique et aux énergies alternatives (CEA)
2 // Copyright (c) 2017 Centre national de la recherche scientifique (CNRS)
3 // Copyright (c) 2020 Simons Foundation
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 //     http://www.apache.org/licenses/LICENSE-2.0.txt
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 //
17 // Authors: Olivier Parcollet, Nils Wentzell
18 
19 #pragma once
20 #include <tuple>
21 #include <variant>
22 
23 #include "../traits.hpp"
24 #include "../pyref.hpp"
25 
26 namespace cpp2py {
27 
28   // std::variant<T...> converter
29   // converts in the first possible type
30   template <typename... T> struct py_converter<std::variant<T...>> {
31 
32     private:
33     // using variant_t                = std::variant<T...>;
34     template <int N> using types_t = std::tuple_element_t<N, std::tuple<T...>>;
35     //constexpr static int n_types   = sizeof...(T);
36 
37     // c2py_visitor
38     //struct c2py_visitor {
39     //template <typename T> PyObject *operator()(T const &x) { return py_converter<T>::c2py(x); }
40     //};
41 
42     // is_convertible_impl
43     /*  template <int N> static std::enable_if_t<(N < n_types), bool> is_convertible_impl(PyObject *ob) {*/
44     //return py_converter<types_t<N>>::is_convertible(ob, false) || is_convertible_impl<N + 1>(ob);
45     //}
46     //template <int N> static std::enable_if_t<N == n_types, bool> is_convertible_impl(PyObject *ob) { return false; }
47 
48     // py2c_impl
49     //template <int N> static std::enable_if_t<(N < n_types), variant_t> py2c_impl(PyObject *ob) {
50     //if (py_converter<types_t<N>>::is_convertible(ob, false))
51     //return py_converter<types_t<N>>::py2c(ob);
52     //else
53     //return py2c_impl<N + 1>(ob);
54     //}
55     //template <int N> static std::enable_if_t<N == n_types, variant_t> py2c_impl(PyObject *ob) {
56     //CPP2PY_RUNTIME_ERROR << "Internal error: py2c called for a Python object incompatible with std::variant";
57     //}
58 
py2c_implcpp2py::py_converter59     template <int N> static std::variant<T...> py2c_impl(PyObject *ob) {
60       using conv = py_converter<std::tuple_element_t<N, std::tuple<T...>>>;
61       if (conv::is_convertible(ob, false)) return conv::py2c(ob);
62       if constexpr (N < sizeof...(T) - 1)
63         return py2c_impl<N + 1>(ob);
64       else
65         CPP2PY_RUNTIME_ERROR << "Internal error: py2c called for a Python object incompatible with std::variant";
66     }
67 
68     struct _visitor {
operator ()cpp2py::py_converter::_visitor69       template <typename U> PyObject *operator()(U &&x) { return convert_to_python(std::forward<U>(x)); }
70     };
71 
72     public:
c2pycpp2py::py_converter73     template <typename V> static PyObject *c2py(V &&v) {
74       static_assert(is_instantiation_of_v<std::variant, std::decay_t<V>>);
75       return visit(_visitor{}, std::forward<V>(v));
76     }
77 
is_convertiblecpp2py::py_converter78     static bool is_convertible(PyObject *ob, bool raise_exception) {
79       if ((... or py_converter<std::decay_t<T>>::is_convertible(ob, false))) return true;
80       if (raise_exception) { PyErr_SetString(PyExc_TypeError, ("Cannot convert "s + to_string(ob) + " to std::variant"s).c_str()); }
81       return false;
82     }
83 
py2ccpp2py::py_converter84     static std::variant<T...> py2c(PyObject *ob) { return py2c_impl<0>(ob); }
85   };
86 
87 } // namespace cpp2py
88