Seite 1 von 2

Properties

Verfasst: Mi Apr 24, 2013 1:28 pm
von Glocke
Hi, ich hab' mir mal Gedanken gemacht, wie man Properties in C++ realisieren könnte. Über den Vorteil lässt sich streiten, dennoch würde mich mal eure Meinung zu meiner einfachen Implementierung interessieren :)

Code: Alles auswählen

template <typename Type, typename Parent>
class property {

    protected:
        Parent* parent;
        Type (Parent::*getter)();
        void (Parent::*setter)(Type);
        
        Type get() {
            return (this->parent->*getter)();
        }
        void set(Type value) {
            (this->parent->*setter)(value);
        }
    
    public:
        property(Parent* parent, Type (Parent::*getter)(), void (Parent::*setter)(Type))
            : parent(parent)
            , getter(getter)
            , setter(setter) {
        }
        
        property<Type, Parent>& operator=(Type value) {
            this->set(value);
        }
        property<Type, Parent>& operator+=(Type value) {
            Type current = this->get();
            current += value;
            this->set(current);
        }
        property<Type, Parent>& operator-=(Type value) {
            Type current = this->get();
            current -= value;
            this->set(current);
        }
        property<Type, Parent>& operator*=(Type value) {
            Type current = this->get();
            current *= value;
            this->set(current);
        }
        property<Type, Parent>& operator/=(Type value) {
            Type current = this->get();
            current /= value;
            this->set(current);
        }
        
        // @todo: Weitere Operatoren: + - * /  == != <= => < >
        
        operator Type() {
            return this->get();
        }
};
Ein Beispiel könnte so aussehen:

Code: Alles auswählen

class Example2 {
    protected:
        int _value;
        
        int getValue() {
            return this->_value;
        }
        void setValue(int value) {
            if (value < -10) { value = -10; }
            if (value > 100) { value = 100; }
            this->_value = value;
        }

    public:
        // Deklaration der Property
        property<int, Example2> value;

        Example2()
            // Wert initialisieren
            : _value(0)
            // Property initialisieren
            , value(this, &Example2::getValue, &Example2::setValue) {
        }
};

// ----------------------------------------------------------------------------

int main() {
    Example2 e;
    e.value = 5;
    e.value *= 2;
    int i = e.value;
    std::cout << i << "\n";
}
LG Glocke

/EDIT: Prinzipiell ließe sich auch Read-Only / Write-Only realisieren - dann aber imho nur zur Laufzeit.

Re: Properties

Verfasst: Mi Apr 24, 2013 4:00 pm
von jeanluc
Schick schick, wobei ich persönlich versuche für solche Geschichten eine fertige Bibliothek zu finden.

Re: Properties

Verfasst: Mi Apr 24, 2013 10:19 pm
von Xin
Wie versprochen eine Rückmeldung - ich kann sowas nur nicht während der Arbeit ausprobieren - da soll ich ja für die arbeiten.
Ich habe jetzt keine Methodenzeiger verwendet, sondern einfach Funktionen, aber type::*get wäre ja auch möglich.

Warum ließ sich ro/rw nur zur Laufzeit realisieren?

Code: Alles auswählen


#include <stdio.h>

template< typename type, type (*get)( type in ) >
class readonly
{
  private:
    type operator =( type & rhs ) {}
  protected:
    type Data;
    
  public:
    explicit readonly( type data )
      : Data( data ) {}
      
    operator type () const
    { return get( Data ); }
};

template< typename type, type (*get)( type in ), type (*set)( type out )>
class readwrite : public readonly< type, get >
{
  public:
    readwrite( type data )
      : readonly< type, get >( data ) {}

    type operator =( type const & rhs )
    {
      return readonly< type, get >::Data = set( rhs );
    }
};

int int_get( int data )
{
  printf( "get int: %d\n", data );
  return data;
}

int int_set( int data )
{
  printf( "set int: %d\n", data );
  return data;
}

int main()
{
  readonly< int, int_get > ro( 1 );
  readwrite< int, int_get, int_set > rw( 4711 );
  
  int temp = ro;
  
//  ro = 4;   // geht nicht
  temp = rw;
  rw = 1234;
  
  printf( "----\n" );
  
  printf( "ro: %d, rw: %d", (int)ro, (int)rw );
};
Führt zu - erster Versuch mit der Zuweisung von ro = 4, dannach auskommentiert.

Code: Alles auswählen

