Created
May 9, 2013 14:45
-
-
Save gnzlbg/5547905 to your computer and use it in GitHub Desktop.
variadic zip and join function extensions to boost range
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 <array> | |
#include <vector> | |
#include <list> | |
#include <deque> | |
#include "tupleit.hh" // http://pastebin.com/LFkTHdQk | |
#include <stdio.h> | |
#include <boost/range.hpp> | |
#include <boost/range/algorithm.hpp> | |
#include <boost/range/adaptors.hpp> | |
#include <boost/range/join.hpp> | |
#include <boost/tuple/tuple_io.hpp> | |
/// Some parts of this code can be implemented with minor extensions to boost range. | |
/// A variadic join might look like this: | |
template<class C> | |
auto join(C&& c) -> decltype(boost::make_iterator_range(c)) { | |
return boost::make_iterator_range(c); | |
} | |
template<class C, class D, class... Args> | |
auto join(C&& c, D&& d, Args&&... args) | |
-> decltype(boost::join(boost::join(boost::make_iterator_range(std::forward<C>(c)), | |
boost::make_iterator_range(std::forward<D>(d))), | |
join(std::forward<Args>(args)...))) { | |
return boost::join(boost::join(boost::make_iterator_range(std::forward<C>(c)), | |
boost::make_iterator_range(std::forward<D>(d))), | |
join(std::forward<Args>(args)...)); | |
} | |
/// A variadic zip is more complicated. If we want write access we cannot use | |
/// boost zip_iterator. Still one can use Anthony Williams' TupleIterator: | |
template <class... T> | |
auto zip(T&&... c) | |
-> boost::iterator_range< | |
decltype(iterators::makeTupleIterator(std::begin(std::forward<T>(c))...))> { | |
return boost::make_iterator_range | |
(iterators::makeTupleIterator(std::begin(std::forward<T>(c))...), | |
iterators::makeTupleIterator(std::end(std::forward<T>(c))...)); | |
} | |
/// For read only access one could use boost::zip_iterator's but in my opinion | |
/// that is just not good enough. | |
//////////////////////////////////////////////////////////////////////////////// | |
/// Part 2 implementation: | |
/// The following implements the negation operator for range filters: | |
namespace boost { | |
namespace range_detail { | |
// template <typename T> | |
// auto operator!(filter_holder<T> const& f) | |
// -> decltype(adaptors::filtered(std::not1(f.val))) { | |
// return adaptors::filtered(std::not1(f.val)); | |
// } | |
template <typename T> | |
auto operator!(filter_holder<T> const& f) | |
-> decltype(adaptors::filtered(std::not1(f.val))) { | |
return adaptors::filtered(std::not1(f.val)); | |
} | |
} | |
} | |
void part_one() { | |
/// Hi, | |
/// I would like to be able to write the following code using boost range: | |
std::array<double,4> a = {{ 1, 2, 3, 4 }}; | |
std::list<int> b = { 11, 22, 33, 44 }; | |
std::deque<int> c = { 111, 222, 333, 444 }; | |
std::vector<int> d = {1111,2222,3333,4444 }; | |
/// First if the values stored in a container have the same type, I | |
/// would like to iterate over different containers as if it were a single one, and: | |
/// - modify its value | |
for(auto&& i : join(b,c,d)) { i += 1; } | |
/// - use it with boost algorithms | |
boost::transform(join(b,c,d),begin(join(b,c,d)),[&](int j){ return j * 2; }); | |
/// - and read it | |
for(const auto& i : join(b,c,d)) { std::cout << i << "\n"; } | |
/// I think this code is really easier to write than the corresponding stl iterator code. | |
/// A problem I usually face is manipulating data that has diferent types. This data | |
/// is usually stored in different containers for efficiency reasons, but it does | |
/// belong together. A way to iterate through this data is by means of the zip function: | |
for(const auto& t : zip(a,b,c,d)) { std::cout << t << "\n"; } | |
/// However, boost zip_iterators are not writable. I don't really think it would be possible | |
// to make them writtable, but that would allow code like this: | |
typedef decltype(*begin(zip(a,c,d))) tIt; | |
boost::sort(zip(a, c, d), [](const tIt& i, const tIt& j){ | |
return boost::get<0>(i) > boost::get<0>(j); }); | |
/// which sorts the three containers in lock-step after the values in container a: | |
for(auto&& t : zip(a, c, d)) { std::cout << t << std::endl; } | |
} | |
void part_two() { | |
/// When one uses ranges for a while, filtered is something really appealing: | |
std::vector<int> v = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; | |
for(auto i : v | boost::adaptors::filtered([](int i){ return i % 2; })) { | |
std::cout << i << "\n"; | |
} | |
/// Quiet handy, but in real applications some filters are just used over and | |
/// over again, so one might be tempted to store them somehow: | |
/// - in a variable | |
auto not_4 = boost::adaptors::filtered([](int i){ return i != 4; }); | |
/// - or in a function | |
auto feven = [](){ return boost::adaptors::filtered([](int i){ return i % 2 == 0; }); }; | |
for(auto i : v | not_4 | feven()) { std::cout << i << "\n"; } | |
/// However say I now need a filter for odd numbers. I could of course define | |
/// od filter, but I would rather use the negation operator on a filter: | |
//for(auto i : v | !feven()) { std::cout << i << "\n"; } // doesn't work | |
/// but this doesn't work because we constructed the filters using a lambda! | |
/// That is if we want this functionality, right now the only way is to use | |
/// a std::function instead: | |
auto mfeven = boost::adaptors::filtered | |
(std::function<bool(int)>([](int i){ return i % 2 == 0; })); | |
for(auto i : v | !mfeven) { std::cout << i << "\n"; } | |
/// So this feels weird. I guess it would be nice if filtered supported lambdas. | |
/// My last point about filters is about the size of the filtered range. This | |
// feels weird: | |
std::cout << "# of even numbers in vector: " | |
<< boost::count_if(v | feven(), [](int){return true;}) << "\n"; | |
/// Is there a better way of counting the elements of a range (eagerly)? | |
// count(range); | |
} | |
int main() { | |
part_one(); | |
part_two(); | |
return 0; | |
} | |
/// Thanks to: | |
/// everyone who participated in the so discussions): | |
// - join http://stackoverflow.com/questions/14366576/boostrangejoin-for-multiple-ranges | |
// - zip http://stackoverflow.com/questions/13840998/sorting-zipped-locked-containers-in-c-using-boost-or-the-stl | |
// - negation operator for filters http://stackoverflow.com/questions/14769418/negate-boost-range-filtered-adaptor | |
// Anthony Williams for tupleIterator and for pairign of iterators (http://www.justsoftwaresolutions.co.uk/articles/pair_iterators.pdf) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment