Backslash in Makro



  • Du kannst damit einfach defines über mehrere Zeilen verteilen, z.B. sowas:

    #define NONSENS \
    	if(0) {\
    		printf("hab nix zu sagen...")\
    	}
    


  • @matze
    Ja genau deswegen kam diese kleine "Verwechslung" zu stande

    @pumuckl
    Ich habe ein Template welches ich MEHRFACH spazialisere. Für die Spezialisierung habe ich eine Basis für die Gemeinsamkeiten.
    Das Makro nutze ich um die Operatoren der Basis zu nutzen da diese überall gleich sind und ich es für unübersichtlich halte das "using ..." jedes mal abzuschreiben.
    Ich fürchte jetzt hagelt es Gründe warum das grauenhaft ist? 😞



  • markouser schrieb:

    @pumuckl
    Ich habe ein Template welches ich MEHRFACH spazialisere. Für die Spezialisierung habe ich eine Basis für die Gemeinsamkeiten.
    Das Makro nutze ich um die Operatoren der Basis zu nutzen da diese überall gleich sind und ich es für unübersichtlich halte das "using ..." jedes mal abzuschreiben.
    Ich fürchte jetzt hagelt es Gründe warum das grauenhaft ist? 😞

    Naja, Makros verschleiern oft was dahinter steckt, wenns allerdings um viele Zeilen geht, kann man das schonmal machen ( #undef fen des Makros am Ende nicht vergessen). Beim operator/ seh ich das allerdings nicht so recht, da man den eh meist als freie Funktion implementieren sollte. Zeig doch mal einen Ausschnitt mit deiner Basisklasse und einer der Spezialisierungen 🙂



  • pumuckl schrieb:

    markouser schrieb:

    @pumuckl
    Ich habe ein Template welches ich MEHRFACH spazialisere. Für die Spezialisierung habe ich eine Basis für die Gemeinsamkeiten.
    Das Makro nutze ich um die Operatoren der Basis zu nutzen da diese überall gleich sind und ich es für unübersichtlich halte das "using ..." jedes mal abzuschreiben.
    Ich fürchte jetzt hagelt es Gründe warum das grauenhaft ist? 😞

    Naja, Makros verschleiern oft was dahinter steckt, wenns allerdings um viele Zeilen geht, kann man das schonmal machen ( #undef fen des Makros am Ende nicht vergessen). Beim operator/ seh ich das allerdings nicht so recht, da man den eh meist als freie Funktion implementieren sollte. Zeig doch mal einen Ausschnitt mit deiner Basisklasse und einer der Spezialisierungen 🙂

    Ja deswegen (ich trau mich kaum das zu sagen) wird dir nun auch gleich Auffallen dass der Rüchgabetyp der Basis bereits die (einzige) Ableitung ist ... aber es funktioniert so gut! 😃

    template<int op,typename first_t, typename second_t>
    	class term_base
    	{
    			template<typename T>
    		term<PLUS,term<op,first_t,second_t>,T> operator+(const T&);
    
    		template<typename T>
    		term<MINUS,term<op,first_t,second_t>,T> operator-(const T&);
    
    		template<typename T>
    		term<MULT,term<op,first_t,second_t>,T> operator*(const T&);
    
    		template<typename T>
    		term<DIV,term<op,first_t,second_t>,T> operator/(const T&);
    	};
    

    Eine der geplanten 6 Spezialisierungen

    template<typename first_t, typename second_t>
    	struct term<MULT,first_t,second_t> : public term_base<MULT,first_t,second_t>
    	{
    		typedef term_base<MULT,first_t,second_t> BASE;
    		MTL_SYM_USE_BASE_OPERATORS
    
                    //Noch ein paar andere zensierte Sachen  ....
    
    		friend std::ostream& operator<<(std::ostream& stream, const term<MULT,first_t,second_t>& t) {
    			stream << t.m_first << " * " << t.m_second;
    			return stream;
    		}
    	};
    

    Nunja ... wenn du das jetzt so weit zerlegen solltest und sagst dass man das vielleicht schöner mit Polymorphie regeln könnte:
    Ich möchte in diesem Fall auf Speicherverwaltung (new/delete) verzichten



  • Mit freien Operatoren sähe das dann so aus:

    template <class first_t, class second_t>
    term<MULT, first_t, second_t> operator* (first_t const& lhs, second_t const& rhs);
    
    // usw.
    
    template<int OP>
    struct op_traits;
    
    template <int OP, class first_t, class second_t>
    struct term
    {
      //keine basis benoetigt soweit.
      //keine geerbten ops benoetigt
    
      typedef term<OP, first_t, second_t> this_t;
    
      friend std::ostream& operator<<(std::ostream& stream, this_t const& t) 
      {
         stream << t.m_first << " " << op_traits<OP>::operator_char << " " << t.m_second;
         return stream;
      }
    };
    
    template <>
    struct op_traits<MULT>
    {
      const static char operator_char = '*';
    };
    

    Die speziellen Einzelheiten kann man aus dem term-Template in die Traits auslagern, damit wirds noch etwas übersichtlicher.

    zum freien Operator ist noch zu sagen, dass du die ganzen Templates inklusive der freien Operatoren am Besten in einen eigenen Namespace packst, weil die template-operatoren sonst wirklich ALLES multiplizieren was du ihnen vorgibst - im namespace werden sie nur per koenig-Lookup gefunden, d.h. wenn einer der OPeranden auch aus den Namespace kommt.



  • sehr sehr sehr schön.
    Herzlichen Dank! Ich versuche das gleich mal umzusetzen. 🙂



  • Also ich habe damit nun satte 150 Zeilen gespart!! 🙄
    Wegen diesem Ergebnis frage ich daher noch eine Kleinigkeit:

    template<typename T>
    class var
    {
    //Irrelevant
    };
    
    template<typename T>
    class var_ptr
    {
    	var<T>* m_target;
            public:
    	var_ptr(var<T>&);
    
    	T value() const { return m_target->value(); }		
    
    	friend std::ostream& operator<<(std::ostream& stream, const var_ptr<T>& vptr) {
    		stream << vptr.m_target->name();
    		return stream;
    	}
    };
    

    Wenn ich nun 2 var<double> addieren möchte würde ja (laut globalen operatoren)

    term<PLUS,var<double>,var<double>> entstehen.
    

    Term darf aber niemals var direkt beinhalten sondern ausschließlich var_ptr! (Also wenn es sich überhaupt um ein var<T> handelt!) Wie löse ich das am elegantesten? 😕

    Bis jetzt habe ich sowas, aber das ist ja eigendlich die Aufgabe der globalen Operatoren...

    template<typename T> template<typename T2>
    	term<PLUS,var_ptr<T>,T2 > var<T>::operator+(const T2& v){
    		return term<PLUS,var_ptr<T>,T2 >(*this,v);
    	}
    	template<typename T> template<typename T2>
    	term<PLUS,var_ptr<T>,var_ptr<T2> > var<T>::operator+(var<T2>& v){
    		return operator+(var_ptr<T2>(v));
    	}
    

    Wenn ich die global machen möchte bräcute ich dafür 3 Spezialisierungen?!
    var + x
    x + var
    var + var
    oder? 😕



  • markouser schrieb:

    Wenn ich die global machen möchte bräcute ich dafür 3 Spezialisierungen?!
    var + x
    x + var
    var + var
    oder? 😕

    Operatoren/Funktionen kann man leider nicht spezialisieren. Höchstens überladen.
    Ich würds auf den ersten Blick über eine Metafunktion regeln:

    template <class T>
    struct var_wrapper
    {
      typedef T type; //allgemeiner Fall
    };
    template <class W>
    struct var_wrapper<var<W> >
    {
      typedef var_ptr<W> type;
    };
    
    template <class left_t, class right_t>
    term<MULT, typename var_wrapper<left_t>::type, typename var_wrapper<right_t>::type> operator* (left_t const& lhs, right_t const& rhs)
    {
      typedef typename var_wrapper<left_t>::type leftwrap_t;
      typedef typename var_wrapper<right_t>::type rightwrap_t;
      return term<MULT, leftwrap_t, rightwrap_t>( leftwrap_t(lhs), rightwrap_t(rhs) );
    }
    

    sieht hässlich aus, sollte aber funktionieren. lhs wird umgewandelt, und zwar im Normalfall in ein Objekt vom selben Typ, was der Compiler wegoptimieren kann. Ebenso rhs. für var<X> wirds aber in ein var_ptr<X> gewandelt, was der Konstruktor von var_ptr<X> ja möglich macht.

    Das jetzt für jeden operator auszuschreiben könnte aber frickelig werden - was uns wieder zu Makros bringt (einer der, wie ich finde, "erlaubten" Anwendungen von Makros):

    #define TERM_BINARY_OP(opword, op) \
    template <class left_t, class right_t> \
    term< opword , typename var_wrapper<left_t>::type, typename var_wrapper<right_t>::type> \
    operator op (left_t const& lhs, right_t const& rhs) \
    {  \
      typedef typename var_wrapper<left_t>::type leftwrap_t; \
      typedef typename var_wrapper<right_t>::type rightwrap_t; \
      return term< opword , leftwrap_t, rightwrap_t>( leftwrap_t(lhs), rightwrap_t(rhs) ); \
    } 
    
    TERM_BINARY_OP(MULT, *)
    TERM_BINARY_OP(DIV, /)
    TERM_BINARY_OP(PLUS, +)
    TERM_BINARY_OP(MINUS, -)
    
    #undef TERM_BINARY_OP
    


  • AAAlllso
    Ich habe nun versucht das zu übernehmen, aber hier

    return term<MULT, leftwrap_t, rightwrap_t>( leftwrap_t(lhs), rightwrap_t(rhs) );
    

    Kopiert der Wrapper das Objekt in den selben Typ (hier optimiert dann wohl gcc), wenn es ein var<T> ist wird es in var_ptr<T> umgewandelt und dabei var<T> kopiert (oder gecastet??)
    Jedoch darf var<T> nicht kopiert werden! Das ist ein vom User erzeugte Instanz, daher auch der var_ptr Umstand.
    Das kann man hieran gut sehen:

    224: error: no matching function for call to 'mtl::var_ptr<float>::var_ptr(const mtl::var<float>&)'

    var_ptr hätte somit einen Zeiger auf ein lokales Objekt (const var<T>&) was natürlich sinnlos ist. Der Zeiger soll ja wie gesagt auf die User Instnaz zeigen und Kopieren ist daher ausgeschlossen.

    Gedanken zur Lösung konnte ich mir noch nicht machen das ist also nur ein Zwischenbericht.

    Danke für Gedult und Mühe! 🙂



  • So habe nun das draus gemacht... im Nachhinein ist das wohl von Anfang an nur ein cast-problem und es wird nirgends kopiert? Ich bin verwirrt.
    So scheint es jedenfalls zu funktionieren:

    template <class T>
    	struct var_wrapper {
    		typedef T type;
    		static type wrap(type t){
    			return t;	
    		}
    	};
    	template <class W>
    	struct var_wrapper<var<W> > {
      		typedef var_ptr<W> type;
    		static type wrap(const var<W>& v) {
                            //Ist dieser cast zu abendteuerlich? 
    			return type(*const_cast<var<W>*> (&v));
    		}
    	};
    
    	#define TERM_BINARY_OP(opword, op) \
    	template <class left_t, class right_t> \
    	term< opword , typename var_wrapper<left_t>::type, typename var_wrapper<right_t>::type> \
    	operator op (left_t const& lhs, right_t const& rhs) \
    	{  \
    		typedef typename var_wrapper<left_t>::type leftwrap_t; \
    		typedef typename var_wrapper<right_t>::type rightwrap_t; \
    		return term< opword , leftwrap_t, rightwrap_t>( var_wrapper<left_t>::wrap(lhs), var_wrapper<right_t>::wrap(rhs) ); \
    	}
    

Anmelden zum Antworten