A little template challenge
-
Ich arbeite gerade an Metafunktorkomposition. Wobei ich ein Funktor wie folgt definiere:
struct Functor { template <typename...> struct apply { using type = *; }; };Meine aktuelle Syntax ist die Folgende:
using newFunctor = compose <mul /* mul = ein functor der 2 Argumente akzeptiert */, compose <add, mplex::placeholders::_1, int_ <7> >, compose <div, mplex::placeholders::_3, mplex::placeholders::_4> >; using result = newFunctor::template apply <int_ <1>, int_ <3>, int_<4>>;mit
template <unsigned V> struct _ { // darf völlig geändert werden. constexpr static const unsigned value = V; }; #define PLACEHOLDER_SHORT_HAND(NUM) \ using _ ## NUM = _<NUM> PLACEHOLDER_SHORT_HAND(1);Die Implementierung von compose habe ich noch nicht fertig, und bevor ich weiter mache, suche ich lieber:
-
Eine möglicherweise bessere Syntax für die Komposition. Die aktuelle gefällt mir schon eigentlich ganz gut, aber Vorschläge sind willkommen
-
Ein mögliche effiziente Implementierung. Effizient definiere ich fast ausschließlich über Instantiierungstiefe.
Boost MPL ist erlaubt, das kann ich umübersetzen.
Vielleicht hat jemand Interesse sich darüber Gedanken zu machen. Ist eben nicht so eine "normale" Frage wie (Ich will XY, wie mach ich das?)
EDIT:
bind ist übrigens schon fertig und funktioniert so ähnlich, nur nicht rekursiv und lazy.using doublify = bind <mul, int_ <2>, _1>; std::cout << doublify <int_<4>>; // -> 8
-
-
Was ist mit folgender Syntax?
using newFunctor = decltype((_1 + int_<7>())*(_2/_3))::type; auto constexpr result = eval(newFunctor{}, std::make_tuple(1, 6, 3)); static_assert(result == (1+7)*(6/3), "");resultwird zur Compilezeit berechnet, da esconstexprist. Es ist auch möglich,newFunctormit Klassen zu verwenden, die nicht in ein Template reinpassen, wie z.B. double:auto constexpr result = eval(newFunctor{}, std::make_tuple(1.0, 1, 7.0));Das geht mit deinem System nicht.
Code (ist zwar C++14, sollte aber relativ leicht in C++11 übersetzbar sein):
#include <type_traits> #include <tuple> namespace mplex { template <std::size_t N> struct placeholder { static constexpr std::size_t value = N; template <typename T> friend auto constexpr eval(placeholder, T&& tuple) { return std::get<N>(std::forward<T>(tuple)); } }; using p1 = placeholder<0>; using p2 = placeholder<1>; using p3 = placeholder<2>; template <typename T> struct expression_template { using type = T; }; auto _1 = expression_template<p1>{}; auto _2 = expression_template<p2>{}; auto _3 = expression_template<p3>{}; template <int Value> expression_template<std::integral_constant<int, Value> > int_(); template <typename T, T Value, typename X> auto constexpr eval(std::integral_constant<T, Value>, X&&) { return Value; } template <typename L, typename R> struct mul { template <typename T> friend auto constexpr eval(mul, T&& tuple) { return eval(L{}, tuple)*eval(R{}, tuple); } }; template <typename A, typename B> auto operator*(expression_template<A>, expression_template<B>) -> expression_template<mul<A, B>>; template <typename L, typename R> struct add { template <typename T> friend auto constexpr eval(add, T&& tuple) { return eval(L{}, tuple)+eval(R{}, tuple); } }; template <typename A, typename B> auto operator+(expression_template<A>, expression_template<B>) -> expression_template<add<A, B>>; template <typename L, typename R> struct div { template <typename T> friend constexpr auto eval(div, T&& tuple) { return eval(L{}, tuple)/eval(R{}, tuple); } }; template <typename A, typename B> auto operator/(expression_template<A>, expression_template<B>) -> expression_template<div<A, B>>; } // namespace mplex int main() { using namespace mplex; using newFunctor = decltype((_1 + int_<7>())*(_2/_3))::type; auto constexpr result = eval(newFunctor{}, std::make_tuple(1, 6, 3)); static_assert(result == (1+7)*(6/3), ""); }
-
Die Syntax lässt sich noch weiter verfeinern:
template <typename T, typename... Args> auto constexpr apply(Args&&... args) { return eval(typename T::type{}, std::make_tuple(std::forward<Args>(args)...)); } // und dann: using newFunctor = decltype((_1 + int_<7>())*(_2/_3)); auto constexpr result = apply<newFunctor>(1.0, 1, 7.0);
-
Ich würde vorschlagen, statt diesem
struct _Hack user-defined literals zu verwenden.
-
dot schrieb:
Ich würde vorschlagen, statt diesem
struct _Hack user-defined literals zu verwenden.+1
Dann sind wir beidecltype((_1 + 7_ce)*(_2/_3));
-
Grundsätzlich erstmal: Mir gefällt wie es funktioniert und es erinnert mich an boost lambda.
Die ganze Sache müsste aber auf Typen operieren, nicht auf Werten und mehr als alle Operatoren verstehen.Jetzt habe ich zum Beispiel Haufen structs die gewissen Kram machen, zum Beispiel im Einsatz als Predikate in Metafunktionen (also als Template Argumente)
Wieder sehen die so aus (oder ähnlich | Parameteranzahl variadisch):
struct someFunctionality { template <typename T, typename U, typename W> struct apply { using type = result; /* whatever*/ }; };Das ist ein wiederkehrendes Muster.
Der "Nutzer" könnten nun weiter Funktoren gleicher Art hinzufügen.
Welche dann zusammengesetzten werden können sollen.
Am besten so, dass man die nicht alle anpassen muss oder einen Haufen "Peripheriecode" schreiben, damit sie "verstanden" werden. (Außer vllt mit Macros, oder Vererbung).Vorausgesetzt ich habe dein Code lang genug angesehen und ausreichend verstanden.