Last active
August 29, 2015 14:04
-
-
Save borman/7b4e5d021afd3f28bd87 to your computer and use it in GitHub Desktop.
c++ filter_map
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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