Functorproxy für boost::tuple mit Metaprogrammierung?
-
Schönen Abend!
Ich habe mich in Java-Streams verliebt und wollte das Ganze mal zum Spaß in C++ nachbilden bzw. für meine Bedürfnisse besser gestalten. Das Ganze lässt sich prinzipiell ja sehr gut mit Iteratoren-Paaren machen, insbesondere mit den Iteratoren, die es in Boost.Iterator so gibt.
In Java fehlt mir besonders ein Zip-Stream, also die Möglichkeit über zwei Streams gleichzeitig zu laufen. Das habe ich mal brauchbar implementiert:
#include <iostream> #include <vector> #include <list> #include <cstring> #include <set> #include <boost/iterator/filter_iterator.hpp> #include <boost/iterator/transform_iterator.hpp> #include <boost/iterator/zip_iterator.hpp> #include <boost/tuple/tuple.hpp> #include <algorithm> #include <memory> template<typename IterT> class iterator_stream { private: IterT begin_, end_; public: iterator_stream(IterT begin, IterT end) : begin_(std::move(begin)), end_(std::move(end)) { } IterT begin() const { return begin_; } IterT end() const { return end_; } template<typename CallbackT> void for_each(CallbackT cb) { std::for_each(begin_, end_, cb); } template<typename PredicateT> iterator_stream<boost::filter_iterator<PredicateT, IterT>> filter(PredicateT predicate) { typedef boost::filter_iterator<PredicateT, IterT> FilterIterT; return iterator_stream<FilterIterT>(FilterIterT(predicate, begin_, end_), FilterIterT(predicate, end_, end_)); } template<typename MapperT> iterator_stream<boost::transform_iterator<MapperT, IterT>> map(MapperT mapper) { typedef boost::transform_iterator<MapperT, IterT> MapIterT; return iterator_stream<MapIterT>(MapIterT(begin_, mapper), MapIterT(end_, mapper)); } }; template<typename ContainerT> auto stream(ContainerT& c) -> iterator_stream<typename ContainerT::iterator> { return iterator_stream<typename ContainerT::iterator>(c.begin(), c.end()); } template<typename T, std::size_t Size> iterator_stream<T*> stream(T (&a)[Size]) { return iterator_stream<T*>(a, a + Size); } template<typename Iter1T, typename Iter2T> iterator_stream<boost::zip_iterator<boost::tuple<Iter1T, Iter2T>>> zip(iterator_stream<Iter1T> const& s1, iterator_stream<Iter2T> const& s2) { typedef boost::zip_iterator<boost::tuple<Iter1T, Iter2T>> ZipIterT; return iterator_stream<ZipIterT>( ZipIterT(boost::make_tuple(s1.begin(), s2.begin())), ZipIterT(boost::make_tuple(s1.end(), s2.end()))); } int main() { std::vector<char const*> vec = { "Hello", "World", "!"}; stream(vec) .filter([](auto& str) { return std::strlen(str) > 1; }) .map([](auto& str) { return std::strlen(str); }) .for_each([](auto len) { std::cout << len << std::endl; }); std::list<std::string> en = { "Hello", "World", "Asshole" }; char const* de[] = { "Hallo", "Welt", "Arschloch" }; std::set<std::string> insults = { "Asshole" }; zip(stream(en), stream(de)) .filter([&] (auto const& t) { return insults.find(boost::get<0>(t)) == insults.end(); }) .map([](auto const& t) { return boost::get<0>(t) + " - " + boost::get<1>(t); }) .for_each([] (auto const& str) { std::cout << str << std::endl; }); }
Bzw. hier auf Ideone zum ausprobieren: http://ideone.com/HSZq2F
Dann komme ich gleich zu meinem Problem, das sich aufgrund meiner schwer eingerosteten C++ Kenntnisse für mich nicht vernünftig lösen lässt:
[](auto const& t) { return boost::get<0>(t) + " - " + boost::get<1>(t);
Das finde ich hässlich. boost::tuple ist für mich ein Implementierungsdetail, mit dem man sich nicht rumschlagen sollte. Die Lesbarkeit leidet auch darunter.
[](auto const& word1, auto const& word2) { return word1 + " - " + word2;
ist, was ich mir wünschen würde. Es bräuchte also einen Proxy-Funktor, der variadisch das Tuple entpackt und mit dem Inhalt das Lambda (also den Callback) aufruft.
Kann mir bitte jemand auf die Sprünge helfen?
Danke und schöne Grüße,
Ethon
-
Das entpacken von Tuples ist doch schon oft auf SO demonstriert worden. Du generierst ad hoc eine
index_sequence
und rufst darüberget
auf.template <typename Tup, typename F, typename... Is> constexpr decltype(auto) expand(Tup&& t, F f, std::index_sequence<Is...>) { return f(std::get<Is>(std::forward<Tup>(t))...); } template <typename Tup, typename F> constexpr decltype(auto) expand(Tup&& t, F f) { return expand(std::forward<T>(t), f, std::make_index_sequence<std::tuple_length<Tup>{}>{}); }
(Ungetestet)
Das wird übrigens mit ad hoc packs deutlich einfacher. Ich arbeite gerade an einem entsprechenden Paper.
-
War etwas umzubauen (boost::tuple funktioniert auch anders als std::tuple) aber vielen Dank, funktioniert jetzt.
-
Schon mal
std::apply(func, tupleObject)
probiert? apply() wendet func auf alle Tupelelemente an. Ab c++14, soweit ich weiß