Berechnung ueber verschiedene Template Instantiierungen moeglich?



  • Interessante Angelegenheit. Das Problem liegt in der funktionalen Natur der Templatemetaprogrammierung, bei der Werte (und Typen) immer Konstanten sind und somit nicht verändert werden können. Sowas wie eine globale Variable, welche die Ressourcen zählt, ist zur Kompilierzeit also nicht möglich. Man muss also irgendwie den momentanen Status an das Template übergeben, jetzt gehts darum, das möglichst elegant durchzuführen.

    Genau deswegen funktioniert auch unskilled's Code nicht. 😉

    Ich überlege mir noch etwas, vielleicht bringt man was halbwegs Vertretbares zu Stande. Aber ich mach mir etwas Sorgen, wenn der Code in mehreren Modulen verwendet wird, da ist die Metaprogrammierung (auch hinsichtlich Präprozessor) schnell machtlos...



  • An diese Zähltechnik dachte ich. Geht aber nicht innerhalb der Funktion.

    #include <iostream>
    #include <cstring>
    using namespace std;
    
    template<bool b>
    struct static_assert_helper;
    template<>
    struct static_assert_helper<true>{
    	typedef int x;
    };
    #define STATIC_ASSERT(b) typedef static_assert_helper<(b)>::x sa
    
    template<int buchungsnummer>
    struct LaufendeSumme{
    	static int const summe=LaufendeSumme<buchungsnummer-1>::summe;
    };
    template<>
    struct LaufendeSumme<0>{
    	static int const summe=0;
    };
    #define BUCHUNG(x)\
    template<>\
    struct LaufendeSumme<__LINE__>{\
    	static int const summe=LaufendeSumme<__LINE__-1>::summe+x;\
    };
    #define SUMME (LaufendeSumme<__LINE__>::summe)
    
    BUCHUNG(100);STATIC_ASSERT(SUMME>=0);
    BUCHUNG(-50);STATIC_ASSERT(SUMME>=0);
    BUCHUNG(-50);STATIC_ASSERT(SUMME>=0);
    //BUCHUNG(-1);STATIC_ASSERT(SUMME>=0);
    
    int main(){
    }
    


  • stimmt - gar nicht dran gedacht 😣

    mit boost::mpl::vector könnte es gehen - aber da hab ich zu wenig ahnung, um iwas zu versuchen^^



  • Wenn ich jedoch zu viele Komponenten in einem Programm instantiiere, soll der Compiler bereits die Uebersetzung verweigern. Folglich soll das Problem nicht erst zur Laufzeit erkannt werden.

    Instantiierung von Objekten erfolgt zur Laufzeit und kann nicht in allen Faellen auf die Kompilierung geschoben werden. Daraus folgt: Geht nicht mit Templates!



  • volkard hat einen schönen Ansatz gebracht, der sogar mit einem einzelnen Makro umgesetzt werden kann:

    #define PRUEFE_BUCHUNG(x) BUCHUNG(x); STATIC_ASSERT(SUMME>=0);
    

    Die gewünschte Flexibilität erreicht man damit leider trotzdem nicht. Zudem kann relativ schnell eine grosse Rekursion entstehen, die den Compiler erschöpft. Ich habe selbst ansatzweise etwas probiert, doch ich denke, der dafür notwendige Aufwand steht in keinstem Verhältnis zum Nutzen. Wirklich viele Möglichkeiten stehen nicht offen, da innerhalb von Funktionen keine Templatedefinitionen erlaubt sind (somit besteht keine Möglichkeit, über Templatemetaprogrammierung gleich bei der Deklaration eines Objekts die Ressourcen zu testen).

    Der Präprozessor wäre die Ausweichmöglichkeit, aber dann hat man ständig die Statements von volkards erstem Vorschlag im Code (Präprozessoranweisungen selbst kann man ja nicht in Makros verpacken). An sich innovative Hacks, die ausnutzen, dass das __LINE__ -Makro sich von selbst ändert, sind trotz allem recht stark eingeschränkt. Ich würde sagen, am besten ist es, du prüfst die Ressourcenbedingung doch erst zur Laufzeit oder übergibst zur Not jeweils das zuletzt instanziierte Template. Du solltest nämlich auch überlegen, was du durch die Prüfung vor dem Programmstart tatsächlich gewinnst.

    Eine Kompilierzeit-Prüfung kann bei diesem Beispiel sowieso nur sinnvoll funktionieren, wenn ein einziger Ablaufpfad ohne Verzweigungen oder Schleifen besteht. Die Logik von Kontrollstrukturen ist zur Kompilierzeit schliesslich nicht nutzbar. In diesem Falle eines eindeutigen Programmverlaufs hat aber auch die Laufzeitprüfung keine wirklichen Nachteile - eine Assertion wird mit Sicherheit erreicht. Die Probleme von Prüfungen, die erst zur Laufzeit durchgeführt werden, sind ja oftmals die vielen Testfälle und möglichen Ablaufpfade. Gerade diesbezüglich würde man durch eine Kompilierzeitprüfung nichts gewinnen, könnte Laufzeitmöglichkeiten jedoch mit geringem Aufwand voll ausnutzen.



  • Wenn man für neue Objekte Blöcke aufmachen darf,

    #include <iostream>
    #include <cstring>
    using namespace std;
    
    template<bool b>
    struct static_assert_helper;
    template<>
    struct static_assert_helper<true> {
    	typedef int x;
    };
    #define STATIC_ASSERT(b) typedef static_assert_helper<(b)>::x sa
    
    template < int r >
    struct Component {
    };
    
    template<int _tiefe,int _summe>
    struct status {
    	static int const tiefe=_tiefe;
    	static int const summe=_summe;
    };
    typedef status<0,100> aktuell;
    
    #define COMPONENT(x)\
    typedef status<aktuell::tiefe+1,aktuell::summe-x> aktuell;\
    STATIC_ASSERT(aktuell::summe>=0);\
    Component<x>
    
    int main() {
    	COMPONENT(50) c1;
    	{
    		COMPONENT(50) c2;
    		{
    			COMPONENT(1) c3;
    		}
    	}
    	return 0;
    }
    


  • Die Loesungen von Volkard finde ich wirklich sehr gut und ansonsten, wie bereits schon gesagt wurde, werde ich wohl nicht um das Uebergeben der Vorgaengertypen herumkommen.

    Kurz noch eine Erklaerung dazu warum ich dies zur Uebersetzungszeit machen moechte. Der Code laeuft auf einem eingebetteten System mit sehr limitierten Debugmoeglichkeiten und daher moechte ich so viele "Fehler" wie moeglich bereits zur Uebersetzungszeit entdecken.

    Wenn noch jemandem spontan etwas supertolles einfallen sollte, waere ich natuerlich erfreut, ansonsten
    Herzlichen Dank euch allen



  • Testen kannst du doch auf den grossen Rechner.



  • mysterio schrieb:

    Wenn noch jemandem spontan etwas supertolles einfallen sollte, waere ich natuerlich erfreut

    Nichts supertolles, aber kann dein Compiler gut optimieren? Falls nicht, hast du unter Umständen recht viel Code durch die Templates. Wird wahrscheinlich kein Problem darstellen, aber falls doch, könnte es angesichts hoher Zeilenzahlen und der Verwendung auf Embedded-Systemen durchaus einen Faktor ausmachen.

    Aber eben: Brauchst du das nur in solch trivialen Ablaufpfäden? Ressourcen nur bedingt anfordern oder sowas geht dann nämlich schon mal nicht.



  • unskilled schrieb:

    mit boost::mpl::vector könnte es gehen - aber da hab ich zu wenig ahnung, um iwas zu versuchen^^

    Vermutlich könnte man ein statisches accumulate zusammefrickeln und das über einen mpl::vector laufen lassen, der die components beinhaltet.

    struct Dummy {};
    template <class FirstIter, class LastIter = Dummy>
    struct accumulate; //erstmal nur deklaration
    
    template <class MplSequence>
    struct accumulate<MplSequence, Dummy> //spezialisierung für sequenzen
        : public accumulate<boost::begin<MplSequence>::type,
                            boost::end<MplSequence>::type>
    {};
    
    template <class Iter>
    struct accumulate<Iter, Iter> //spezialisierung für leere Range
       : public boost::mpl::int_<0>
    {};
    
    template <class FirstIter, class LastIter>
    struct accumulate //definition allgemeiner Fall
      : public boost::mpl::plus<
                       boost::mpl::deref<FirstIter>::type,        
                       accumulate<boost::mpl::next<FirstIter>::type, LastIter>    
                               >::type
    {};
    

    sollte mit einer boost::mpl-Sequence (z.B. vector) funktionieren, deren Elemente alle das Konzept der Integral Constant erfüllen. Ergebnis ist wieder ein Integral Constant.

    Beispiel für deine Anwendung:

    template <int r>
    struct Component : public boost::mpl::int_<r>
    {};
    
    template <class container, int max>
    struct checkLessThan
    {
      typedef accumulate<container> sum_t;
      BOOST_STATIC_ASSERT(sum_t::value <= max);
    };
    
    struct checkLessThan<
       boost::mpl::vector<
          Component<50>,
          Component<50>,
          Component<1>
       >,   
       100>;
    

    Wie immer ohne gewähr auf korrektheit, hab grad kein boost zur Hand und ist nur fix ausm Ärmel geschüttelt.



  • An eine boost::mpl Umsetzung hatte ich auch schon gedacht, welches im Prinzip ja equivalent zu meinem ersten Beispiel ist, denn dort sind alle Typen halt bekannt. Ich hatte dies der Einfachheit halber auf mehrere Template-Instantiierungen verteilt. Es ist nun eins die Typen dort zu checken, aber dann weiss ich immer noch nicht, ob nicht vielleicht eine Komponente mehrfach mit ein und dem selben Typ erzeugt wird. Wuerde ja dann moeglicherweise einen Fehler darstellen. Daher finde ich die Variante von Volkard an dieser Stelle ein wenig im Vorteil, wo gleichzeitig die Instantiierung der Objekte sich anschliesst. Ein Kombination aus beiden waere vielleicht hier von Vorteil.

    Nochamls Danke fuer eure Hilfe 😃


Anmelden zum Antworten