brainfuck-Compiler TMP style



  • Hacker schrieb:

    Tja, schon ein wenig weitergekommen. Aber eigentlich sehe ich so etwas wie temp-programmierung als unnötig - ich mein', wozu brauchst du es in Applikationen? :p Oder es ist einfach lustig, sich in sowas auszukennen 😃

    TMP an sich würde ich nicht als unnötig sehen. Der größte Vorteil ist wohl, dass man mit etwas Getrickse und Operatorüberladungen Dinge bauen kann, die man in der Sprache sonst nur schwer ausdrücken konnte. (Lambdas, Expression Templates, ...)
    Aber die Reinform die hier präsentiert wird ist natürlich völlig sinnlos, abgesehen vom Lerneffekt vielleicht, aber erstmal ist es nur Spaß an den vielen <<<<...>...>...>...> 😉





  • camper, Aufgabe 3 hab ich irgendwie gelöst 😃

    **3:
    **

    template<int...a>
    int F()
    {
          typedef decltype(std::make_tuple(a...)) tuple_type;
          static tuple_type const tuple(std::make_tuple(a...));
          static constexpr size_t s = std::tuple_size<tuple_type>::value;
          static int rval = std::get<s - 1>(std::make_tuple(a...));
          return rval;
    }
    

    Ich hab mal die Aufgabe zu optimieren in meine Hand genommen.

    Edit: PI würde mich für diese Lösung ohrfeigen.



  • so ist das nicht richtig. TMP-Funktionen sind Klassen:

    template<int ...a>
    struct F{
        static const int value=...;
    };
    


  • otze schrieb:

    so ist das nicht richtig. TMP-Funktionen sind Klassen:

    template<int ...a>
    struct F{
        static const int value=...;
    };
    

    Same story:

    template<int...a>
    struct F
    {
          typedef decltype(std::make_tuple(a...)) tuple_type;
          static tuple_type const tuple;
          static constexpr size_t s = std::tuple_size<tuple_type>::value;
          static int const value;
    };
    
    template<int...a>
    typename F<a...>::tuple_type const F<a...>::tuple(a...);
    template<int...a>
    int const F<a...>::value = std::get<s - 1>(tuple);
    


  • Kannst du mir erklären wie das gehen soll?

    F<<5, 5, 5>, 8>
    

    Wie soll man das deklarieren, damit das so verschachtelt werden kann?



  • Hacker schrieb:

    Kannst du mir erklären wie das gehen soll?

    F<<5, 5, 5>, 8>
    

    Wie soll man das deklarieren, damit das so verschachtelt werden kann?

    Gar nicht. Das geht schon eher:

    F< List< 5 , 5 , 5 > , 8 >
    

    oder, je nach context, auch

    F< F< 5 , 5 , 5 > , 8 >
    


  • Ah, alles klar. Das erinnert mich ein wenig an Typlisten, dein letztes Beispiel (auch wenn variadic templates sie natürlich überflüssig machen).


  • Mod

    Hacker schrieb:

    Ah, alles klar. Das erinnert mich ein wenig an Typlisten, dein letztes Beispiel (auch wenn variadic templates sie natürlich überflüssig machen).

    Ja, das ist durchaus kein anderes Problem. Im Übrigen sollen der Code in den Aufgaben nur Pseudo-code darstellen, du kannst dir also z.B. aussuchen, ob die Listen in einem Typ verpackt werden, oder ob es überhaupt ein Klassentemplate sein muss - falls du lieber constexpr-Funktionen verwenden willst: bitte.

    Ein bisschen zur Motivation habe ich einmal experimentiert, wie Listen (0...N-1) am effizientes erzeigt werden können. Das kann zum Beispiel ganz simpel linear gemacht werden:

    template <std::size_t... i> struct index_list {};
    template <std::size_t N, typename L = index_list<>> struct make_index_list;
    template <std::size_t N, std::size_t... i> struct make_index_list<N, index_list<i...>>
    {
        typedef typename make_index_list<N-1, index_list<i..., sizeof...(i)>>::type type;
    };
    

    effizient ist das allerdings nicht. Optimieren könnte man z.B., indem man pro Rekursionsschritt mehr als 1 Element hinzufügt:
    Mit ein bisschen Präprozessormagie also so etwas:

    #include <iostream>
    #include <boost/preprocessor/punctuation/comma_if.hpp>
    #include <boost/preprocessor/repetition/repeat.hpp>
    #include <boost/preprocessor/repetition/enum_params.hpp>
    template <std::size_t... i> struct index_list {};
    template <std::size_t N, typename L = index_list<>> struct make_index_list;
    #define LL(z, n, text) , ( sizeof...(i) + n )
    template <std::size_t N, std::size_t... i> struct make_index_list<N, index_list<i...>>
    {
        typedef typename make_index_list<N-LIST_APPEND, index_list<i... BOOST_PP_REPEAT(LIST_APPEND,LL,)>>::type type;
    };
    #define LLL(z, n, text) \
    template <std::size_t... i> struct make_index_list<n, index_list<i...>> \
    { \
        typedef index_list<BOOST_PP_ENUM_PARAMS(n,) BOOST_PP_COMMA_IF(n) ( n + i )...> type; \
    };
    BOOST_PP_REPEAT(LIST_APPEND,LLL,)
    int main()
    {
        std::cout << sizeof(make_index_list<LIST_LENGTH>::type) << '\n';
    }
    

    Alternativ kann man das Ganze auch durch direkte Teilung erzeugen:

    #include <iostream>
    #include <boost/preprocessor/punctuation/comma_if.hpp>
    #include <boost/preprocessor/repetition/repeat.hpp>
    #include <boost/preprocessor/repetition/enum_params.hpp>
    template <std::size_t... i> struct index_list {};
    template <typename L, typename M> struct repeat;
    #define LL(z, n, text) ( n * sizeof...(i) + i )...,
    template <std::size_t... i, std::size_t...j> struct repeat<index_list<i...>, index_list<j...>>
    {
        typedef index_list<BOOST_PP_REPEAT(LIST_DIV,LL,) ( LIST_DIV * sizeof...(i) + j )...> type;
    };
    template <std::size_t N> struct make_index_list
    {
        typedef typename repeat<typename make_index_list<N / LIST_DIV >::type, typename make_index_list<N % LIST_DIV>::type>::type type;
    };
    #define LLL(z, n, text) \
    template <> struct make_index_list<n> \
    { \
        typedef index_list<BOOST_PP_ENUM_PARAMS(n,)> type; \
    };
    BOOST_PP_REPEAT(LIST_DIV,LLL,)
    int main()
    {
        std::cout << sizeof(make_index_list<LIST_LENGTH>::type) << '\n';
    }
    

    Mit

    for (( x=256; x>0; x/=2)); do MEM=0; for (( y=0; MEM<=25000000; y+=y/10+1 )); do echo -n "${x};${y};"; OUTPUT=`/usr/bin/time -f "%U;%M" -o /dev/stdout  g++-4.7.1 -std=c++0x make_list_linear.cpp -ftemplate-depth=100000 -DLIST_LENGTH=$y -DLIST_APPEND=$x`; MEM=`echo $OUTPUT | awk '{ split($1,FOO,";"); print FOO[2] }'`; echo $OUTPUT; done; done > make_list_linear.csv
    

    usw.
    lässt sich dann eine Auswertung durchführen.
    Man bekommt z.B. heraus, dass clang offenbar erheblich effizienter als g++ mit dem Speicher umgeht (Anm. der gemessene Speicher ist hier nicht der tatsächliche Speicherbrauch, der beträgt nur ungefähr 1/3 des hier gemessenen Wertes). Für die lineare Variante fehlen für kleine N Messungen: clang stürzt bei mir ab einer Rekursionstiefe über 1452 grundsätzlich mit einem segfault ab.
    Interessanterweise verhalten sich g++ und clang völlig anders bei der logarithmischen Variante.


  • Mod

    Als nächstes habe ich mir mal die 4. Aufgabe vorgenommen, also die Erstellung einer Zugriffsfunktion auf das n-te Elemente einer Liste.
    Die Standardbibliothek verfügt bereits über einen einfachen Mechanismus dafür:

    ...
    
    template <typename L, std::size_t N> struct at;
    template <typename T, T... i, std::size_t N> struct at<value_list<T, i...>, N>
    {
        static constexpr T value = std::remove_reference<decltype(std::get<N>(std::tuple<std::integral_constant<T, i>...>()))>::type::value;
    };
    
    int main()
    {
        typedef make_value_list<std::size_t,LENGTH>::type list;
    #define INSTANTIATE(z, n, x) (void)at<list,LENGTH*(256*x+n)/8192>::value;
        BOOST_PP_REPEAT(256,INSTANTIATE,0)  BOOST_PP_REPEAT(256,INSTANTIATE,1)  BOOST_PP_REPEAT(256,INSTANTIATE,2)  BOOST_PP_REPEAT(256,INSTANTIATE,3)
        BOOST_PP_REPEAT(256,INSTANTIATE,4)  BOOST_PP_REPEAT(256,INSTANTIATE,5)  BOOST_PP_REPEAT(256,INSTANTIATE,6)  BOOST_PP_REPEAT(256,INSTANTIATE,7)
        BOOST_PP_REPEAT(256,INSTANTIATE,8)  BOOST_PP_REPEAT(256,INSTANTIATE,9)  BOOST_PP_REPEAT(256,INSTANTIATE,10) BOOST_PP_REPEAT(256,INSTANTIATE,11)
        BOOST_PP_REPEAT(256,INSTANTIATE,12) BOOST_PP_REPEAT(256,INSTANTIATE,13) BOOST_PP_REPEAT(256,INSTANTIATE,14) BOOST_PP_REPEAT(256,INSTANTIATE,15)
    }
    

    Zum Vergleich habe ich Zugriffe auf alle Listenelement instantiiert, denn das dürfte bei typischer Nutzung auch auftreten, dass jedes einzelne Element einer Liste an irgendeiner Stelle der Berechnung benötigt wird.
    Die Liste wird jeweils mit der mit der logarithmischen Variante (DIV=256) aus dem vorherigen Beitrag erzeugt.

    Ein andere einfache Möglichkeit, die Funktion zu implementieren, besteht wie bei make_list darin, jeweils die ersten x Elemente der Liste zu entfernen, sofern der gesuchte Listenindex größer ist.

    template <typename L, std::size_t N> struct at
    {
        static_assert( sizeof(L)==0, "index zu gross!" );
    };
    template <typename T, T i0, T... i, std::size_t N> struct at<value_list<T, i0, i...>, N>
    {
        static const T value = at<value_list<T, i...>, N - 1>::value;
    };
    template <typename T, T i0, T... i> struct at<value_list<T, i0, i...>, 0>
    {
        static const T value = i0;
    };
    

    Allgemeiner:

    template <typename L, std::size_t N> struct at
    {
        static_assert( sizeof(L)==0, "index zu gross!" );
    };
    template <typename T, BOOST_PP_ENUM_PARAMS(SHIFT,T i), T... i, std::size_t N> struct at<value_list<T, BOOST_PP_ENUM_PARAMS(SHIFT,i), i...>, N>
    {
        static const T value = at<value_list<T, i...>, N - SHIFT>::value;
    };
    #define SPECIALIZE1(z, n, test) \
    template <typename T, BOOST_PP_ENUM_PARAMS(BOOST_PP_INC(n),T i), T... i> struct at<value_list<T, BOOST_PP_ENUM_PARAMS(BOOST_PP_INC(n),i), i...>, n> \
    { \
        static const T value = i##n; \
    };
    BOOST_PP_REPEAT(BOOST_PP_DEC(SHIFT),SPECIALIZE1,)
    #define SPECIALIZE2(z, n, test) \
    template <typename T, BOOST_PP_ENUM_PARAMS(SHIFT,T i), T... i> struct at<value_list<T, BOOST_PP_ENUM_PARAMS(SHIFT,i), i...>, n> \
    { \
        static const T value = i##n; \
    };
    BOOST_PP_REPEAT(SHIFT,SPECIALIZE2,)
    

    Schließlich können wir die einzelnen Elemente auch in Klassen verpacken, und diese voneinander ableiten

    template <std::size_t> struct tag {};
    template <typename L, typename J = typename make_value_list<std::size_t, L::size>::type> struct holder;
    template <typename T, T i0, T... i, std::size_t j0, std::size_t... j> struct holder<value_list<T, i0, i...>, value_list<std::size_t, j0, j...>>
        : holder<value_list<T, i...>, value_list<std::size_t, j...>>
    {
        using holder<value_list<T, i...>, value_list<std::size_t, j...>>::get;
        static constexpr T get(tag<j0>) { return i0; }
    };
    template <typename T> struct holder<value_list<T>, value_list<std::size_t>>
    {
        static constexpr T get(...);
    };
    
    template <typename L, std::size_t N> struct at;
    template <typename T, T... i, std::size_t N> struct at<value_list<T, i...>, N>
    {
        static constexpr T value = holder<value_list<T, i...>>::get(tag<N>());
    };
    

    Keine dieser Varianten ist sonderlich performant. Bei Listen, die mehr als wenige hundert Elemente sind, führen sie zu einem enormen Speicher- und Zeitverbrauch. Das ist auch nicht sonderlich überraschend, schließlich sind durch die Rekursion eine Menge an Instantiierungen durchzuführen.

    Kann man es besser machen? z.B. durch Mehrfachvererbung:

    template <std::size_t i, typename T, T v> struct holder_helper
    {
        template <std::size_t j>
        static constexpr typename std::enable_if<i == j, T>::type get() { return v; };
    };
    
    template <typename L, typename J = typename make_value_list<std::size_t, L::size>::type> struct holder;
    template <typename T, T... i, std::size_t... j> struct holder<value_list<T, i...>, value_list<std::size_t, j...>>
        : holder_helper<j, T, i>...{};
    
    template <typename L, std::size_t N> struct at;
    template <typename T, T... i, std::size_t N> struct at<value_list<T, i...>, N>
    {
        static constexpr T value = holder<value_list<T, i...>>::template get<N>();
    };
    

    Diese Variante ist zwar einfach, hat aber einen Schönheitsfehler: sie funktioniert nicht.
    SFINAE kann nicht dazu benutzt werden, um Mehrdeutigkeiten aufzulösen, die entstehen, weil ein Bezeichner in mehreren verschiedenen Basisklassen gefunden wird!

    Wenn es mit Membern nicht geht, müssen friends her (den Compiler dazu zu bringen, die Deklaration zu finden, ist allerdings nicht trivial):

    template <std::size_t N> struct tag {};
    template <std::size_t i, typename T, T v> struct holder_helper
    {
        friend constexpr T get_(tag<i>, ...) { return v; }
    };
    
    template <typename L, typename J = typename make_value_list<std::size_t, L::size>::type> struct holder;
    template <typename T, T... i, std::size_t... j> struct holder<value_list<T, i...>, value_list<std::size_t, j...>>
        : holder_helper<j, T, i>...
    {};
    
    void get_(...);
    template <typename L, std::size_t N> struct at;
    template <typename T, T... i, std::size_t N> struct at<value_list<T, i...>, N>
    {
        static constexpr T value = get_(tag<N>(), holder<value_list<T, i...>>());
    //                                             ^ Trick um holder_helper<...> als assozierte Klasse zu gewinnen
    };
    

    Alle diese Varianten können auch problemlos auf Typlisten umgeschrieben werden. Im Falle von Wertlisten besteht zusätzlich die Möglichkeit, diese Werte einfach in einem Array zu speichern.

    template <typename T, T... i> struct value_list
    {
        static constexpr T data[] = { i... };
    };
    
    ...
    template <typename L, std::size_t N> struct at;
    template <typename T, T...i, std::size_t N> struct at<value_list<T, i...>, N>
    {
        static constexpr T value = value_list<T, i...>::data[N];
    };
    

    Wie zuvor habe ich alle Varianten getestet, Hier gibts die Charts dazu.
    Wiederum gibt es hierbei interessante Unterschiede zwischen beiden Compilern.

    Ich bitte mal um kurze Rückmeldung, ob dieses Thema überhaupt jemanden interessiert...



  • Mich nicht wirklich (im Moment (!) ), aber du bist trotzdem ein Gott 😃



  • camper schrieb:

    Ich bitte mal um kurze Rückmeldung, ob dieses Thema überhaupt jemanden interessiert...

    Definitiv 👍

    Das größte Problem ist vermutlich bei den Meisten, dass sie dir nicht folgen können. Ich muss auch ganz ehrlich zugeben, dass ich das Zeug mit Boost.PP gar nicht mehr verstehe, einfach, weil ich die Bibliothek nicht kenne.



  • Ich bin auch immer noch interessiert.



  • Ein TMP Brainfuck Interpreter ist deutlich "schlimmer" als Brainfuck selbst 😃


  • Mod

    Hier eine neue Variante, die weitgehen ohne Rekursion Rekursion auskommt. Selbst der aktuelle Snapshot von g++ 4.8 (01.07.12) kann damit allerdings nicht umgehen, weil constexpr immer noch nicht ausreichend unterstützt wird:

    struct Foo {
        constexpr Foo() : a(0), b(a) {}
        int a, b;
    };
    
    constexpr auto x = Foo();
    

    ergibt

    g++-4.8.0-alpha20120701 (Gentoo 4.8.0_alpha20120701) 4.8.0-alpha20120701 20120701 (experimental)
    test.cpp: In constructor ‘constexpr Foo::Foo()’:
    test.cpp:2:35: sorry, unimplemented: use of the value of the object being constructed in a constant expression
         constexpr Foo() : a(0), b(a) {}
                                       ^
    test.cpp: At global scope:
    test.cpp:6:24: error: ‘constexpr Foo::Foo()’ called in a constant expression
     constexpr auto x = Foo();
                            ^
    

    (ich mag die neue Fehlerformatierung)

    Vorerst muss also clang herhalten, und das hat seine eigenen Probleme, so dass ich leider keine allzu langen bf-Programme testen kann.

    #include <cstddef>
    #include <cstdio>
    #include <utility>
    
    #include <boost/preprocessor/punctuation/comma_if.hpp>
    #include <boost/preprocessor/repetition/repeat.hpp>
    #include <boost/preprocessor/repetition/enum_params.hpp>
    
    constexpr char source[] = // Hello world von wikipedia
    "+++++ +++++             initialize counter (cell #0) to 10"
    "[                       use loop to set the next four cells to 70/100/30/10"
    "    > +++++ ++              add  7 to cell #1"
    "    > +++++ +++++           add 10 to cell #2"
    "    > +++                   add  3 to cell #3"
    "    > +                     add  1 to cell #4"
    "    <<<< -                  decrement counter (cell #0)"
    "]"
    "> ++ .                  print 'H'"
    "> + .                   print 'e'"
    "+++++ ++ .              print 'l'"
    ".                       print 'l'"
    "+++ .                   print 'o'"
    "> ++ .                  print ' '"
    "<< +++++ +++++ +++++ .  print 'W'"
    "> .                     print 'o'"
    "+++ .                   print 'r'"
    "----- - .               print 'l'"
    "----- --- .             print 'd'"
    "> + .                   print '!'"
    "> .                     print '\n'";
    
    struct Env
    {
        unsigned char data[32768];
        unsigned char* ptr;
    } env = { {}, env.data };
    
    template <typename T, T... i> struct value_list
    {
        typedef T type;
        static constexpr std::size_t size = sizeof...( i );
        static constexpr T data[] = { i... };
    };
    
    #define MAKE_LIST_DIVIDER 256
    
    template <typename L, typename M> struct repeat;
    
    #define MAKE_LIST_EXPAND(z, n, text) ( n * sizeof...(i) + i )...,
    template <typename T, T... i, T... j> struct repeat<value_list<T, i...>, value_list<T, j...>>
    {
        typedef value_list<T, BOOST_PP_REPEAT(MAKE_LIST_DIVIDER, MAKE_LIST_EXPAND,) ( MAKE_LIST_DIVIDER * sizeof...(i) + j )...> type;
    };
    
    template <typename T, std::size_t N> struct make_value_list
    {
        typedef typename repeat<typename make_value_list<T, N / MAKE_LIST_DIVIDER>::type, typename make_value_list<T, N % MAKE_LIST_DIVIDER>::type>::type type;
    };
    
    #define MAKE_LIST_SPECIALIZATION(z, n, text) \
    template <typename T> struct make_value_list<T, n> \
    { \
        typedef value_list<T BOOST_PP_COMMA_IF(n) BOOST_PP_ENUM_PARAMS(n,)> type; \
    };
    BOOST_PP_REPEAT(MAKE_LIST_DIVIDER, MAKE_LIST_SPECIALIZATION,)
    
    /***************************************************************************************************************************/
    
    void at_(...);
    constexpr std::size_t find_next_(...) { return std::size_t(-1); }
    template <std::size_t N> struct tag {};
    
    template <std::size_t N> struct ordered_tag : ordered_tag<N-1> {};
    template <> struct ordered_tag<-1> {};
    // 0-->N   ordered_tag::*
    // N-->0   ordered_tag*
    
    template <std::size_t N, typename T> struct tagged_value
    {
        constexpr tagged_value(T v) : v(v) {}
        T v;
    };
    
    template <typename V, typename I = typename make_value_list<std::size_t, V::size>::type> struct partial_sum;
    template <typename T, T... i, std::size_t... j> struct partial_sum<value_list<T, i...>, value_list<std::size_t, j...>>
        : tagged_value<j, T>...
    {
        constexpr partial_sum() : tagged_value<j, T>( get<j - 1>() + i )... {}
    
        template <std::size_t N, typename = typename std::enable_if<( N != -1 ), T>::type, typename = void>
        constexpr T get() { return tagged_value<N, T>::v; }
    
        template <std::size_t N, typename = typename std::enable_if<( N == -1 ), T>::type>
        constexpr T get() { return 0; }
    };
    
    constexpr int grad(char c) { return c == '[' ? 1 : c != ']' ? 0 : -1; }
    template <typename L = make_value_list<std::size_t, sizeof source>::type> struct get_nesting;
    template <typename T, T... i> struct get_nesting<value_list<T, i...>>
    {
        static constexpr auto tmp = partial_sum<value_list<int, grad(source[i])...>>();
        typedef value_list<T, tmp.template get<i-1>()...> type;
    };
    
    template <std::size_t i, typename T, T v> struct pair
    {
        friend constexpr T at_(tag<i>, const pair&) { return v; }
        friend constexpr std::size_t find_next_(tag<v>, const pair&, int ordered_tag<i>::*) { return i; }
    };
    
    template <typename V, typename I = typename make_value_list<std::size_t, V::size>::type> struct bimap;
    template <typename T, T... i, std::size_t... j> struct bimap<value_list<T, i...>, value_list<std::size_t, j...>>
        : pair<j, T, i>...
    {};
    
    constexpr auto nesting = bimap<get_nesting<>::type>();
    
    constexpr bool execute(...) { return false; }
    
    template <std::size_t start, std::size_t... i>
    bool run(value_list<std::size_t, i...>)
    {
        constexpr int level = at_( tag<start>(), nesting );
        constexpr std::size_t end = find_next_( tag<level-1>(), nesting, static_cast<int ordered_tag<start>::*>( nullptr ) );
        bool isns[] = { execute(tag<source[i]>(),
                                tag<( i >= start ) && ( i < end ) && ( at_( tag<i>(), nesting ) == level )>(),
                                tag<i>())... };
        return false;
    }
    
    bool execute(tag<'>'>, tag<true>, ...) { return ++env.ptr, false; }
    bool execute(tag<'<'>, tag<true>, ...) { return --env.ptr, false; }
    bool execute(tag<'+'>, tag<true>, ...) { return ++*env.ptr, false; }
    bool execute(tag<'-'>, tag<true>, ...) { return --*env.ptr, false; }
    bool execute(tag<','>, tag<true>, ...) { return *env.ptr = std::getchar(), false; }
    bool execute(tag<'.'>, tag<true>, ...) { return std::putchar( *env.ptr ), false; }
    
    constexpr auto index_list = make_value_list<std::size_t, sizeof source>::type();
    
    template <std::size_t N>
    bool execute(tag<'['>, tag<true>, tag<N>)
    {
        while ( *env.ptr )
            run<N+1>(index_list);
        return false;
    }
    
    int main()
    {
        run<0>(index_list);
    }
    

    abgesehen von make_list ist ordered_tag die einzige rekursive Struktur, evtl. kann man das auch noch loswerden, falls die Überladungsauflösung anders erledigt werden kann.



  • camper schrieb:

    Hier eine neue Variante, die weitgehen ohne Rekursion Rekursion auskommt. Selbst der aktuelle Snapshot von g++ 4.8 (01.07.12) kann damit allerdings nicht umgehen, weil constexpr immer noch nicht ausreichend unterstützt wird:[cpp]struct Foo {
    constexpr Foo() : a(0), b(a) {}
    int a, b;
    };

    Wieso geht nicht so ein kleiner dirty hack?

    struct Foo
    {
        constexpr Foo() {}
    
        int a = 0, 
            b = 0;
    };
    

    Edit: sry, aber die CPP-Tags sind heute ziemlich stinkig.


  • Mod

    Sone schrieb:

    camper schrieb:

    Hier eine neue Variante, die weitgehen ohne Rekursion Rekursion auskommt. Selbst der aktuelle Snapshot von g++ 4.8 (01.07.12) kann damit allerdings nicht umgehen, weil constexpr immer noch nicht ausreichend unterstützt wird:

    struct Foo {
        constexpr Foo() : a(0), b(a) {}
        int a, b;
    };
    

    Wieso geht nicht so ein kleiner dirty hack?

    struct Foo
    {
        constexpr Foo() {}
    
        int a = 0, 
            b = 0;
    };
    

    Edit: sry, aber die CPP-Tags sind heute ziemlich stinkig.

    Das ist nicht dirty, allerdings auch nicht das was ich will. Mir geht es darum, einen rekursiven Ausdruck direkt auszuschreiben, effektiv also in eine iterative Form zu überführen (und dadurch Speicher und Zeit zu sparen, weil weniger (bzw. billigere) Instantierungen durchgeführt werden (siehe oben Zeile 88).

    z.B.

    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 )... {}
    };
    

    Ohne constexpr funktioniert das problemlos, das Objekt kann dann aber eben nicht in einem konstanten Ausdruck verwendet werden.
    Eigentlich sollte das auch mit Arrays funktionieren

    constexpr int fib[] = { 0, 1, fib[0]+fib[1], fib[1]+fib[2] /* usw. per packexpansion */ };
    

    Allerdings macht hier nicht einmal clang mit (wiederum: lässt man constexpr weg, gibt es keine Probleme)


Anmelden zum Antworten