tail von std::tuple
-
Danke krümelkacker!
Die Idee hatte ich von Anfang an als 'Not-Lösung', aber ich hoffte, dass es einen schöneren Weg gibt
-
XSpille schrieb:
Danke krümelkacker!
Die Idee hatte ich von Anfang an als 'Not-Lösung', aber ich hoffte, dass es einen schöneren Weg gibt
So hässlich finde ich das gar nicht. Der erste Teil ist halbwegs generisch/wiederverwendbar.
Das Tuple-spezifische beschränkt sich auf
template<class T1, class T2> struct blah { T1& t1; T2& t2; blah(T1& t1, T2& t2) : t1(t1), t2(t2) {} template<int I> void operator()(std::integral_constant<int,I>) { cout << std::get<I>(t1) << ", " << std::get<I>(t2) << '\n'; } };
Mit polymorphen Lambdas wäre das wahrscheinlich noch schöner geworden:
auto tuple1 = make_tuple(23,3.14159265,'p'); auto tuple2 = make_tuple(99,2.71828183',x'); compile_time_for<0,3>([&](auto index_tag){ constexpr int idx = decltype(index_tag)::value; cout << std::get<idx>(tuple1) << ", " << std::get<idx>(tuple2) << '\n'; });
Aber so etwas wie "polymorphe Lambdas" (siehe auto als Lambda-Parameter-Typ) gibt es leider nicht.
Das mit den Fusion-Iteratoren sieht auch nett aus. Allerdings macht es mir etwas Sorgen, dass die Abbruchbedingung als "Laufzeit-If-Return" dort steht.
-
krümelkacker schrieb:
Das mit den Fusion-Iteratoren sieht auch nett aus. Allerdings macht es mir etwas Sorgen, dass die Abbruchbedingung als "Laufzeit-If-Return" dort steht.
Muss sie nicht. Man kann zur Compilezeit mit
boost::fusion::result_of::equal_to<It, ItEnd>::type
arbeiten (z.B. inenable_if
). Nach ein bisschen Nachforschen stieß ich aber auch auf denzip_view
. Der macht aus mehreren Sequenzen eine Sequenz von Tupeln der Originalelemente:struct print { template<class Tupel> void operator()(const Tuple& t) { std::cout << at_c<0>(t) << ' ' << at_c<1>(t) << std::endl; } } for_each(zip(t1, t2), print());
Dank der Mächtigkeit von Fusion und mit einer Lambda-Bibliothek (welche mit Boost.ResultOf zusammen spielen muss) geht es sogar mit einem Einzeiler:
for_each(zip(t1, t2), make_fused(std::cout << _1 << ' ' << _2 << std::endl));
-
ipsec schrieb:
Muss sie nicht.
Echt nicht?
Also bei folgendem Code
#include<iostream> #include <boost/fusion/sequence.hpp> #include <boost/fusion/include/sequence.hpp> #include <boost/fusion/adapted/boost_tuple.hpp> #include <boost/fusion/iterator.hpp> template<class It1, class ItEnd1, class It2, class ItEnd2> void print(const It1& begin1, const ItEnd1& end1, const It2& begin2, const ItEnd2& end2) { if(begin1 == end1 || begin2 == end2) return; std::cout << boost::fusion::deref(begin1) << ' ' << boost::fusion::deref(begin2) << std::endl; print(boost::fusion::next(begin1), end1, boost::fusion::next(begin2), end2); } int main(){ typedef boost::tuple<int, char, double> tuple_type1; tuple_type1 t1(1, 'c', 1.1); typedef boost::tuple<char, double, int> tuple_type2; tuple_type2 t2('b', 2.2, 2); print(boost::fusion::begin(t1),boost::fusion::end(t1),boost::fusion::begin(t2),boost::fusion::end(t2)); }
erhalte ich eine ellenlange Fehlermeldung, die unter anderem folgende Zeilen enthält:
test6.cpp:13:5: error: no matching function for call to 'deref(const boost::fusion::boost_tuple_iteratorboost::tuples::null_type&)'
test6.cpp:14:5: error: no matching function for call to 'next(const boost::fusion::boost_tuple_iteratorboost::tuples::null_type&)'
Für mich (als Hobby-Programmierer) sieht es allerdings so aus als würde die Bedingung nicht greifen...
-
Hm sieht so aus, als muss man es zur Compilezeit prüfen (z.B. mit
enable_if
). Bei genauerer Betrachtung eigentlich auch logisch [Hintergrund: die Position des Iterators verändert dessen Typ, d.h.decltype(it) != decltype(next(it))
. Offenbar prüftderef
schon zur Compiletime, statt zur Runtime, ob der Iterator gültig ist]. Leider ist es mir z.Z. nicht möglich, die Codes zu testen. Funktioniert denn das andere mit demzip
?
-
Dann frag ich mal vorsichtig:
Welche Bedingungen werden zur Compilierzeit denn schon ausgewertet?Ich dachte wirkliche Meta-Programmierung geht nur über typedefs und Konstanten...
In folgendem einfachen Beispiel scheint die Bedingung schon nicht zu greifen:
template<int I> void endless(){ endless<I+1>(); } template<int I> void print(){ if(I==1) endless<0>(); } int main(){ print<0>(); }
test7.cpp: In function 'void endless() [with int I = 1022]':
test7.cpp:4:2: error: template instantiation depth exceeds maximum of 1024 (use -ftemplate-depth= to increase the maximum) instantiating 'void endless() [with int I = 1023]'
test7.cpp:4:2: recursively instantiated from 'void endless() [with int I = 1]'
test7.cpp:4:2: instantiated from 'void endless() [with int I = 0]'
test7.cpp:10:3: instantiated from 'void print() [with int I = 0]'
test7.cpp:16:11: instantiated from here
-
ipsec schrieb:
Funktioniert denn das andere mit dem
zip
?Also folgender Code
#include <iostream> #include <boost/fusion/sequence/intrinsic/at_c.hpp> #include <boost/fusion/adapted/boost_tuple.hpp> #include <boost/fusion/algorithm/transformation/zip.hpp> #include <boost/foreach.hpp> #define foreach BOOST_FOREACH struct print { template<class Tuple> void operator()(const Tuple& t) { std::cout << boost::fusion::at_c<0>(t) << ' ' << boost::fusion::at_c<1>(t) << std::endl; } }; int main(){ typedef boost::tuple<int, char, double> tuple_type1; tuple_type1 t1(1, 'c', 1.1); typedef boost::tuple<char, double, int> tuple_type2; tuple_type2 t2('b', 2.2, 2); foreach(boost::fusion::zip(t1, t2), print()); }
führt zu dieser Fehlermeldung, die ich persönlich nicht zuordnen kann:
In file included from /opt/local/include/boost/mpl/aux_/begin_end_impl.hpp:20:0,
from /opt/local/include/boost/mpl/begin_end.hpp:18,
from /opt/local/include/boost/mpl/is_sequence.hpp:19,
from /opt/local/include/boost/fusion/support/detail/is_mpl_sequence.hpp:12,
from /opt/local/include/boost/fusion/support/tag_of.hpp:13,
from /opt/local/include/boost/fusion/sequence/intrinsic/at.hpp:12,
from /opt/local/include/boost/fusion/sequence/intrinsic/at_c.hpp:10,
from test8.cpp:2:
/opt/local/include/boost/mpl/eval_if.hpp: In instantiation of 'boost::mpl::eval_if<mpl_::bool_<true>, boost::range_const_iterator<print>, boost::range_mutable_iterator<print> >':
/opt/local/include/boost/foreach.hpp:355:13: instantiated from 'boost::foreach_detail_::foreach_iterator<print, mpl_::bool_<true> >'
test8.cpp:22:2: instantiated from here
/opt/local/include/boost/mpl/eval_if.hpp:38:31: error: no type named 'type' in 'boost::mpl::eval_if<mpl_::bool_<true>, boost::range_const_iterator<print>, boost::range_mutable_iterator<print> >::f_'
test8.cpp: In function 'int main()':
test8.cpp:22:2: error: no matching function for call to 'begin(const boost::foreach_detail_::auto_any_base&, boost::foreach_detail_::type2type<print, mpl_::bool_<true> >, boost::mpl::or_<boost::mpl::and_<boost::mpl::not_<boost::is_array<print> >, mpl_::bool_<true>, mpl_::bool_<true>, mpl_::bool_<true>, mpl_::bool_<true> >, boost::mpl::and_<boost::mpl::not_<boost::foreach::is_noncopyable<print> >, boost::foreach::is_lightweight_proxy<print>, mpl_::bool_<true>, mpl_::bool_<true>, mpl_::bool_<true> >, mpl_::bool_<false>, mpl_::bool_<false>, mpl_::bool_<false> >)'
test8.cpp:22:2: note: candidates are:
/opt/local/include/boost/foreach.hpp:637:61: note: template<class T, class C> boost::foreach_detail_::auto_any<typename boost::foreach_detail_::foreach_iterator<T, C>::type> boost::foreach_detail_::begin(boost::foreach_detail_::auto_any_t, boost::foreach_detail_::type2type<T, C>, mpl_::true_)
/opt/local/include/boost/foreach.hpp:645:62: note: template<class T, class C> boost::foreach_detail_::auto_any<typename boost::foreach_detail_::foreach_iterator<T, C>::type> boost::foreach_detail_::begin(boost::foreach_detail_::auto_any_t, boost::foreach_detail_::type2type<T, C>, mpl_::false_)
/opt/local/include/boost/foreach.hpp:666:63: note: template<class T, class C> boost::foreach_detail_::auto_any<T*> boost::foreach_detail_::begin(boost::foreach_detail_::auto_any_t, boost::foreach_detail_::type2type<T*, C>, mpl_::true_)
test8.cpp:22:2: error: no matching function for call to 'end(const boost::foreach_detail_::auto_any_base&, boost::foreach_detail_::type2type<print, mpl_::bool_<true> >, boost::mpl::or_<boost::mpl::and_<boost::mpl::not_<boost::is_array<print> >, mpl_::bool_<true>, mpl_::bool_<true>, mpl_::bool_<true>, mpl_::bool_<true> >, boost::mpl::and_<boost::mpl::not_<boost::foreach::is_noncopyable<print> >, boost::foreach::is_lightweight_proxy<print>, mpl_::bool_<true>, mpl_::bool_<true>, mpl_::bool_<true> >, mpl_::bool_<false>, mpl_::bool_<false>, mpl_::bool_<false> >)'
test8.cpp:22:2: note: candidates are:
/opt/local/include/boost/foreach.hpp:677:59: note: template<class T, class C> boost::foreach_detail_::auto_any<typename boost::foreach_detail_::foreach_iterator<T, C>::type> boost::foreach_detail_::end(boost::foreach_detail_::auto_any_t, boost::foreach_detail_::type2type<T, C>, mpl_::true_)
/opt/local/include/boost/foreach.hpp:685:60: note: template<class T, class C> boost::foreach_detail_::auto_any<typename boost::foreach_detail_::foreach_iterator<T, C>::type> boost::foreach_detail_::end(boost::foreach_detail_::auto_any_t, boost::foreach_detail_::type2type<T, C>, mpl_::false_)
/opt/local/include/boost/foreach.hpp:706:57: note: template<class T, class C> boost::foreach_detail_::auto_any<int> boost::foreach_detail_::end(boost::foreach_detail_::auto_any_t, boost::foreach_detail_::type2type<T*, C>, mpl_::true_)
test8.cpp:22:2: error: no matching function for call to 'deref(const boost::foreach_detail_::auto_any_base&, boost::foreach_detail_::type2type<print, mpl_::bool_<true> >)'
test8.cpp:22:2: note: candidate is:
/opt/local/include/boost/foreach.hpp:745:40: note: template<class T, class C> typename boost::foreach_detail_::foreach_reference::type boost::foreach_detail_::deref(boost::foreach_detail_::auto_any_t, boost::foreach_detail_::type2type<T, C>)
-
Edit: zum Fehler mit
zip
: möglicherweise hilft es schon, den operator() const zu machen. Compilerfehlermeldungen sind ein Graus.Offenbar geht es in deinem Beispiel nicht mal mit Spezialisierung, da immer versucht wird,
endless<0>
zu generieren (auch wenn es nie gebraucht wird).
Bei den Iteratoren ist der Fall aber anders. Folgende Funktion sollte z.B. schon funktionieren (der Einfachhalt halber mit nur einem Iterator)template<class It, class ItEnd> void foo(It it, ItEnd end) { do_something(deref(it)); foo(next(it), end); } template<class ItEnd> void foo(ItEnd, ItEnd) { }
Wobei
equal_to
aber vermutlich der korektere Weg wäre. Mit 2 Iteratoren ist es natürlich etwas aufwendiger. Du könntest annehmen, dass die zweite Sequenz immer mindestens so lang ist, wie die erste, dann brauchst du nur einen end-Iterator (Verstöße werden zur Compilezeit erkannt). Oder du sparst dir die Iteratoren ganz und nimmst sowas wiepop_back
oderzip
.
-
ipsec schrieb:
Edit: zum Fehler mit
zip
: möglicherweise hilft es schon, den operator() const zu machen. Compilerfehlermeldungen sind ein Graus.Nein... Ein
void operator() const (const Tuple& t)
verändert (scheinbar) gar nichts (ohne jetzt alles 100%ig verglichen zu haben)
-
Ok zum zip-Fehler: das foreach ist nicht etwas BOOST_FOREACH, sondern boost::fusion::for_each. Folgender Code funktioniert (seit etwa 1min weiß ich, dass ideone Boost installiert hat, also konnte ich testen):
#include <iostream> #include <boost/fusion/sequence/intrinsic/at_c.hpp> #include <boost/fusion/adapted/boost_tuple.hpp> #include <boost/fusion/algorithm/transformation/zip.hpp> #include <boost/fusion/include/for_each.hpp> struct print { template<class Tuple> void operator()(const Tuple& t) const { std::cout << boost::fusion::at_c<0>(t) << ' ' << boost::fusion::at_c<1>(t) << std::endl; } }; int main(){ typedef boost::tuple<int, char, double> tuple_type1; tuple_type1 t1(1, 'c', 1.1); typedef boost::tuple<char, double, int> tuple_type2; tuple_type2 t2('b', 2.2, 2); boost::fusion::for_each(boost::fusion::zip(t1, t2), print()); }
Ausgabe
1 b c 2.2 1.1 2
-
ipsec schrieb:
Folgende Funktion sollte z.B. schon funktionieren (der Einfachhalt halber mit nur einem Iterator)
Komplettes (nicht-)kompilierfähiges Programm:
#include <iostream> #include <boost/fusion/sequence.hpp> #include <boost/fusion/include/sequence.hpp> #include <boost/fusion/adapted/boost_tuple.hpp> #include <boost/fusion/iterator.hpp> template<class It, class ItEnd> void foo(It it, ItEnd end) { do_something(deref(it)); foo(next(it), end); } template<class ItEnd> void foo(ItEnd, ItEnd) { } int main(){ typedef boost::tuple<int, char, double> tuple_type1; tuple_type1 t1(1, 'c', 1.1); typedef boost::tuple<char, double, int> tuple_type2; tuple_type2 t2('b', 2.2, 2); foo(boost::fusion::begin(t1),boost::fusion::end(t1)); }
Diesmal mit kompletter Fehlermeldung, auch wenn es für mich nach dem gleichen Problem aussieht:
test9.cpp: In function 'void foo(It, ItEnd) [with It = boost::fusion::boost_tuple_iterator<boost::tuples::tuple<int, char, double> >, ItEnd = boost::fusion::boost_tuple_iteratorboost::tuples::null_type]':
test9.cpp:25:53: instantiated from here
test9.cpp:11:5: error: 'do_something' was not declared in this scope
test9.cpp: In function 'void foo(It, ItEnd) [with It = boost::fusion::boost_tuple_iterator<boost::tuples::cons<char, boost::tuples::cons<double, boost::tuples::null_type> > >, ItEnd = boost::fusion::boost_tuple_iteratorboost::tuples::null\_type]':
test9.cpp:12:5: instantiated from 'void foo(It, ItEnd) [with It = boost::fusion::boost_tuple_iterator<boost::tuples::tuple<int, char, double> >, ItEnd = boost::fusion::boost_tuple_iteratorboost::tuples::null_type]'
test9.cpp:25:53: instantiated from here
test9.cpp:11:5: error: 'do_something' was not declared in this scope
test9.cpp: In function 'void foo(It, ItEnd) [with It = boost::fusion::boost_tuple_iterator<boost::tuples::cons<double, boost::tuples::null_type> >, ItEnd = boost::fusion::boost_tuple_iteratorboost::tuples::null\_type]':
test9.cpp:12:5: recursively instantiated from 'void foo(It, ItEnd) [with It = boost::fusion::boost_tuple_iterator<boost::tuples::cons<char, boost::tuples::cons<double, boost::tuples::null_type> > >, ItEnd = boost::fusion::boost_tuple_iteratorboost::tuples::null\_type]'
test9.cpp:12:5: instantiated from 'void foo(It, ItEnd) [with It = boost::fusion::boost_tuple_iterator<boost::tuples::tuple<int, char, double> >, ItEnd = boost::fusion::boost_tuple_iteratorboost::tuples::null_type]'
test9.cpp:25:53: instantiated from here
test9.cpp:11:5: error: 'do_something' was not declared in this scope
In file included from /opt/local/include/boost/fusion/sequence/comparison/detail/equal_to.hpp:12:0,
from /opt/local/include/boost/fusion/sequence/comparison/equal_to.hpp:14,
from /opt/local/include/boost/fusion/sequence/comparison.hpp:10,
from /opt/local/include/boost/fusion/sequence.hpp:11,
from test9.cpp:2:
/opt/local/include/boost/fusion/iterator/deref.hpp: At global scope:
/opt/local/include/boost/fusion/iterator/deref.hpp: In instantiation of 'boost::fusion::extension::deref_implboost::fusion::iterator\_facade\_tag::apply<boost::fusion::boost_tuple_iteratorboost::tuples::null\_type >':
/opt/local/include/boost/fusion/iterator/deref.hpp:50:16: instantiated from 'boost::fusion::result_of::deref<boost::fusion::boost_tuple_iteratorboost::tuples::null\_type >'
test9.cpp:12:5: recursively instantiated from 'void foo(It, ItEnd) [with It = boost::fusion::boost_tuple_iterator<boost::tuples::cons<char, boost::tuples::cons<double, boost::tuples::null_type> > >, ItEnd = boost::fusion::boost_tuple_iteratorboost::tuples::null\_type]'
test9.cpp:12:5: instantiated from 'void foo(It, ItEnd) [with It = boost::fusion::boost_tuple_iterator<boost::tuples::tuple<int, char, double> >, ItEnd = boost::fusion::boost_tuple_iteratorboost::tuples::null_type]'
test9.cpp:25:53: instantiated from here
/opt/local/include/boost/fusion/iterator/deref.hpp:34:20: error: no class template named 'deref' in 'struct boost::fusion::boost_tuple_iteratorboost::tuples::null_type'
test9.cpp: In function 'void foo(It, ItEnd) [with It = boost::fusion::boost_tuple_iteratorboost::tuples::null\_type, ItEnd = boost::fusion::boost_tuple_iteratorboost::tuples::null\_type]':
test9.cpp:12:5: recursively instantiated from 'void foo(It, ItEnd) [with It = boost::fusion::boost_tuple_iterator<boost::tuples::cons<char, boost::tuples::cons<double, boost::tuples::null_type> > >, ItEnd = boost::fusion::boost_tuple_iteratorboost::tuples::null\_type]'
test9.cpp:12:5: instantiated from 'void foo(It, ItEnd) [with It = boost::fusion::boost_tuple_iterator<boost::tuples::tuple<int, char, double> >, ItEnd = boost::fusion::boost_tuple_iteratorboost::tuples::null_type]'
test9.cpp:25:53: instantiated from here
test9.cpp:11:5: error: no matching function for call to 'deref(boost::fusion::boost_tuple_iteratorboost::tuples::null_type&)'
test9.cpp:11:5: note: candidate is:
/opt/local/include/boost/fusion/iterator/deref.hpp:58:5: note: template<class Iterator> typename boost::fusion::result_of::deref::type boost::fusion::deref(const Iterator&)
test9.cpp:11:5: error: 'do_something' was not declared in this scope
In file included from /opt/local/include/boost/fusion/sequence/comparison/detail/equal_to.hpp:13:0,
from /opt/local/include/boost/fusion/sequence/comparison/equal_to.hpp:14,
from /opt/local/include/boost/fusion/sequence/comparison.hpp:10,
from /opt/local/include/boost/fusion/sequence.hpp:11,
from test9.cpp:2:
/opt/local/include/boost/fusion/iterator/next.hpp: At global scope:
/opt/local/include/boost/fusion/iterator/next.hpp: In instantiation of 'boost::fusion::extension::next_implboost::fusion::iterator\_facade\_tag::apply<boost::fusion::boost_tuple_iteratorboost::tuples::null\_type >':
test9.cpp:12:5: recursively instantiated from 'void foo(It, ItEnd) [with It = boost::fusion::boost_tuple_iterator<boost::tuples::cons<char, boost::tuples::cons<double, boost::tuples::null_type> > >, ItEnd = boost::fusion::boost_tuple_iteratorboost::tuples::null\_type]'
test9.cpp:12:5: instantiated from 'void foo(It, ItEnd) [with It = boost::fusion::boost_tuple_iterator<boost::tuples::tuple<int, char, double> >, ItEnd = boost::fusion::boost_tuple_iteratorboost::tuples::null_type]'
test9.cpp:25:53: instantiated from here
/opt/local/include/boost/fusion/iterator/next.hpp:33:20: error: no class template named 'next' in 'struct boost::fusion::boost_tuple_iteratorboost::tuples::null_type'
test9.cpp: In function 'void foo(It, ItEnd) [with It = boost::fusion::boost_tuple_iteratorboost::tuples::null\_type, ItEnd = boost::fusion::boost_tuple_iteratorboost::tuples::null\_type]':
test9.cpp:12:5: recursively instantiated from 'void foo(It, ItEnd) [with It = boost::fusion::boost_tuple_iterator<boost::tuples::cons<char, boost::tuples::cons<double, boost::tuples::null_type> > >, ItEnd = boost::fusion::boost_tuple_iteratorboost::tuples::null\_type]'
test9.cpp:12:5: instantiated from 'void foo(It, ItEnd) [with It = boost::fusion::boost_tuple_iterator<boost::tuples::tuple<int, char, double> >, ItEnd = boost::fusion::boost_tuple_iteratorboost::tuples::null_type]'
test9.cpp:25:53: instantiated from here
test9.cpp:12:5: error: no matching function for call to 'next(boost::fusion::boost_tuple_iteratorboost::tuples::null_type&)'
test9.cpp:12:5: note: candidate is:
/opt/local/include/boost/fusion/iterator/next.hpp:57:5: note: template<class Iterator> const typename boost::fusion::result_of::next::type boost::fusion::next(const Iterator&)
-
ipsec schrieb:
Ok zum zip-Fehler: das foreach ist nicht etwas BOOST_FOREACH, sondern boost::fusion::for_each. Folgender Code funktioniert (seit etwa 1min weiß ich, dass ideone Boost installiert hat, also konnte ich testen)
Okay...
So funktioniert es jedenfalls auch bei mirEDIT: und was kann BOOST_FOREACH, was boost::fusion::for_each nicht kann bzw. umgekehrt?
-
fusion::for_each ist fuer fusion container. also heterogene datentypen wie tuple. BOOST_FOREACH ist fuer homogene Container (also std::vector<double>).