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 <map>
21 #include "../traits.hpp"
22 #include "../pyref.hpp"
23 
24 namespace cpp2py {
25 
26   template <typename K, typename V> struct py_converter<std::map<K, V>> {
27 
c2pycpp2py::py_converter28     template <typename M> static PyObject *c2py(M &&m) {
29       static_assert(is_instantiation_of_v<std::map, std::decay_t<M>>, "Logic Error");
30 
31       PyObject *d = PyDict_New();
32       for (auto &[key, val] : m) {
33         pyref k, v;
34         if constexpr (std::is_reference_v<M>) {
35           k = convert_to_python(key);
36           v = convert_to_python(val);
37         } else { // Map passed as rvalue
38           k = convert_to_python(std::move(key));
39           v = convert_to_python(std::move(val));
40         }
41 
42         // if the K is a list, we transform into a tuple
43         if (PyList_Check(k)) k = PyList_AsTuple(k);
44 
45         if (k.is_null() or v.is_null() or (PyDict_SetItem(d, k, v) == -1)) {
46           Py_DECREF(d);
47           return NULL;
48         } // error
49       }
50       return d;
51     }
52 
53     // ----------------------------------------------
54 
is_convertiblecpp2py::py_converter55     static bool is_convertible(PyObject *ob, bool raise_exception) {
56       if (!PyDict_Check(ob)) goto _false;
57       {
58         pyref keys   = PyDict_Keys(ob);
59         pyref values = PyDict_Values(ob);
60         int len      = PyDict_Size(ob);
61         for (int i = 0; i < len; i++) {
62           if (!py_converter<K>::is_convertible(PyList_GET_ITEM((PyObject *)keys, i), raise_exception)) goto _false;   //borrowed ref
63           if (!py_converter<V>::is_convertible(PyList_GET_ITEM((PyObject *)values, i), raise_exception)) goto _false; //borrowed ref
64         }
65         return true;
66       }
67     _false:
68       if (raise_exception) { PyErr_SetString(PyExc_TypeError, ("Cannot convert "s + to_string(ob) + " to std::map"s).c_str()); }
69       return false;
70     }
71 
72     // ----------------------------------------------
73 
py2ccpp2py::py_converter74     static std::map<K, V> py2c(PyObject *ob) {
75       pyref keys   = PyDict_Keys(ob);
76       pyref values = PyDict_Values(ob);
77       std::map<K, V> res;
78       int len = PyDict_Size(ob);
79       for (int i = 0; i < len; i++)
80         res.emplace(py_converter<K>::py2c(PyList_GET_ITEM((PyObject *)keys, i)),    //borrowed ref
81                     py_converter<V>::py2c(PyList_GET_ITEM((PyObject *)values, i))); //borrowed ref
82       return res;
83     }
84   };
85 } // namespace cpp2py
86