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