File: packet.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 (154 lines) | stat: -rw-r--r-- 7,260 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

/**************************************************************************
 *                                                                        *
 *  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/>. *
 *                                                                        *
 **************************************************************************/

/*! \file python/helpers/packet.h
 *  \brief Assists with packets that wrap standalone C++ types.
 */

namespace regina {

class Packet;
template <class> class PacketData;
template <class> class PacketOf;

template <typename Held> std::shared_ptr<PacketOf<Held>> make_packet(Held&&);
template <typename Held> std::shared_ptr<PacketOf<Held>> make_packet(Held&&,
    const std::string&);

namespace python {

/**
 * Adds Python bindings for the class PacketOf<Held>, as well as corresponding
 * make_packet functions.
 *
 * The new packet class will be given a deep copy constructor, which takes a
 * single argument of type const Held&, and also acts as a copy constructor
 * for PacketOf<Held>.
 *
 * For all other Held constructors (except for the copy constructor), you will
 * need to add a corresponding "forwarding constructor" to this class by
 * calling add_packet_constructor() with the pybind11::class_ object that
 * this function returns.
 *
 * Aside from the constructors, the new packet class will (as a subclass
 * of Held) inherit the full interface from Held.
 *
 * Since all packet types are held by std::shared_ptr in their Python
 * bindings, you _must_ ensure that the base class Held is likewise held by
 * std::shared_ptr (not the default std::unique_ptr that pybind11 uses
 * unless instructed otherwise).  If you do not do this, then Python
 * will raise an ImportError when loading Regina's module.
 *
 * Note that, when creating the bindings for the \a Held type, you should
 * use packet_eq_operators() and not add_eq_operators().  See
 * python/helpers/equality.h for further details.
 */
template <class Held>
auto add_packet_wrapper(pybind11::module_& m, const char* className) {
    auto c = pybind11::class_<regina::PacketOf<Held>, Held, regina::Packet,
            std::shared_ptr<regina::PacketOf<Held>>>(m, className,
            doc::common::PacketOf)
        .def(pybind11::init<const Held&>(), // also takes PacketOf<Held>
            doc::common::PacketOf_copy)
        .def_readonly_static("typeID", &regina::PacketOf<Held>::typeID)
    ;
    regina::python::add_output(c);

    m.def("make_packet", [](const Held& h) {
        // The C++ make_packet expects an rvalue reference.
        return regina::make_packet(Held(h));
    }, doc::common::make_packet);
    m.def("make_packet", [](const Held& h, const std::string& label) {
        // The C++ make_packet expects an rvalue reference.
        return regina::make_packet(Held(h), label);
    }, doc::common::make_packet_2);

    // Be kind to users who expect regina-style capitalisation.
    m.def("makePacket", [](const Held& h) {
        return regina::make_packet(Held(h)); // rvalue ref, as above
    }, doc::common::make_packet);
    m.def("makePacket", [](const Held& h, const std::string& label) {
        return regina::make_packet(Held(h), label); // rvalue ref, as above
    }, doc::common::make_packet_2);

    return c;
}

/**
 * Adds a Python constructor for PacketOf<Held> that forwards its arguments
 * to a corresponding Held constructor.
 *
 * Specifically, if the type Held has a constructor that takes arguments
 * x, y, ..., z of types Tx, Ty, ..., Tz respectively, then this function will
 * add a corresponding constructor to the Python wrapper for PacketOf<Held>.
 *
 * At the Python level, this constructor looks like PacketOfHeld(x, y, .., z).
 * At the C++ level, it will call PacketOf<Held>(std::in_place, x, y, ..., z).
 *
 * To add the wrapper, call add_packet_constructor<Tx, Ty, ...  Tz>(c, ...),
 * where c is the pybind11::class_ object returned from add_packet_wrapper()
 * (that is, the pybind11 wrapper for the C++ class PacketOf<Held>).  Any
 * additional arguments (e.g., a docstring) will be passed through to
 * class_.def().
 *
 * The additional \a options arguments are the usual pybind11 options
 * (for example, pybind11::arg objects to specify default arguments).
 * The types PythonClass and Options... are deduced automatically (as
 * opposed to the constructor argument types Args..., which must be
 * explicitly specified as part of the template function call).
 */
template <typename... Args, typename PythonClass, typename... Options>
void add_packet_constructor(PythonClass& classWrapper, Options&&... options) {
    classWrapper.def(pybind11::init([](Args... args) {
        using WrappedType = typename PythonClass::type;
        return new WrappedType(std::in_place, std::forward<Args>(args)...);
    }), std::forward<Options>(options)...);
}

/**
 * Adds wrappers for the member functions for a C++ type Held that are
 * inherited from PacketData<Held>.
 *
 * The argument \a classWrapper should be the pybind11::class_ object
 * that wraps the C++ class Held.
 */
template <typename PythonClass>
void add_packet_data(PythonClass& classWrapper) {
    using DataType = regina::PacketData<typename PythonClass::type>;
    classWrapper
        .def("packet", pybind11::overload_cast<>(&DataType::packet),
            doc::common::PacketData_packet)
        .def("anonID", &DataType::anonID, doc::common::PacketData_anonID)
        ;
}

} // namespace python
} // namespace regina