Skip to content

Instantly share code, notes, and snippets.

@borman
Last active August 29, 2015 14:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save borman/7b4e5d021afd3f28bd87 to your computer and use it in GitHub Desktop.
Save borman/7b4e5d021afd3f28bd87 to your computer and use it in GitHub Desktop.
c++ filter_map
#include <iostream>
#include <vector>
#include <type_traits>
#include <boost/optional.hpp>
#include <boost/range/adaptor/transformed.hpp>
#include <boost/range/adaptor/filtered.hpp>
template<typename MapperT>
class filter_mapped_adaptor;
template<typename T>
struct nonempty {
using result_type = bool;
bool operator ()(T &&x) const {
return x;
}
};
template<typename T>
struct unwrap;
template<typename T>
struct unwrap<boost::optional<T>> {
using result_type = T;
T operator ()(const boost::optional<T> &x) const {
return *x;
}
};
template<typename RangeT, typename MapperT>
struct filter_mapped_helper {
using input_range_type = typename std::decay<RangeT>::type;
using input_item_type = typename boost::range_value<input_range_type>::type;
using optional_item_type = typename std::result_of<MapperT(input_item_type)>::type;
using transformed_type = decltype(
static_cast<RangeT>(*reinterpret_cast<input_range_type *>(0))
| boost::adaptors::transformed(
static_cast<MapperT>(*reinterpret_cast<MapperT *>(0))
)
| boost::adaptors::filtered(
nonempty<optional_item_type>()
)
| boost::adaptors::transformed(
unwrap<optional_item_type>()
)
);
};
template<typename MapperT>
class filter_mapped_adaptor {
MapperT mapper_;
template<typename RangeT, typename SomeMapperT>
friend auto operator |(RangeT &&rng, const filter_mapped_adaptor<SomeMapperT> &adaptor)
-> typename filter_mapped_helper<RangeT, SomeMapperT>::transformed_type;
public:
filter_mapped_adaptor(MapperT mapper):
mapper_ (mapper)
{ }
};
template<typename RangeT, typename SomeMapperT>
auto operator |(RangeT &&rng, const filter_mapped_adaptor<SomeMapperT> &adaptor)
-> typename filter_mapped_helper<RangeT, SomeMapperT>::transformed_type
{
using optional_item_type = typename filter_mapped_helper<RangeT, SomeMapperT>::optional_item_type;
return rng
| boost::adaptors::transformed(
adaptor.mapper_
)
| boost::adaptors::filtered(
nonempty<optional_item_type>()
)
| boost::adaptors::transformed(
unwrap<optional_item_type>()
);
}
template<typename T>
filter_mapped_adaptor<T> filter_mapped(T mapper) {
return mapper;
}
int main() {
using boost::optional;
std::vector<int> values = {1,2,3,4,5,3,6,2,36,4,6,2,0,3,4};
std::cout << "reference:\n";
for (int x: values) {
std::cout << x << " -> ";
if (x % 2) {
std::cout << x * x + 1;
} else {
std::cout << "...";
}
std::cout << '\n';
}
std::cout << '\n';
auto mapper = [](int x) -> optional<int> {
if (x % 2) {
return x * x + 1;
} else {
return {};
}
};
auto mapped = values | filter_mapped(mapper);
std::cout << "test:\n";
for (auto &&x: mapped) {
std::cout << x << " ";
}
std::cout << '\n';
return 0;
}
reference:
1 -> 2
2 -> ...
3 -> 10
4 -> ...
5 -> 26
3 -> 10
6 -> ...
2 -> ...
36 -> ...
4 -> ...
6 -> ...
2 -> ...
0 -> ...
3 -> 10
4 -> ...
test:
2 10 26 10 10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment