Skip to content

Commit d8d93f6

Browse files
committed
refactor(python/containers): refactor containers
feat(concepts): add TransformFunction concept test(concepts): add related tests
1 parent 748507a commit d8d93f6

File tree

5 files changed

+256
-106
lines changed

5 files changed

+256
-106
lines changed

binding/containers_binding.cpp

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
namespace py = pybind11;
2222
using namespace cpp_features::containers;
2323
using cpp_features::concepts::CopyableType;
24+
using cpp_features::concepts::TransformFunction;
2425

2526
namespace {
2627

@@ -32,6 +33,12 @@ auto GetItem(const Container<T> &self, typename Container<T>::size_type index) -
3233
throw py::index_error("Index out of bounds");
3334
}
3435

36+
template <typename Input, typename Output>
37+
auto GetTransformWrapper(const Container<Input> &self,
38+
const std::function<Output(Input)> &transform) {
39+
return self.template GetTransformedView<Output>(transform);
40+
}
41+
3542
template <CopyableType T>
3643
auto GetIter(const Container<T> &self) {
3744
return py::make_iterator(self.begin(), self.end());
@@ -69,7 +76,9 @@ void BindContainers(py::module &m) {
6976
.def("at", &GetItem<int>)
7077
.def("view", &IntContainer::GetView)
7178
.def("filter", &IntContainer::GetFilteredView<std::function<bool(int)>>)
72-
.def("transform", &IntContainer::GetTransformedView<std::function<int(int)>>)
79+
.def("transform", &GetTransformWrapper<int, int>)
80+
.def("transform", &GetTransformWrapper<int, double>)
81+
.def("transform", &GetTransformWrapper<int, std::string>)
7382
.def("__len__", &IntContainer::GetSize)
7483
.def("__bool__", [](const IntContainer &self) { return !self.IsEmpty(); })
7584
.def("__getitem__", &GetItem<int>)
@@ -90,7 +99,9 @@ void BindContainers(py::module &m) {
9099
.def("at", &GetItem<double>)
91100
.def("view", &FloatContainer::GetView)
92101
.def("filter", &FloatContainer::GetFilteredView<std::function<bool(double)>>)
93-
.def("transform", &FloatContainer::GetTransformedView<std::function<double(double)>>)
102+
.def("transform", &GetTransformWrapper<double, int>)
103+
.def("transform", &GetTransformWrapper<double, double>)
104+
.def("transform", &GetTransformWrapper<double, std::string>)
94105
.def("__len__", &FloatContainer::GetSize)
95106
.def("__bool__", [](const FloatContainer &self) { return !self.IsEmpty(); })
96107
.def("__getitem__", &GetItem<double>)
@@ -111,8 +122,9 @@ void BindContainers(py::module &m) {
111122
.def("at", &GetItem<std::string>)
112123
.def("view", &StringContainer::GetView)
113124
.def("filter", &StringContainer::GetFilteredView<std::function<bool(const std::string &)>>)
114-
.def("transform",
115-
&StringContainer::GetTransformedView<std::function<std::string(const std::string &)>>)
125+
.def("transform", &GetTransformWrapper<std::string, int>)
126+
.def("transform", &GetTransformWrapper<std::string, double>)
127+
.def("transform", &GetTransformWrapper<std::string, std::string>)
116128
.def("__len__", &StringContainer::GetSize)
117129
.def("__bool__", [](const StringContainer &self) { return !self.IsEmpty(); })
118130
.def("__getitem__", &GetItem<std::string>)

include/concepts/callable_concepts.hpp

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,30 @@ template <typename Func>
9393
concept TimerCallback = std::invocable<Func, std::int64_t> &&
9494
std::same_as<std::invoke_result_t<Func, std::int64_t>, void>;
9595

96+
/**
97+
* @brief Concept for transformation functions that can be used with container transformations
98+
*
99+
* @tparam Func The type of the transformation function
100+
* @tparam Input The type of the input elements
101+
* @tparam Output The type of the output elements
102+
*
103+
* This concept ensures that a type can be used as a transformation function for container
104+
* operations. The function must be invocable with an Input type and return an Output type.
105+
*
106+
* @code
107+
* template <TransformFunction<int, int> Func>
108+
* auto transform_elements(const std::vector<int> &vec, Func func) {
109+
* return vec | std::views::transform(func);
110+
* }
111+
*
112+
* auto square = [](int n) { return n * n; };
113+
* auto squared_numbers = transform_elements(numbers, square);
114+
* @endcode
115+
*/
116+
template <typename Func, typename Input, typename Output = Input>
117+
concept TransformFunction =
118+
std::invocable<Func, Input> && std::convertible_to<std::invoke_result_t<Func, Input>, Output>;
119+
96120
/**
97121
* @brief Concept for predicate functions that can be used with container filtering
98122
*
@@ -101,13 +125,11 @@ concept TimerCallback = std::invocable<Func, std::int64_t> &&
101125
*
102126
* This concept ensures that a type can be used as a predicate for filtering operations.
103127
* The predicate must be invocable with a const reference to T and return a type that
104-
* is convertible to bool. This allows for flexible usage with lambdas, function pointers,
105-
* functors, and other callable objects.
128+
* is convertible to bool.
106129
*
107130
* @code
108131
* template <typename T, PredicateFor<T> Predicate>
109132
* auto filter_elements(const std::vector<T> &vec, Predicate predicate) {
110-
* // Use predicate to filter elements
111133
* return vec | std::views::filter(predicate);
112134
* }
113135
*
@@ -116,7 +138,6 @@ concept TimerCallback = std::invocable<Func, std::int64_t> &&
116138
* @endcode
117139
*/
118140
template <typename Predicate, typename T>
119-
concept PredicateFor = std::invocable<Predicate, const T &> &&
120-
std::convertible_to<std::invoke_result_t<Predicate, const T &>, bool>;
141+
concept PredicateFor = TransformFunction<Predicate, const T &, bool>;
121142

122143
} // namespace cpp_features::concepts

include/containers/container.hpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -276,8 +276,9 @@ class Container {
276276
/**
277277
* @brief Get a transformed view of elements
278278
*
279-
* @tparam Func Type of the transformation function
280-
* @param transform_func Function to apply to each element
279+
* @tparam U The type of the output elements
280+
* @tparam Func The type of the transformation function. Must satisfy TransformFunction<T, U>
281+
* @param transform_func The function to apply to each element
281282
* @return A ranges view containing transformed elements
282283
*
283284
* Returns a lazy-evaluated view where each element is transformed by the provided function.
@@ -286,7 +287,7 @@ class Container {
286287
* auto doubled = container.GetTransformedView([](int n) { return n * 2; });
287288
* @endcode
288289
*/
289-
template <typename Func>
290+
template <typename U = T, cpp_features::concepts::TransformFunction<T, U> Func>
290291
[[nodiscard]] auto GetTransformedView(Func transform_func) const {
291292
return data_ | std::views::transform(transform_func);
292293
}

0 commit comments

Comments
 (0)