campers TMP-Aufgabe(n): 7
-
Was spricht gegen std::binary_search?
Ich wusste bis eben nicht dass der Compiler bei
template<int... Values> bool contains(int v) { int arr[sizeof... Values] = { Values... }; return binary_search(begin(arr), end(arr), v); }wirklich sämtlich Overhead rausoptimiert, zumindestens macht das der GCC bei -O3.
So macht es natürlich weniger Sinn. Trotzdem schöne Lösungen ihr 2.

-
Ich habe gerade, glaube ich, einen genialen Trick gefunden.

-
Sone schrieb:
Ich habe gerade, glaube ich, einen genialen Trick gefunden.

Dann teil ihn wenigstens mit uns.
-
fghfghfgh schrieb:
Sone schrieb:
Ich habe gerade, glaube ich, einen genialen Trick gefunden.

Dann teil ihn wenigstens mit uns.
Mit GCC 4.8 wird das nix. Versuch es jetzt mit Clang... (Edit: Scheiße. Ich krieg Clang nicht installiert. Führt wohl nix um Linux herum... *sigh*)
template<int N> struct Tag { static constexpr int value = N; constexpr Tag(){} template<int N2> constexpr Tag<N+N2> operator,(Tag<N2>) { return Tag<N+N2>(); } };
decltype( Tag<args>... )::value
-
camper schrieb:
kleine Aufgabe für zwischendurch:
maps
Gegeben seien zwei index_listen gleicher Länge und eine zusätzliche Zahl.
wenn die Zahl in der ersten Liste enthalten ist, soll der entsprechende Wert aus der zweiten Liste zurückgegeben werden.
Keine Rekursion.
lookup<map<index_list<2,3,5,7>,index_list<1,2,3,5>>,5>::value ergibt 3
Bonuspunkte, wenn die map bidirektionales Lookup erlaubt.template<typename, typename, int> struct lookup; template<int ... keys, int ... vals, int index> struct lookup<index_list<keys...>, index_list<vals...>, index> { typedef index_list<((keys == index) * vals)...> hilighted_second; typedef index_list<((vals == index) * keys)...> hilighted_first; };Jetzt muss man nur noch aufaddieren (ggf. mit dem Trick oben) und man hat die Werte. Null bei keinem Fund, Summe der Werte bei mehreren Funden... letzteres ist ein Problem, nehme ich an?
Edit: Ach, wenn dieser Trick oben ginge... man könnte so viel geiles damit machen...
-
Sone schrieb:
fghfghfgh schrieb:
Sone schrieb:
Ich habe gerade, glaube ich, einen genialen Trick gefunden.

Dann teil ihn wenigstens mit uns.
Mit GCC 4.8 wird das nix. Versuch es jetzt mit Clang... (Edit: Scheiße. Ich krieg Clang nicht installiert. Führt wohl nix um Linux herum... *sigh*)
template<int N> struct Tag { static constexpr int value = N; constexpr Tag(){} template<int N2> constexpr Tag<N+N2> operator,(Tag<N2>) { return Tag<N+N2>(); } };
decltype( Tag<args>... )::valueMit clang wird das auch nichts werden. Das durch eine Packexpansion erzeugte Komma ist niemals ein Kommaoperator.
-
camper schrieb:
Mit clang wird das auch nichts werden. Das durch eine Packexpansion erzeugte Komma ist niemals ein Kommaoperator.
Verdammt...!
-
Kleiner Tip: Templateargumentlisten sind nicht die einzigen Stellen, wo Packexpansionen auftreten können (14.5.3/4)
-
Spielst du auf sowas an?
#include <type_traits> template<typename base, int val> struct AccTag { static int constexpr value = val * std::is_base_of<AccTag<base, val>, base>::value + AccTag<base, val-1>::value; }; template<typename base> struct AccTag<base, 0> : std::integral_constant<int, 0> {}; template<int ... args> struct accumulate : AccTag<accumulate<args...>, args>... // Trick: Da AccTag von einem Template-Parameter abhängt, wird seine Definition erst wenn sie gebraucht wird angesehen - wenn accumulate schon *vollständig definiert* ist. { static int constexpr value = AccTag<accumulate<args...>, 10>::value; // Hmm. Das muss schöner gehen, ich mach mich ran. };Was an dieser Lösung schön ist: Hier wird nix aufgeteilt, wie du wolltest.

