template operator



  • ja, für Klassen geht das, ich habe allerdings keine template Klasse. für template Klassen ist so ein überladener Operator realisierbar.
    Ich wollte es ohne Klasse handhaben.


  • Mod

    HeroHolger schrieb:

    ja, für Klassen geht das, ich habe allerdings keine template Klasse. für template Klassen ist so ein überladener Operator realisierbar.
    Ich wollte es ohne Klasse handhaben.

    Wenn du keine Klasse hast, was willst du dann überhaupt ausgeben? 😕

    Wild geraten: Wäre vielleicht Polymorphie das richtige für dich?



  • Ich habe diverse C-Variablen die halt alle unter dem Motto laufen, dass sie einen "bool valid" haben und einen beliebigen Valuetypen haben. Und die will ich halt per cout ausgeben.



  • HeroHolger schrieb:

    Ich habe diverse C-Variablen die halt alle unter dem Motto laufen, dass sie einen "bool valid" haben und einen beliebigen Valuetypen haben. Und die will ich halt per cout ausgeben.

    Du hast die hoffentlich in einem eigenen namespace, genau wie deinen op<<, sonst versucht der Compiler im Zweifel ALLES was du ihm vorlegst in deinen op<< zu stopfen.
    Lösung für dein problem ist, dem Compiler bei der richtigen Auflösung der aufgerufenen Operatoren zu helfen:

    template<class T>
    std::ostream& operator<< (std::ostream& os, const T& fValue)
    {
        if (!fValue.valid)
        {
            os.operator<<("not valid\n"); //ist eindeutig das Member von ostream
            return os;
        }
    
        (os << fValue.value).operator<<("\n");
        return os;
    };
    

    Versuch mal obs klappt...


  • Mod

    Wenn's schnell und dreckig sein soll (ich habe den Verdacht, dass du noch nie von namespaces und so gehört hast), hätte ich hier auch noch eine Hacker-Version. Das was pumuckl vorschlägt ist aber VIEL besser.

    #include <iostream>
    #include <sstream>
    using namespace std;
    
    template<typename T> const char* to_cstring(const T& value)
    {
      stringstream s;
      s << value;
      return s.str().c_str();
    }
    
    #define OUT(FOO) (((FOO).valid) ? to_cstring((FOO).value) : "not valid") << '\n'
    
    struct A
    {
      bool valid;
      int value;
    };
    
    struct B
    {
      bool valid;
      const char* value;
    };
    
    int main()
    {
      A a;
      a.valid=true;
      a.value=100;
      B b;
      b.valid=true;
      b.value="Hallo Welt!";
      cout<<OUT(a);
      cout<<OUT(b);
    }
    


  • leider hab ich die nicht in einem extra Namespace. und ich kann ihn auch nicht mehr in extra namespace packen weil wir hier mit 15 Jahre alter C-Software arbeiten.

    zu deinem Beispiel: Genau das will ich ebend nicht machen. Ich will ebend nicht erst "per Auge" überprüfen ob diese variable einen Validtypen hat oder nicht und wenn sie dass hat, dann nutze ich out, sondern ich will es halt optimieren.

    ich hab ja schon sowas hier, aber es ist halt unpraktisch wenn man 1000 Zeilen lang Ausgaben macht, weil man riesige Strukturen in den "Cout-Debugger" stecken will.

    Ich will ebend nciht erst ewig in den Variablen definitions suchen was das für ein variablentyp ist.

    template<class T> void validOutput (T fValue)
    
    {
    	if (!fValue.valid)
    	{
    		cout << "not valid\n";
    	}
    	else
    	{
    		cout << fValue.value << "\n";
    	}
    
    }
    

    Aber dieses Beispiel oder auch deines, ist eben genau nicht was ich suche.

    Trotzdem nochmal danke für den Vorschlag.



  • Heroholger schrieb:

    [...] weil wir hier mit 15 Jahre alter C-Software arbeiten.

    zu deinem Beispiel: Genau das will ich ebend nicht machen. Ich will ebend nicht erst "per Auge" überprüfen ob diese variable einen Validtypen hat oder nicht und wenn sie dass hat, dann nutze ich out, sondern ich will es halt optimieren.

    Dann wäre das Beste wohl, nicht großen Aufwand in solche Mikro-Optimierungen zu stecken sondern erstmal in ein vernünftiges Refactoring, um aus dem Antik-Code eine wartbare (und optimierbare) Software zu machen...


  • Mod

    pumuckl schrieb:

    Lösung für dein problem ist, dem Compiler bei der richtigen Auflösung der aufgerufenen Operatoren zu helfen:

    Damit wird, falls das global passiert, immer noch der Namenraum verseucht. Wenn anschließend versucht wird, einen std::string auszugeben, ist es wieder mehrdeutig.

    Schnell hingeschrieben ohne Anspruch auf Eleganz und ungetestet, könnte das ungefähr so aussehen

    template <typename T>
    struct is_valid_struct
    {
        typedef char no;
        typedef no yes[2];
        static no is_bool(...);
        template <typename U> static typename enable_if<is_same<U,bool>,yes&>::type is_bool(U);
        static no func(...);
        template <typename U>
        static typename enable_if_c<sizeof(is_bool(((U*)0)->valid))==sizeof(yes)&&sizeof(&U::value),yes&>::type func(const U&);
        static T make();
        static const bool value = sizeof( func(make()) ) == sizeof( yes );
    };
    
    template<class T>
    typename enable_if<is_valid_struct<T>,std::ostream&>::type operator<< (std::ostream& os, const T& fValue)
    {
        if (fValue.valid)
            os << fValue.value << '\n';
        else
            os << "not valid\n";
        return os;
    };
    


  • pumuckl schrieb:

    Dann wäre das Beste wohl, nicht großen Aufwand in solche Mikro-Optimierungen zu stecken sondern erstmal in ein vernünftiges Refactoring, um aus dem Antik-Code eine wartbare (und optimierbare) Software zu machen...

    Scherzkeks, das ist offtopic! 😉



  • @Camper: kannst du mir bitte einen kleinen Gefallen tun, und mir erklären was du da geschrieben hast, ich verstehe es leider nicht.



  • Mit SFINAE kommt man da hin, obwohl ich es in diesem Fall für ziemlich gewagt halte. Camper hat das schon angesprochen, ich denke aber, dass der Mechanismus einer Erklärung bedarf.

    SFINAE steht für "substitition failure is not an error". Konkret bedeutet das, wenn der Compiler eine Vorlage findet, die von der Signatur her passt, sie aber für den gegebenen Typen nicht konkretisieren kann, schmeißt er keinen Fehler, sondern sucht nach einer anderen, die passt. Beispiel:

    #include <iostream>
    
    template<typename T>
    typename T::value_type foo() {
      std::cout << "value_type" << std::endl;
      return (typename T::value_type)(0);
    }
    
    template<typename T>
    typename T::val_t foo() {
      std::cout << "val_t" << std::endl;
      return (typename T::val_t)(0);
    }
    
    struct A { typedef int value_type; };
    struct B { typedef int val_t; };
    
    int main() {
      foo<A>();
      foo<B>();
    }
    
    // Ausgabe:
    //
    // value_type
    // val_t
    

    Damit ist es möglich, eine Vorlage operator<< so zu entwerfen, dass sie nur für Typen, die einen bestimmten Member haben, konkretisierbar ist und nur für diese benutzt wird. Beispielsweise

    #include <iostream>
    #include <string>
    
    struct A {
      int xyz;
    };
    
    struct B {
      int xyz;
    };
    
    template<typename T, int const T::* const = &T::xyz>
    struct check_type {
      typedef std::ostream &type;
    };
    
    template<typename T>
    typename check_type<T>::type operator<<(std::ostream &out, T const &x) {
      return out << "X: " << x.xyz;
    }
    
    int main() {
      A a = { 1 };
      B b = { 2 };
    
      int x = 3;
      std::string s = "foo";
    
      std::cout << a << std::endl
    	    << b << std::endl
    	    << x << std::endl
    	    << s << std::endl;
    }
    

    campers Code benutzt boost.enable_if, welches genau dieses Prinzip ausnutzt, allerdings auf geordnetere Weise.



  • HeroHolger schrieb:

    @Camper: kannst du mir bitte einen kleinen Gefallen tun, und mir erklären was du da geschrieben hast, ich verstehe es leider nicht.

    im großen und ganzen nutzt er SFINAE (s.google) - um die Doppeldeutigkeiten zu verhindern. Soll heißen, er schränkt dein Template ein auf Typen, die ein member "valid" vom typ bool haben:

    enable_if (s.google) tut das, und zwar genau dann, wenn wenn das erste templateargument ein statisches member value mit Wert true hat. enable_if<>::type hat dann den typend es zweiten arguments, also ostream&.

    HeroHolger schrieb:

    Scherzkeks, das ist offtopic! 😉

    Das war durchaus kein Scherz. Du willst bei Uralt-Code mit deinem Template kleine Optimierungen anbringen. Vermutlich eher in der Lesbarkeit als in der Performance selber, denn SeppJs Beispiel hat keinen Performancenachteil gegenüber deinem operator.
    Wenns aber um Lesbarkeit/Wartbarkeit geht, setzt du wirklich an der falschen Ecke an.



  • @seldon: ALTA SCHWEDE, das ist ja die hohe Zauberkunst der Programmierung. Benutzt er dafür die Boost-Lib weil du schreibst "boost.enable_if"? . Ich muss das mal die Tage ausprobieren, damit ich das verstehe.

    Für mich hab ich die Typen jetzt mittels Copy+Paste 3 einzeloperatoren spendiert, amit kann ich jetzt erstmal arbeiten.

    @pumuckl: Das die Software mal dringend gewartet werden muss, weiß ich auch (Und ich werde hier nicht Diskutieren weshalb ja oder nein), aber das war hier nicht das Thema und deshalb ist es Offtopic. Trotzdem Danke. Ich werd mal ne Runde googeln.


Anmelden zum Antworten