Fall für Trait Klassen?



  • Hallo!

    Folgende Situation:
    Ich habe einerseits Containerklassen zum Verwalten (laden, speichern, zugreifen) von Bildern, andererseits Algorithmen die auf diesen Bildern arbeiten.
    Um das ganze nicht zu sehr miteinander zu verstricken habe ich mich entschieden, das ganze mit Templates zu machen.

    Nun gibt es aber immer wieder Algorithmen, die eben doch ein bisschen was über die verwendeten Datentypen wissen müssen. Werden die Bilder intern als unsigned char gespeichert, so ist jeder Kanal eines Pixels mit Werten im Bereich 0...255 kodiert, während bei einem float der Bereich 0...1 verwendet wird.

    Eine einfacher Algorithmus ist die Schwellwertfunktion, welche jedem Pixel unter einem Schwellwert schwarz zuordnet, und über dem Schwellwert weiß.
    Vergebe ich Default Parameter, so habe ich das Problem, dass ich mich auf einen Datentyp beziehe, z.B. high=255 bezieht sich auf unsigned char.

    Wie soll ich das umgehen?
    Mit den Trait Klassen kenn ich mich noch nicht so aus, aber wäre das ein typischer Einsatzbereich?
    Sodass ich dann statt high=255 schreiben kann typeTraits<typename T::value_t>::high ?
    Dann mach ich eine typeTraits Klasse mit den entsprechenden static Membern und spezialisiere diese Klasse schließlich für die verwendbaren primitiven Typen. Letztlich ist dann ...::high 1.0 bei float und 255 bei unsigned char.

    //...irgendein Algorithmus...
        template<class T>
        void threshold(T& img, typename T::value_t value=127,typename T::value_t low=0,typename T::value_t high=255)
        {
            Pixel<typename T::value_t> px;
    
            makeGray(img);
            for(typename T::coordinates_t x=0;x<img.getWidth();x++)
            {
                for(typename T::coordinates_t y=0;y<img.getHeight();y++)
                {
                    px=img.getPixelAt(x,y);
                    px[0]=px[0]>value?high:low;
                    img.setPixelAt(x,y,px);
                }
            }
        }
    

    Vielen Dank für Eure Hilfe!



  • c++ fragender schrieb:

    Nun gibt es aber immer wieder Algorithmen, die eben doch ein bisschen was über die verwendeten Datentypen wissen müssen.

    Schon mal was von typeid gehört?



  • Hacker schrieb:

    c++ fragender schrieb:

    Nun gibt es aber immer wieder Algorithmen, die eben doch ein bisschen was über die verwendeten Datentypen wissen müssen.

    Schon mal was von typeid gehört?

    Mit typeid könnte ich mittels if(typeid(var)==typeid(int)) {...} ja im Algorithmus Sonderbehandlungen für verschiedene Datentypen einbauen, und das Problem somit lösen. Danke für den Vorschlag!

    Andererseits könnte ich das ganze auch mittels Traits Klassen lösen.

    Was ist der übliche Weg in so einem Fall? Was spricht für die eine, und was für die andere Variante?



  • Type Traits klingen hier meiner Meinung nach sinnvoll. Ich würds wahrscheinlich so machen.

    Hacker schrieb:

    Schon mal was von typeid gehört?

    OMG, das ist nicht dein Ernst, oder? Wenn der OP doch schon Type Traits erwähnt, warum googlest du das dann nicht mal?



  • Michael E. schrieb:

    Type Traits klingen hier meiner Meinung nach sinnvoll. Ich würds wahrscheinlich so machen.

    Hacker schrieb:

    Schon mal was von typeid gehört?

    OMG, das ist nicht dein Ernst, oder? Wenn der OP doch schon Type Traits erwähnt, warum googlest du das dann nicht mal?

    Danke für deine Einschätzung.
    Deiner AW entnehme ich, dass typeid nicht so das wahre in dem Fall ist? Könntest du noch kurz erklären was gegen typeid spricht?



  • c++ fragender schrieb:

    Deiner AW entnehme ich, dass typeid nicht so das wahre in dem Fall ist? Könntest du noch kurz erklären was gegen typeid spricht?

    Algorithmen sollten immer so "rein" (d.h. nahe an der mathematischen Formulierung) wie möglich implementiert werden. Das erleichtert das Verständnis ungemein. Fallunterscheidungen nach Typen haben mit dem Algorithmus überhaupt nichts zu tun und müllen nur deinen Bildschirm zu. Algorithmen sind ohnehin häufig länger als die angestrebten zehn Zeilen pro Funktion.



  • c++ fragender schrieb:

    Deiner AW entnehme ich, dass typeid nicht so das wahre in dem Fall ist? Könntest du noch kurz erklären was gegen typeid spricht?

    typeid ist fast immer Schwachsinn und hier ganz besonders. typeid ist ein Laufzeitmechanismus, damit geht alles was du hier mit templates gewinnen wolltest den Bach runter...



  • Oh SHIt! Nein nein, ich meinte natürlich type traits. Das liegt daran, dass ich zufälligerweise gerade vorm Schreiben was mit typeid ausprobiert habe.. und das dann gegoogelt habe...
    @TE:
    Du kannst einfach

    template<typename T>
    //...
    if(std::is_floating_point<T>::value)
        //...
    else if(std::is_integral<T>::value)
        //...
    

    Schreiben.
    Puh...
    Zudem solltest du evt. ein static_assert oder ein std::enable_if benutzen, solange du nicht willst dass jemand da irgendein von dir nicht behandelte Typen als Template-Parameter übergibt... 😉



  • c++ fragender schrieb:

    Hallo!
    Eine einfacher Algorithmus ist die Schwellwertfunktion, welche jedem Pixel unter einem Schwellwert schwarz zuordnet, und über dem Schwellwert weiß.
    Vergebe ich Default Parameter, so habe ich das Problem, dass ich mich auf einen Datentyp beziehe, z.B. high=255 bezieht sich auf unsigned char.

    Wie soll ich das umgehen?

    Ich denke man kann das mit Template Spezialisierungen lösen:

    template< class T > struct threshold_impl;
    
    template<> struct threshold_impl< unsigned char >
    {
        template< class Img >
        static void threshold( Img &img , float value )
        {
            // umrechnen von float in unsigned char
        }
    }
    
    template<> struct threshold_impl< float >
    {
        template< class Img >
        static void threshold( Img &img , float value )
        {
            // "umrechnen von float in float"
        }
    }
    
    template<class T>
    void threshold(T& img, float value=0.5 )
    {
       threshold_impl< T::value_t >::threshold( img , value );
    }
    

    Die Spezialisierung kann auch noch tiefer im Algorithmus auftreten, so dass nicht der gesamte Algorithmus neu geschrieben wird, sondern nur die Zuweisung, d.h. die Ausdrücke innerhalb der innersten Schleife.



  • Danke für eure AW.

    Habs jetzt mit Traitsklassen gelöst, funktioniert wunderbar!
    Änderungen am Code waren minimal ... und ich glaub es kaum, nun funktioniert das Zeugs wirklich mit allen primitiven Datentypen! Tolles C++ 🤡

    template<class T>
    struct ImageTypeTraits
    {
        static const size_t cardinality;//anzahl möglicher werte
        static const T      low;//kleinster wert
        static const T      high;//größter wert
        static const T      center;//mittlerer wert
        static const T      delta;
        static void         tellType(){std::cout<<"T";}
    };
    
    template<>
    struct ImageTypeTraits<unsigned char>
    {
        static const size_t         cardinality=256;//anzahl möglicher werte
        static const unsigned char  low=0;//kleinster wert
        static const unsigned char  high=255;//größter wert
        static const unsigned char  center=127;//mittlerer wert
        static const unsigned char  delta=1;
        static void                 tellType(){std::cout<<"UC";}
    };
    
    //und weitere spezialisierungen...
    

Anmelden zum Antworten