File: facehelper.h

package info (click to toggle)
regina-normal 7.4.1-1.1
  • links: PTS
  • area: main
  • in suites: forky, sid
  • size: 154,244 kB
  • sloc: cpp: 295,026; xml: 9,992; sh: 1,344; python: 1,225; perl: 616; ansic: 138; makefile: 26
file content (259 lines) | stat: -rw-r--r-- 9,964 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259

/**************************************************************************
 *                                                                        *
 *  Regina - A Normal Surface Theory Calculator                           *
 *  Python Interface                                                      *
 *                                                                        *
 *  Copyright (c) 1999-2025, Ben Burton                                   *
 *  For further details contact Ben Burton (bab@debian.org).              *
 *                                                                        *
 *  This program is free software; you can redistribute it and/or         *
 *  modify it under the terms of the GNU General Public License as        *
 *  published by the Free Software Foundation; either version 2 of the    *
 *  License, or (at your option) any later version.                       *
 *                                                                        *
 *  As an exception, when this program is distributed through (i) the     *
 *  App Store by Apple Inc.; (ii) the Mac App Store by Apple Inc.; or     *
 *  (iii) Google Play by Google Inc., then that store may impose any      *
 *  digital rights management, device limits and/or redistribution        *
 *  restrictions that are required by its terms of service.               *
 *                                                                        *
 *  This program is distributed in the hope that it will be useful, but   *
 *  WITHOUT ANY WARRANTY; without even the implied warranty of            *
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
 *  General Public License for more details.                              *
 *                                                                        *
 *  You should have received a copy of the GNU General Public License     *
 *  along with this program. If not, see <https://www.gnu.org/licenses/>. *
 *                                                                        *
 **************************************************************************/

#ifndef __REGINA_PYTHON_FACEHELPER_H
#define __REGINA_PYTHON_FACEHELPER_H

#include <pybind11/pybind11.h>
#include "triangulation/generic.h"
#include "../helpers.h"

// On some systems we get warnings about regina's helper classes having
// greater visibility than the pybind11 code that it uses.  We can fix
// this by setting the same visibility attributes that pybind11 uses.
#ifdef __GNUG__
    #define MATCH_PYBIND11_VISIBILITY __attribute__((visibility("hidden")))
#else
    #define MATCH_PYBIND11_VISIBILITY
#endif

namespace regina::python {

/**
 * The type of the function pointer T::countFaces(subdim).
 */
template <typename T>
using countFacesFunc = size_t (T::*)(int) const;

/**
 * The type of the function pointer T::face(subdim, index).
 */
template <typename T>
using faceFunc = decltype(T().face(0, 0)) (T::*)(int, size_t) const;

/**
 * The type of the function pointer T::faces(subdim).
 */
template <typename T>
using facesFunc = decltype(T().faces(0)) (T::*)(int) const;

/**
 * Implementation details for Python bindings of template member functions.
 *
 * Python does not support templates, and so we bind C++ template member
 * functions (such as Component::countFaces<subdim>() or
 * Simplex::face<subdim>()) by converting the C++ template argument \a subdim
 * into the the first argument of the Python function (i.e., the function in
 * Python has one more argument than in C++).
 *
 * This helper class is designed to work with "auxiliary" types \a T such as
 * Component and BoundaryComponent, not the "primary" type Triangulation<dim>.
 * This is due to the limitations surrounding lifespan management (see below
 * for details).
 *
 * Note that some of these C++ functions return different types depending on
 * the argument \a subdim; we resolve this by converting return values
 * to python objects here, instead of letting pybind11 do it later.
 * The cost of returning a pybind11::object is that we circumvent pybind11's
 * normal casting mechanism, and so we do not get the lifespan relationships
 * that we would normally get from return_value_policy::reference_internal
 * (as we do get, for instance, through fixed-subdimension routines such
 * as vertex() or vertices()).  Instead all objects that are returned
 * will be treated with a policy of pybind11::return_value_policy::reference.
 *
 * Note: when given a pointer, pybind11::cast() and pybind11::list::append()
 * both default to a return value policy of reference, not take_ownership.
 */
template <class T, int dim, int subdim>
struct FaceHelper {
    using Face = regina::Face<dim, subdim>;

    static size_t countFacesFrom(const T& t, int subdimArg) {
        if (subdimArg == subdim)
            return t.template countFaces<subdim>();
        return FaceHelper<T, dim, subdim - 1>::countFacesFrom(t, subdimArg);
    }

    template <typename Index>
    static pybind11::object faceFrom(const T& t, int subdimArg, Index f) {
        if (subdimArg == subdim)
            return pybind11::cast(t.template face<subdim>(f));
        else
            return FaceHelper<T, dim, subdim - 1>::
                template faceFrom<Index>(t, subdimArg, f);
    }

