Skip to content

Commit b0556a5

Browse files
committed
Add a bunch of explanatory comments
More documentation
1 parent e4e8ee0 commit b0556a5

File tree

2 files changed

+132
-67
lines changed

2 files changed

+132
-67
lines changed

include/util_caching/python_bindings.hpp

Lines changed: 104 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -12,98 +12,139 @@ namespace py = pybind11;
1212

1313
namespace number_based {
1414
namespace internal {
15+
16+
/*!
17+
* \brief Bind the comparison policies to the Cache class
18+
*
19+
* This function binds the comparison policies to the Cache class. The policies
20+
* are passed as variadic template arguments. The function overloads the
21+
* `cached` function for each policy.
22+
*/
1523
template <typename CacheT, typename NumberT, typename... ComparisonPolicyTs>
16-
void bindPolicies(py::class_<CacheT, std::shared_ptr<CacheT>>& cacheClass) {
17-
(cacheClass.def(
18-
"cached",
19-
[](CacheT& self, const NumberT& key, const ComparisonPolicyTs& policy) {
20-
return self.template cached<ComparisonPolicyTs>(key, policy);
21-
},
22-
py::arg("key"),
23-
py::arg("policy")),
24-
...);
24+
void bindPolicies(py::class_<CacheT, std::shared_ptr<CacheT>> &cacheClass) {
25+
(cacheClass.def(
26+
"cached",
27+
[](CacheT &self, const NumberT &key, const ComparisonPolicyTs &policy) {
28+
return self.template cached<ComparisonPolicyTs>(key, policy);
29+
},
30+
py::arg("key"), py::arg("policy")),
31+
...);
2532
}
2633
} // namespace internal
2734

35+
/*!
36+
* \brief Bind the ApproximateNumber policy
37+
*
38+
* This function adds bindings for the ApproximateNumber policy to the given
39+
* python module under the given name.
40+
*/
2841
template <typename NumberT>
29-
void bindApproximatePolicy(py::module& module, const std::string& name = "ApproximateNumber") {
30-
using ApproximateNumberT = policies::ApproximateNumber<NumberT>;
31-
py::class_<ApproximateNumberT, std::shared_ptr<ApproximateNumberT>>(module, name.c_str())
32-
.def(py::init<NumberT>(), py::arg("threshold"))
33-
.def("__call__", &ApproximateNumberT::operator(), "Compare two numbers");
42+
void bindApproximatePolicy(py::module &module,
43+
const std::string &name = "ApproximateNumber") {
44+
using ApproximateNumberT = policies::ApproximateNumber<NumberT>;
45+
py::class_<ApproximateNumberT, std::shared_ptr<ApproximateNumberT>>(
46+
module, name.c_str())
47+
.def(py::init<NumberT>(), py::arg("threshold"))
48+
.def("__call__", &ApproximateNumberT::operator(), "Compare two numbers");
3449
}
3550

3651
/*!
3752
* \brief Bindings for a Cache that is based on number comparisons
3853
*
39-
* This function binds the Cache class for a specific number-based key type (NumberT) and value type (ValueT).
40-
* Call this function once inside PYBIND11_MODULE macro to create a Python module with the bindings.
54+
* This function binds the Cache class for a specific number-based key type
55+
* (NumberT) and value type (ValueT). Optionally, add a list of comparison
56+
* policies to the list of template parameters. The `cached` function will be
57+
* overloaded for each one of them. Call this function once inside
58+
* PYBIND11_MODULE macro to create the bindings for the Cache class.
4159
*/
4260
template <typename NumberT, typename ValueT, typename... ComparisonPolicies>
43-
void bindCache(py::module& module) {
44-
using CacheT = Cache<NumberT, ValueT>;
45-
py::class_<CacheT, std::shared_ptr<CacheT>> cache(module, "Cache");
46-
cache
47-
.def(py::init<>())
48-
// We cannot pass template parameters to python functions, therefore we need to explicitly bind all
49-
// instantiations to different python functions.
50-
// We need to use the lambdas here to handle the seconds argument, defining the comparison policy.
51-
.def(
52-
"cached",
53-
[](CacheT& self, const NumberT& key) { return self.template cached<std::equal_to<NumberT>>(key); },
54-
py::arg("key"))
55-
.def("cache", &CacheT::cache, py::arg("key"), py::arg("value"))
56-
.def("reset", &CacheT::reset);
57-
58-
internal::bindPolicies<CacheT, NumberT, ComparisonPolicies...>(cache);
61+
void bindCache(py::module &module) {
62+
using CacheT = Cache<NumberT, ValueT>;
63+
py::class_<CacheT, std::shared_ptr<CacheT>> cache(module, "Cache");
64+
cache
65+
.def(py::init<>())
66+
// We cannot pass template parameters to python functions, therefore we
67+
// need to explicitly bind all instantiations to different python
68+
// functions. We need to use the lambdas here to handle the seconds
69+
// argument, defining the comparison policy.
70+
.def(
71+
"cached",
72+
[](CacheT &self, const NumberT &key) {
73+
return self.template cached<std::equal_to<NumberT>>(key);
74+
},
75+
py::arg("key"))
76+
.def("cache", &CacheT::cache, py::arg("key"), py::arg("value"))
77+
.def("reset", &CacheT::reset);
78+
79+
internal::bindPolicies<CacheT, NumberT, ComparisonPolicies...>(cache);
5980
}
6081

6182
} // namespace number_based
6283

6384
namespace time_based {
64-
6585
namespace internal {
86+
87+
/*!
88+
* \brief Bind the comparison policies to the Cache class
89+
*
90+
* This function binds the comparison policies to the Cache class. The policies
91+
* are passed as variadic template arguments. The function overloads the
92+
* `cached` function for each policy.
93+
*/
6694
template <typename CacheT, typename TimeT, typename... ComparisonPolicyTs>
67-
void bindPolicies(py::class_<CacheT, std::shared_ptr<CacheT>>& cache) {
68-
(cache.def(
69-
"cached",
70-
[](CacheT& self, const TimeT& key, const ComparisonPolicyTs& policy) {
71-
return self.template cached<ComparisonPolicyTs>(key, policy);
72-
},
73-
py::arg("key"),
74-
py::arg("policy")),
75-
...);
95+
void bindPolicies(py::class_<CacheT, std::shared_ptr<CacheT>> &cache) {
96+
(cache.def(
97+
"cached",
98+
[](CacheT &self, const TimeT &key, const ComparisonPolicyTs &policy) {
99+
return self.template cached<ComparisonPolicyTs>(key, policy);
100+
},
101+
py::arg("key"), py::arg("policy")),
102+
...);
76103
}
77104
} // namespace internal
78105

106+
/*!
107+
* \brief Bind the ApproximateTime policy
108+
*
109+
* This function adds bindings for the ApproximateTime policy to the given
110+
* python module under the given name.
111+
*/
79112
template <typename TimeT, typename ThresholdTimeUnitT>
80-
void bindApproximatePolicy(py::module& module, const std::string& name = "ApproximateTime") {
81-
using ApproximateTimeT = policies::ApproximateTime<TimeT, ThresholdTimeUnitT>;
82-
py::class_<ApproximateTimeT, std::shared_ptr<ApproximateTimeT>>(module, name.c_str())
83-
.def(py::init<double>(), py::arg("threshold"))
84-
.def("__call__", &ApproximateTimeT::operator(), "Compare two time points");
113+
void bindApproximatePolicy(py::module &module,
114+
const std::string &name = "ApproximateTime") {
115+
using ApproximateTimeT = policies::ApproximateTime<TimeT, ThresholdTimeUnitT>;
116+
py::class_<ApproximateTimeT, std::shared_ptr<ApproximateTimeT>>(module,
117+
name.c_str())
118+
.def(py::init<double>(), py::arg("threshold"))
119+
.def("__call__", &ApproximateTimeT::operator(),
120+
"Compare two time points");
85121
}
86122

87123
/*!
88-
* \brief Bindings for a Cache that is based on time comparisons
124+
* \brief Bindings for a Cache that is based on time comparisons.
89125
*
90-
* This function binds the Cache class for a specific time-based key type (TimeT) and value type (ValueT).
91-
* Call this function once inside PYBIND11_MODULE macro to create a Python module with the bindings.
126+
* This function binds the Cache class for a specific time-based key type
127+
* (TimeT) and value type (ValueT). Optionally, add a list of comparison
128+
* policies to the list of template parameters. The `cached` function will be
129+
* overloaded for each one of them. Call this function once inside
130+
* PYBIND11_MODULE macro to create the bindings for the Cache class.
92131
*/
93132
template <typename TimeT, typename ValueT, typename... ComparisonPolicyTs>
94-
void bindCache(py::module& module) {
95-
using CacheT = Cache<TimeT, ValueT>;
96-
97-
py::class_<CacheT, std::shared_ptr<CacheT>> cache(module, "Cache");
98-
cache.def(py::init<>())
99-
.def(
100-
"cached",
101-
[](CacheT& self, const TimeT& key) { return self.template cached<std::equal_to<TimeT>>(key); },
102-
py::arg("key"))
103-
.def("cache", &CacheT::cache, py::arg("key"), py::arg("value"))
104-
.def("reset", &CacheT::reset);
105-
106-
internal::bindPolicies<CacheT, TimeT, ComparisonPolicyTs...>(cache);
133+
void bindCache(py::module &module) {
134+
using CacheT = Cache<TimeT, ValueT>;
135+
136+
py::class_<CacheT, std::shared_ptr<CacheT>> cache(module, "Cache");
137+
cache.def(py::init<>())
138+
.def(
139+
"cached",
140+
[](CacheT &self, const TimeT &key) {
141+
return self.template cached<std::equal_to<TimeT>>(key);
142+
},
143+
py::arg("key"))
144+
.def("cache", &CacheT::cache, py::arg("key"), py::arg("value"))
145+
.def("reset", &CacheT::reset);
146+
147+
internal::bindPolicies<CacheT, TimeT, ComparisonPolicyTs...>(cache);
107148
}
108149

109150
} // namespace time_based

test/python_bindings.cpp

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,29 +8,53 @@ namespace py = pybind11;
88

99
using namespace util_caching;
1010

11+
/*!
12+
* \brief A policy that always returns true
13+
*
14+
* Custom policies have to be defined in C++ and then bound to Python.
15+
* To overload the `cache` function, the policy has to be passed as a template parameter to the `bindCache` function.
16+
*/
1117
struct SomePolicyWithoutParams {
1218
SomePolicyWithoutParams() = default;
1319
bool operator()(const Time& /*lhs*/, const Time& /*rhs*/) const {
1420
return true;
1521
}
1622
};
1723

24+
/*!
25+
* \brief The python module definition that allows running equivalent unit tests in python.
26+
*/
1827
PYBIND11_MODULE(util_caching_py, mainModule) {
28+
// Just some aliases to make the code more readable
1929
using ApproximateNumberT = policies::ApproximateNumber<double>;
2030
using ApproximateTimeT = policies::ApproximateTime<Time, std::chrono::milliseconds>;
2131
using ApproximateTimeSecondsT = policies::ApproximateTime<Time, std::chrono::seconds>;
2232

33+
// Since we want to use this policy in python, we need to be able to instatiate it there
2334
py::class_<SomePolicyWithoutParams, std::shared_ptr<SomePolicyWithoutParams>>(mainModule, "SomePolicyWithoutParams")
2435
.def(py::init<>())
2536
.def("__call__", &SomePolicyWithoutParams::operator());
2637

38+
// Adding a submodule is optional but a good way to structure the bindings
2739
py::module numberBased = mainModule.def_submodule("number_based");
40+
// If we want to use a policy, we need to bind it. For the builtin policies, we can use this convenience function.
2841
python_api::number_based::bindApproximatePolicy<double>(numberBased);
29-
python_api::number_based::bindCache<double, double, ApproximateNumberT>(numberBased);
30-
42+
// The core binding, the cache class itself.
43+
python_api::number_based::bindCache<double, // KeyType
44+
double, // Value type
45+
ApproximateNumberT // Optionally, a list of comparison policies, each one will
46+
// overload the `cached` function
47+
>(numberBased);
48+
49+
// Same as above, but for the time-based cache
3150
py::module timeBased = mainModule.def_submodule("time_based");
51+
// We can bind the builtin comparison policy for different time units but then we have to name them differently
3252
python_api::time_based::bindApproximatePolicy<Time, std::chrono::milliseconds>(timeBased, "ApproximateTime");
3353
python_api::time_based::bindApproximatePolicy<Time, std::chrono::seconds>(timeBased, "ApproximateTimeSeconds");
34-
python_api::time_based::bindCache<Time, double, ApproximateTimeT, ApproximateTimeSecondsT, SomePolicyWithoutParams>(
35-
timeBased);
54+
// The core binding, the cache class itself.
55+
python_api::time_based::bindCache<Time, // Key type
56+
double, // Value type
57+
ApproximateTimeT, // A list of all comparison policies we intend to use
58+
ApproximateTimeSecondsT,
59+
SomePolicyWithoutParams>(timeBased);
3660
}

0 commit comments

Comments
 (0)