apoc:glocke xin$ g++ property.cpp 
property.cpp: In function ‘int main()’:
property.cpp:51: error: no match for ‘operator=’ in ‘ro = 4’
property.cpp:7: note: candidates are: type readonly<type, get>::operator=(type&) [with type = int, type (* get)(type) = int_get]
property.cpp:5: note:                 readonly<int, int_get>& readonly<int, int_get>::operator=(const readonly<int, int_get>&)
apoc:glocke xin$ g++ property.cpp 
apoc:glocke xin$ ./a.out 
get int: 1
get int: 4711
set int: 1234
----
get int: 1234
get int: 1

Re: Properties

Verfasst: Do Apr 25, 2013 6:02 am
von Glocke
Xin hat geschrieben:Wie versprochen eine Rückmeldung - ich kann sowas nur nicht während der Arbeit ausprobieren - da soll ich ja für die arbeiten.
Sehr gut :D
Xin hat geschrieben:Ich habe jetzt keine Methodenzeiger verwendet, sondern einfach Funktionen, aber type::*get wäre ja auch möglich.
Naja gut, wenn ich aber getter/setter - die ich als Klassenmethoden geschrieben habe - für die Property verwenden will, brauche ich afaik zwingend Funktionszeiger. Oder irre ich mich da?
Xin hat geschrieben:Warum ließ sich ro/rw nur zur Laufzeit realisieren?
Weil ich nicht nachgedacht habe ^^ Klar geht das (wie du gezeigt hast xD)

LG Glocke

Re: Properties

Verfasst: Do Apr 25, 2013 7:49 am
von Xin
Glocke hat geschrieben:
Xin hat geschrieben:Ich habe jetzt keine Methodenzeiger verwendet, sondern einfach Funktionen, aber type::*get wäre ja auch möglich.
Naja gut, wenn ich aber getter/setter - die ich als Klassenmethoden geschrieben habe - für die Property verwenden will, brauche ich afaik zwingend Funktionszeiger. Oder irre ich mich da?
Hier wird ja auch mit Funktionszeigern gearbeitet, aber die werden in das Template reinkompiliert. Du kannst ja statische Funktionen in die Klasse einbauen. Wenn Du Methoden verwenden möchtest, brauchst Du Methodenzeiger (type::*get, ...), die Du aber ebenfalls in das Template einbinden kannst.
Eigene Methoden oder Funktionszeiger brauchst Du nur, wenn Du die Methode/Funktion zur Laufzeit auswechseln möchtest - dann musst Du Dir merken, welche Du gerade benutzt.

Im ausgeschlafenen Zustand muss ich sagen, dass zumindest set nicht gut implementiert ist: Es sollte jedenfalls nicht nur den neuen Wert erhalten, sondern auch Zugriff auf den vorherigen Wert haben, um diesen zurückgegeben zu können, falls der neue Wert inakzeptabel ist. Also Methode oder zumindest int set( int old, int new ).

Re: Properties

Verfasst: Do Apr 25, 2013 9:12 am
von GilbertDur
man könnte eventuell noch ein bisschen Ausnahmebehandlung einbauen :D

Re: Properties

Verfasst: Do Apr 25, 2013 9:15 am
von Xin
GilbertDur hat geschrieben:man könnte eventuell noch ein bisschen Ausnahmebehandlung einbauen :D
Welche Ausnahme?

Re: Properties

Verfasst: Do Apr 25, 2013 11:08 am
von GilbertDur
Hmm, ich dachte man könnte in C++ ähnlich zu C# z.B. eine Nullpointer-Exception werfen und behandeln. Allerdings sagt mir ein bisschen Google Recherche folgendes: "Dereferencing a nullptr is undefined behavior . This is trying to use an exception to catch undefined behavior. Fundamentally, you cannot do that in C++." Interessant, das hatte ich schon wieder vergessen.

Re: Properties

Verfasst: Do Apr 25, 2013 11:15 am
von Xin
GilbertDur hat geschrieben:Hmm, ich dachte man könnte in C++ ähnlich zu C# z.B. eine Nullpointer-Exception werfen und behandeln. Allerdings sagt mir ein bisschen Google Recherche folgendes: "Dereferencing a nullptr is undefined behavior . This is trying to use an exception to catch undefined behavior. Fundamentally, you cannot do that in C++." Interessant, das hatte ich schon wieder vergessen.
Wo sollte es denn hier zu einem Null-Pointer-Zugriff kommen?

Um eine Exception zu werfen, müsste man ja erstmal einen Grund schaffen...?

Re: Properties

Verfasst: Do Apr 25, 2013 11:41 am
von Glocke
Xin hat geschrieben:Wo sollte es denn hier zu einem Null-Pointer-Zugriff kommen?
Vielleicht meint er eine NULL als Funktionszeiger.. allerdings würde die Implementierung separater readonly, writeonly und readwrite Properties das Angeben von NULL als Funktionszeiger in der Initialisierung der Property sinnlos machen, d.h. Fehler auf Layer 8 ^^