    static pybind11::list facesFrom(const T& t, int subdimArg) {
        if (subdimArg == subdim) {
            pybind11::list ans;
            for (auto f : t.template faces<subdim>())
                ans.append(pybind11::cast(f));
            return ans;
        } else
            return FaceHelper<T, dim, subdim - 1>::facesFrom(t, subdimArg);
    }

    template <int permSize>
    static Perm<permSize> faceMappingFrom(const T& t, int subdimArg, int f) {
        if (subdimArg == subdim)
            return t.template faceMapping<subdim>(f);
        return FaceHelper<T, dim, subdim - 1>::
            template faceMappingFrom<permSize>(t, subdimArg, f);
    }
};

/**
 * Implementation details for Python bindings of template member functions.
 *
 * See the notes above.
 */
template <class T, int dim>
struct FaceHelper<T, dim, 0> {
    using Face = regina::Face<dim, 0>;

    static size_t countFacesFrom(const T& t, int) {
        return t.template countFaces<0>();
    }

    template <typename Index>
    static pybind11::object faceFrom(const T& t, int, Index f) {
        return pybind11::cast(t.template face<0>(f));
    }

    static pybind11::list facesFrom(const T& t, int) {
        pybind11::list ans;
        for (auto f : t.template faces<0>())
            ans.append(pybind11::cast(f));
        return ans;
    }

    template <int permSize>
    static Perm<permSize> faceMappingFrom(const T& t, int, int f) {
        return t.template faceMapping<0>(f);
    }
};

/**
 * Implementation details for Python bindings of template member functions.
 *
 * See the notes above.
 *
 * The compiler needs to instantiate this class, but none of its methods
 * should ever be called.
 */
template <class T, int dim>
struct FaceHelper<T, dim, -1> {
    static size_t countFacesFrom(const T&, int) {
        throw -1;
    }

    template <typename Index>
    static pybind11::object faceFrom(const T&, int, Index) {
        throw -1;
    }

    static pybind11::list facesFrom(const T&, int) {
        throw -1;
    }

    template <int permSize>
    static Perm<permSize> faceMappingFrom(const T&, int, int) {
        throw -1;
    }
};

/**
 * Throws an exception.  The error message will state that the argument
 * for the face dimension (which should be the first argument of the original
 * function, corresponding to the C++ template argument) must be in the
 * range \a minDim, ..., \a maxDim.
 */
void invalidFaceDimension(const char* functionName, int minDim, int maxDim);

/**
 * The Python binding for the C++ template member function
 * T::countFaces<subdimArg>(), where the valid range for the C++ template
 * parameter \a subdimArg is 0, ..., \a maxSubdim.
 */
template <class T, int dim, int maxSubdim>
size_t countFaces(const T& t, int subdimArg) {
    if (subdimArg < 0 || subdimArg > maxSubdim)
        invalidFaceDimension("countFaces", 0, maxSubdim);
    return FaceHelper<T, dim, maxSubdim>::countFacesFrom(t, subdimArg);
}

/**
 * The Python binding for the C++ template member function
 * T::face<subdimArg>(f), where the valid range for the C++ template
 * parameter \a subdimArg is 0, ..., <i>dim</i>-1.
 *
 * The return value policy will be treated as
 * pybind11::return_value_policy::reference.
 */
template <class T, int dim, typename Index>
pybind11::object face(const T& t, int subdimArg, Index f) {
    if (subdimArg < 0 || subdimArg >= dim)
        invalidFaceDimension("face", 0, dim - 1);
    return FaceHelper<T, dim, dim - 1>::template faceFrom<Index>(
        t, subdimArg, f);
}

/**
 * The Python binding for the C++ template member function
 * T::faces<subdimArg>(), where the valid range for the C++ template
 * parameter \a subdimArg is 0, ..., <i>dim</i>-1.
 *
 * The return value policy will be treated as
 * pybind11::return_value_policy::reference.
 */
template <class T, int dim>
pybind11::object faces(const T& t, int subdimArg) {
    if (subdimArg < 0 || subdimArg >= dim)
        invalidFaceDimension("faces", 0, dim - 1);
    return FaceHelper<T, dim, dim - 1>::facesFrom(t, subdimArg);
}

/**
 * The Python binding for the C++ template member function
 * T::faceMapping<subdimArg>(f), where the valid range for the C++ template
 * parameter \a subdimArg is 0, ..., <i>dim</i>-1, and where the function
 * returns a permutation on permSize elements.
 */
template <class T, int dim, int permSize = dim + 1>
Perm<permSize> faceMapping(const T& t, int subdimArg, int f) {
    if (subdimArg < 0 || subdimArg >= dim)
        invalidFaceDimension("faceMapping", 0, dim - 1);
    return FaceHelper<T, dim, dim - 1>::template faceMappingFrom<permSize>(
        t, subdimArg, f);
}

} // namespace regina::python

#endif