-
Notifications
You must be signed in to change notification settings - Fork 5
/
pyvxl_util.h
152 lines (132 loc) · 3.76 KB
/
pyvxl_util.h
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
#ifndef pyvxl_util_h_included
#define pyvxl_util_h_included
#include <pybind11/pybind11.h>
#include <string>
#include <sstream>
#include <vsl/vsl_binary_io.h>
namespace pyvxl {
// simplify overload casting (C++11 version)
// https://pybind11.readthedocs.io/en/stable/classes.html#overloaded-methods
template <typename... Args>
using overload_cast_ = pybind11::detail::overload_cast_impl<Args...>;
// Return a string based on output of the object's stream operator
template<typename T>
std::string streamToString(T const& t){
std::ostringstream buffer;
buffer << t;
return buffer.str();
}
/* PICKLE VXL CORE INSTANCE with vsl_binary_io
*
* Any VXL core class with an "io" counterpart can be pickled via
* these helper functions.
*
* Pickle capabilites are added to an object via py::pickle.
* Note the appropriate "io" header must also be made available.
*
* -----
* #include "../pyvxl_util.h"
* #include <vpgl/io/vpgl_io_affine_camera.h>
*
* py::class_< vpgl_affine_camera<double> > (m, "affine_camera")
* .def(py::init ...)
* ...
* .def(py::pickle(&vslPickleGetState< vpgl_affine_camera<double> >,
* &vslPickleSetState< vpgl_affine_camera<double> >))
* ;
* -----
*
* Additionally, "target_link_libraries" in CMakeLists.txt must link
* to the appropriate "io" target
*
* -----
* target_link_libraries(pyvpgl PRIVATE vpgl vpgl_io ...)
* ^^^^^^^
* -----
*
* Assuming the availability of "operator==" for the pickled class,
* a python pickle unit test follows this pattern:
*
* -----
* def test_pickle(self):
* objA = self.init_obj()
* objB = pickle.loads(pickle.dumps(objA))
* self.assertEqual(objA, objB)
* -----
*/
template<typename T>
pybind11::bytes vslPickleGetState(T const& obj)
{
std::ostringstream oss;
vsl_b_ostream oss_vsl(&oss);
vsl_b_write(oss_vsl, obj);
return pybind11::bytes(oss.str());
}
template<typename T>
T vslPickleSetState(pybind11::bytes b)
{
T obj;
std::istringstream iss(b);
vsl_b_istream iss_vsl(&iss);
vsl_b_read(iss_vsl, obj);
return obj;
}
// Conversion from struct to dict
template<typename T>
pybind11::dict struct_to_dict(const T& obj)
{
pybind11::object pyobj = pybind11::cast(&obj);
pybind11::dict classDict = pyobj.attr("__class__").attr("__dict__");
pybind11::dict output;
for (auto item : classDict)
{
switch (PyObject_IsInstance(item.second.ptr(), (PyObject*)(&PyProperty_Type)))
{
case 1:
output[item.first] = pyobj.attr(item.first);
break;
case -1:
throw std::runtime_error("Could not determine the type of "
+ item.first.cast<std::string>());
break;
}
}
return output;
}
// __repr__ via struct_to_dict
template<typename T>
pybind11::str repr_by_dict(const T& obj)
{
return struct_to_dict(obj).attr("__repr__")();
}
// set object attributes from nested dictionary
void _set_attrs_from_dict(pybind11::object &obj, pybind11::dict dict)
{
for (auto item : dict) {
if (pybind11::isinstance<pybind11::dict>(item.second)) {
auto attr_obj = obj.attr(item.first).cast<pybind11::object>();
auto value_as_dict = item.second.cast<pybind11::dict>();
_set_attrs_from_dict(attr_obj, value_as_dict);
}
else {
obj.attr(item.first) = item.second;
}
}
}
// set struct values via keyword arguments
template<typename T>
void set_struct_from_kwargs(T &self, pybind11::kwargs kwargs)
{
pybind11::object pyself = pybind11::cast(&self);
_set_attrs_from_dict(pyself, kwargs);
}
// init struct via keyword arguments
template<typename T>
T init_struct_from_kwargs(pybind11::kwargs kwargs)
{
T self;
set_struct_from_kwargs<T>(self, kwargs);
return self;
}
} // end namespace pyvxl
#endif