Das Blöde: Die Instantiierungstiefe überschreitet ggf. ihr Maximum. Doof.
P.S.: Empfehle mir mal das beste Linux-Derivat für GCC. Einfach fürs programmieren mit GCC. Gentoo? (Edit: Naja, Gentoo wäre mir vielleicht zuviel. Das ist schon extrem)
-
Irgendeine Distro die Bleeding Edge Pakete anbietet wenn es dir speziell um die neuesten Versionen geht. Fedora ist einsteigerfreundlicch und du kannst meisten relativ neuen Kram haben. Obwohl ich mit Fedora 17 immer noch den 4.7.2 habe, gab wohl keinen Grund umzustellen...
-
Ok, das ist Blödsinn. Da wäre
template<int ... args> struct index_list { static constexpr int arr[]{args...}; }; template<typename base, int index> struct AccTag { static int constexpr value = base::arr[index] + AccTag<base, index-1>::value; }; template<typename base> struct AccTag<base, -1> : std::integral_constant<int, 0> {}; template<int ... args> struct accumulate : std::integral_constant<int, AccTag<index_list<args...>, sizeof...(args)-1>::value> {};Schon direkter.
-
Sone schrieb:
Ok, das ist Blödsinn. Da wäre
template<int ... args> struct index_list { static constexpr int arr[]{args...}; }; template<typename base, int index> struct AccTag { static int constexpr value = base::arr[index] + AccTag<base, index-1>::value; }; template<typename base> struct AccTag<base, -1> : std::integral_constant<int, 0> {}; template<int ... args> struct accumulate : std::integral_constant<int, AccTag<index_list<args...>, sizeof...(args)-1>::value> {};Schon direkter.
Das ist aber auch nur normale Rekursion etwas umständlicher geschrieben. Es wird immer noch die Liste bei jedem Schritt mitgechleppt.
#include <type_traits> template<int ... args> struct index_list { static constexpr int arr[]{args...}; using type = index_list; }; template<typename base, int index> struct AccTag { static int constexpr value = base::arr[index] + AccTag<base, index-1>::value; }; template<typename base> struct AccTag<base, -1> : std::integral_constant<int, 0> {}; template<int ... args> struct accumulate : std::integral_constant<int, AccTag<index_list<args...>, sizeof...(args)-1>::value> {}; template <typename T, typename U> struct concat_list; template <int... i, int... j> struct concat_list<index_list<i...>,index_list<j...>> : index_list<i..., (sizeof...(i)+i)..., (2*sizeof...(i)+j)...> {}; template <int N> struct make_index_list : concat_list<typename make_index_list<N/2>::type, typename make_index_list<N%2>::type> {}; template <> struct make_index_list<1> : index_list<0> {}; template <> struct make_index_list<0> : index_list<> {}; template <typename T> struct acc; template <int... i> struct acc<index_list<i...>> : accumulate<i...> {}; #include <iostream> int main() { using list = typename make_index_list<LENGTH>::type; std::cout << sizeof(list) << '\t'; using result = acc<list>; std::cout << result::value << '\t'; }Mal schnell getestet mit
for ((x=100;x<20000;x+=x/2)) do y=`/usr/bin/time -f "utime=%U res=%M" 2>&1 g++-4.8.1 -std=c++0x test.cpp -DLENGTH=$x -ftemplate-depth=20000`; echo X=$x $y ; doneergibt ohne die letzten beiden Zeilen in main (res in kB, auf i7 2640m)
X=100 utime=0.18 res=136304 X=150 utime=0.18 res=137136 X=225 utime=0.19 res=137408 X=337 utime=0.19 res=139360 X=505 utime=0.19 res=141200 X=757 utime=0.21 res=147248 X=1135 utime=0.21 res=166256 X=1702 utime=0.25 res=179472 X=2553 utime=0.34 res=235424 X=3829 utime=0.52 res=314400 X=5743 utime=0.92 res=534800 X=8614 utime=1.73 res=1034864 X=12921 utime=3.60 res=2025968 X=19381 utime=7.49 res=4211920und mit accumulate
X=100 utime=0.19 res=140400 X=150 utime=0.20 res=144560 X=225 utime=0.21 res=149936 X=337 utime=0.23 res=163872 X=505 utime=0.29 res=186240 X=757 utime=0.41 res=251088 X=1135 utime=0.68 res=358032 X=1702 utime=1.30 res=576096 X=2553 utime=2.61 res=1117840 X=3829 utime=6.07 res=2158192 X=5743 utime=12.86 res=4598640 X=8614 utime=29.15 res=9429120 X=12921 utime=71.27 res=22378096Das ist klar exponentielles Wachstum, was ich nicht haben möchte.
Mit clangX=100 utime=0.21 res=103904 X=150 utime=0.22 res=106256 X=225 utime=0.21 res=109744 X=337 utime=0.23 res=115088 X=505 utime=0.23 res=122848 X=757 utime=0.25 res=134752clang steigt bei einer Tiefe über 1000 mit einem Segfault aus, aus den ermittelten Zahlen ist leider keine Tendenz klar erkennbar.
Ich bin kein Linux-Experte, habe ausser Gentoo nur vor Jahren mal wenige andere angetestet.
-
Ich sehe. Wie ist übrigens
template<int ... args> struct accumulate : std::integral_constant<int, boost::mpl::plus<boost::mpl::int_<args>...>::value> {};?
Btw: schöne
make_indices-Implementation.
-
Sone schrieb:
Ich sehe. Wie ist übrigens
template<int ... args> struct accumulate : std::integral_constant<int, boost::mpl::plus<boost::mpl::int_<args>...>::value> {};?
100x schlechter. mpl verwendet ja keine varidic Templates.
#define BOOST_MPL_CFG_NO_PREPROCESSED_HEADERS 1 #define BOOST_MPL_LIMIT_METAFUNCTION_ARITY LENGTH #include <type_traits> #include <boost/mpl/plus.hpp> #include <boost/mpl/int.hpp> template<int ... args> struct index_list { static constexpr int arr[]{args...}; using type = index_list; }; template<int ... args> struct accumulate : std::integral_constant<int, boost::mpl::plus<boost::mpl::int_<args>...>::value> {}; template <typename T, typename U> struct concat_list; template <int... i, int... j> struct concat_list<index_list<i...>,index_list<j...>> : index_list<i..., (sizeof...(i)+i)..., (2*sizeof...(i)+j)...> {}; template <int N> struct make_index_list : concat_list<typename make_index_list<N/2>::type, typename make_index_list<N%2>::type> {}; template <> struct make_index_list<1> : index_list<0> {}; template <> struct make_index_list<0> : index_list<> {}; template <typename T> struct acc; template <int... i> struct acc<index_list<i...>> : accumulate<i...> {}; #include <iostream> int main() { using list = typename make_index_list<LENGTH>::type; std::cout << sizeof(list) << '\t'; using result = acc<list>; std::cout << result::value << '\t'; }ergibt bei mir mit gcc 4.8.1
X=10 utime=0.36 res=234272 X=15 utime=0.46 res=321376 X=22 utime=0.63 res=388784 X=33 utime=1.02 res=535216 X=49 utime=1.88 res=1112064 X=73 utime=3.79 res=2911728 X=109 utime=8.07 res=4516576 X=163 utime=18.18 res=13134112 X=244 utime=44.89 res=22324720
-
Ich verstehe immer noch nicht. Du willst also, dass ich eine Schleife auf eine pack-expansion "reduziere"?
-
camper schrieb:
Es wird immer noch die Liste bei jedem Schritt mitgechleppt.
Das ist nicht das Problem.
template<int ... args> struct accumulate { static constexpr int arr[]{args...}; static constexpr int compute_acc( int start_index ) { return (start_index == -1 ? 0 : compute_acc(start_index-1) + arr[start_index] ); } static constexpr auto value = compute_acc(sizeof...(args)-1); };Überhaupt, irgendwo muss es ja die Rekursion geben - das meintest du selbst. Und da ist die Rekursion.
-
Sone schrieb:
Überhaupt, irgendwo muss es ja die Rekursion geben - das meintest du selbst. Und da ist die Rekursion.
Um die Indexliste zu erzeugen.
template<int ... args> struct index_list { static constexpr int arr[]{args...}; static constexpr int size = sizeof...(args); using type = index_list; }; template <typename T, typename U> struct concat_list; template <int... i, int... j> struct concat_list<index_list<i...>,index_list<j...>> : index_list<i..., (sizeof...(i)+i)..., (2*sizeof...(i)+j)...> {}; template <int N> struct make_index_list : concat_list<typename make_index_list<N/2>::type, typename make_index_list<N%2>::type> {}; template <> struct make_index_list<1> : index_list<0> {}; template <> struct make_index_list<0> : index_list<> {}; template <int i> struct rack { int v; }; template <typename T, typename = typename make_index_list<T::size>::type> struct acc; template <int... i, int... j> struct acc<index_list<i...>, index_list<j...>> : rack<-1>, rack<j>... { constexpr acc() : rack<-1>{0}, rack<j>{rack<j-1>::v+i}... {} static const int value; }; template <int... i, int... j> const int acc<index_list<i...>, index_list<j...>>::value = acc().rack<sizeof...(j)-1>::v;
-
rack<-1>{0}, rack<j>{rack<j-1>::v+i}...Natürlich...
Ja, das ist schön. Damit kann man alles schöner schreiben, auch partial_sum und wahrscheinlich auch multiply...Und welche ist jetzt schneller?
Edit: Das hätte ich doch sehen müssen...:
template <typename V> struct fib; template <typename T, T... i> struct fib<value_list<T, i...>> : tagged_value<0,T>, tagged_value<1,T>, tagged_value<j+2, T>... { constexpr partial_sum() : tagged_value<0,T>(0), tagged_value<1,T>(1), tagged_value<j+2, T>( tagged_value<j,T>::v + tagged_value<j+1,T>::v )... {} };Mit dem Trick lässt sich alles schöner schreiben.
-
Sone schrieb:
Und welche ist jetzt schneller?
clang steigt bei großer constexpr-Rekursionstiefe aus
Mit g++ 4.8.1 erhalte cih für die constexpr-Variante (jweils wieder mit und ohne Instantiierung von accumulate, die Differenz der Werte ist von Interesse).X=100 utime=0.19 res=137856 utime=0.18 res=136272 X=150 utime=0.18 res=140576 utime=0.18 res=136800 X=225 utime=0.19 res=142240 utime=0.18 res=137136 X=337 utime=0.19 res=152496 utime=0.17 res=139168 X=505 utime=0.20 res=160896 utime=0.18 res=141216 X=757 utime=0.21 res=188928 utime=0.19 res=147392 X=1135 utime=0.25 res=265072 utime=0.21 res=166000 X=1702 utime=0.31 res=381856 utime=0.25 res=179472 X=2553 utime=0.42 res=661360 utime=0.33 res=235328 X=3829 utime=0.71 res=1253536 utime=0.49 res=314448 X=5743 utime=1.28 res=2683328 utime=0.87 res=534880 X=8614 utime=2.57 res=5789856 utime=1.67 res=1034896 X=12921 utime=5.42 res=12672320 utime=3.46 res=2026080Mein Code:
X=100 utime=0.19 res=146224 utime=0.19 res=136416 X=150 utime=0.21 res=151648 utime=0.18 res=137024 X=225 utime=0.23 res=158432 utime=0.19 res=137440 X=337 utime=0.26 res=170960 utime=0.19 res=139360 X=505 utime=0.30 res=186384 utime=0.19 res=141632 X=757 utime=0.39 res=217072 utime=0.20 res=147888 X=1135 utime=0.59 res=269824 utime=0.22 res=166928 X=1702 utime=1.02 res=331872 utime=0.25 res=180640 X=2553 utime=1.85 res=464032 utime=0.33 res=236896 X=3829 utime=3.73 res=654192 utime=0.49 res=316448 X=5743 utime=7.71 res=1068160 utime=0.86 res=538016 X=8614 utime=16.63 res=1723568 utime=1.67 res=1038784 X=12921 utime=36.29 res=3103984 utime=3.45 res=2031216Schön linearer Speicherbedarf, dafür unterirdische Geschwindgikeit.
Mal noch zum Vergleich
template <typename T, typename = typename make_index_list<T::size/2>::type, typename = typename make_index_list<T::size%2>::type> struct acc; template <typename T, int... j, int... k> struct acc<T, index_list<j...>,index_list<k...>> : std::integral_constant<int, acc<index_list<T::arr[j]...>>::value+ acc<index_list<T::arr[sizeof...(j)+j]...>>::value+ acc<index_list<T::arr[2*sizeof...(j)+k]...>>::value> {}; template <> struct acc<index_list<>,index_list<>,index_list<>> : std::integral_constant<int,0> {}; template <int i> struct acc<index_list<i>,index_list<>,index_list<0>> : std::integral_constant<int,i> {};mit gcc 4.8.1
X=100 utime=0.22 res=158560 utime=0.19 res=136432 X=150 utime=0.24 res=181152 utime=0.18 res=137120 X=225 utime=0.27 res=199040 utime=0.18 res=137584 X=337 utime=0.33 res=275280 utime=0.19 res=139648 X=505 utime=0.40 res=340256 utime=0.19 res=141584 X=757 utime=0.60 res=577792 utime=0.21 res=147968 X=1135 utime=1.01 res=1143088 utime=0.22 res=166976 X=1702 utime=1.49 res=1855312 utime=0.26 res=180672 X=2553 utime=2.76 res=3846768 utime=0.33 res=236960 X=3829 utime=4.84 res=7397792 utime=0.56 res=316496 X=5743 utime=9.71 res=16665488 utime=0.88 res=538240also schlechter als die anderen Varianten, wobei sich das sicher etwas verbessern ließe, wenn ein größerer Faktor als 2 verwendet wird.
Mein Code+clang
X=100 utime=0.21 res=102400 utime=0.21 res=99744 X=150 utime=0.22 res=103904 utime=0.21 res=100000 X=225 utime=0.23 res=106128 utime=0.21 res=100256 X=337 utime=0.25 res=109456 utime=0.20 res=100800 X=505 utime=0.30 res=114416 utime=0.20 res=101488 X=757 utime=0.37 res=122064 utime=0.21 res=102560 X=1135 utime=0.60 res=133344 utime=0.22 res=104256 X=1702 utime=1.11 res=150368 utime=0.22 res=106480 X=2553 utime=2.12 res=175776 utime=0.22 res=110112 X=3829 utime=5.27 res=214336 utime=0.23 res=115200 X=5743 utime=13.16 res=272384 utime=0.25 res=123888 X=8614 utime=35.95 res=360032 utime=0.27 res=135584 X=12921 utime=87.30 res=491056 utime=0.29 res=155376 X=19381 utime=193.86 res=685744 utime=0.33 res=184624 X=29071 utime=456.63 res=982000 utime=0.40 res=220384 X=43606 utime=1012.73 res=1421840 utime=0.47 res=290624 X=65409 utime=2432.28 res=2085632 utime=0.64 res=384944 X=98113 utime=5285.76 res=3058112 utime=0.82 res=520656Die letzte Variante mit clang
X=100 utime=0.24 res=107776 utime=0.21 res=99792 X=150 utime=0.26 res=113488 utime=0.20 res=100032 X=225 utime=0.28 res=118176 utime=0.20 res=100336 X=337 utime=0.34 res=130624 utime=0.20 res=100816 X=505 utime=0.37 res=141056 utime=0.21 res=101488 X=757 utime=0.50 res=168128 utime=0.21 res=102592 X=1135 utime=0.71 res=216352 utime=0.21 res=104272 X=1702 utime=0.91 res=255440 utime=0.22 res=106528 X=2553 utime=1.45 res=357184 utime=0.22 res=110176 X=3829 utime=2.10 res=448848 utime=0.22 res=115200 X=5743 utime=3.75 res=675040 utime=0.24 res=124176 X=8614 utime=7.12 res=1074704 utime=0.26 res=136624 X=12921 utime=12.10 res=1403328 utime=0.29 res=155696 X=19381 utime=24.27 res=2250448 utime=0.32 res=184448 X=29071 utime=47.27 res=3022464 utime=0.39 res=221776 X=43606 utime=103.71 res=4885888 utime=0.48 res=291088 X=65409 utime=225.23 res=6634432 utime=0.65 res=386064 X=98113 segfault
-
Ja, diese Aufteil-Variante hätte ich auch noch vorgeschlagen.
Schön linearer Speicherbedarf, dafür unterirdische Geschwindgikeit.
Ja, aber wo denkst du überschneiden sich die Geschwindigkeiten?
Im Übrigen stelle ich in Frage, ob diese - obgleich schöne Variante - überhaupt richtig ist.
valueist nämlich nichtconstexpr.
Ich habe dieselbe Form von Lösung schon gefunden, sie aber abgetan, weil sie ja nicht constexpr ist...template<int ... args> struct index_list { static constexpr int size = sizeof...(args); using type = index_list; }; template <typename T, typename U> struct concat_list; template <int... i, int... j> struct concat_list<index_list<i...>,index_list<j...>> : index_list<i..., (sizeof...(i)+i)..., (2*sizeof...(i)+j)...> {}; template <int N> struct make_index_list : concat_list<typename make_index_list<N/2>::type, typename make_index_list<N%2>::type> {}; template <> struct make_index_list<1> : index_list<0> {}; template <> struct make_index_list<0> : index_list<> {}; template<typename T, typename = typename make_index_list<T::size>::type> // Der Trick mit dem default-Argument ist auch gut. struct accumulate; template<int ... args, int ...ind> struct accumulate<index_list<args...>, index_list<ind...>> { static const int arr[]; static const int value; }; template<int ... args, int ... ind> const int accumulate<index_list<args...>, index_list<ind...>>::arr[]{ 0, (arr[ind] + args)... }; // Bei initializer-lists ist die Auswertungsreihenfolge definiert. template<int ... args, int ... ind> const int accumulate<index_list<args...>, index_list<ind...>>::value = arr[sizeof...(args)]; // ... und value ist nicht constexpr.