Eigenschaften-Variable erstellen

  • C++

    Eigenschaften-Variable erstellen

    Mahlzeit!

    Ich habe mir mithilfe eines Templates eine Eigenschaften-Klasse erstellt. Die Objekte dieser Klasse verhalten sich ungefähr so gleich wie dieses hier: docs.microsoft.com/de-de/cpp/cpp/property-cpp?view=msvc-160
    Warum verwende ich es nicht direkt? Da man auf private Klassenmember mit dieser Methode keinen Zugriff hat. Und ich würde ganz gerne verhindern, dass man die internen Variablen "von außen" sehen kann.

    Diese Property-Klasse sieht wie folgt aus, interessant wird vor allem Zeile 54.:
    Spoiler anzeigen

    C-Quellcode

    1. /// <summary>
    2. /// Offers access to private or protected member fields.
    3. /// </summary>
    4. /// <typeparam name="T">The type of the class's getter/setter.</typeparam>
    5. /// <typeparam name="C">The getter's and setter's class.</typeparam>
    6. template<typename T, class C>
    7. class _declspec(dllexport) Property final
    8. {
    9. // Getter function pointer
    10. using GetterType = T(C::*)();
    11. // Setter function pointer
    12. using SetterType = void (C::*)(T);
    13. // Pointer to the property's object.
    14. C* const m_object{};
    15. // Holds the getter function.
    16. GetterType m_getter{};
    17. // Holds the setter function.
    18. SetterType m_setter{};
    19. public:
    20. /// <summary>
    21. /// Constructor of class "Property".
    22. /// </summary>
    23. /// <param name="object">: The object of the property's class.</param>
    24. /// <param name="getter">: The class's getter function pointer.</param>
    25. /// <param name="setter">: The class's setter function pointer.</param>
    26. Property(C* object, GetterType getter, SetterType setter)
    27. : m_object{ object },
    28. m_getter{ getter },
    29. m_setter{ setter } {}
    30. /// <summary>
    31. /// Constructor of class "Property".
    32. /// </summary>
    33. /// <param name="object">: The object of the property's class.</param>
    34. /// <param name="getter">: The class's getter function pointer.</param>
    35. Property(C* object, GetterType getter)
    36. : m_object{ object },
    37. m_getter{ getter } {}
    38. /// <summary>
    39. /// Calls the getter function and returns its return value.
    40. /// </summary>
    41. T callGetterFunction() const
    42. {
    43. return (m_object->*m_getter)();
    44. }
    45. /// <summary>
    46. /// Calls the getter function and returns its return value.
    47. /// </summary>
    48. operator T() const
    49. {
    50. return callGetterFunction();
    51. }
    52. /// <summary>
    53. /// Calls the setter function of the class's object.
    54. /// </summary>
    55. /// <param name="value">: The new value the property item should be set to.</param>
    56. /// <returns>The class's object.</returns>
    57. C& operator=(const T& value)
    58. {
    59. if (m_setter)
    60. {
    61. (m_object->*m_setter)(value);
    62. }
    63. return *m_object;
    64. }
    65. };
    66. [/spoiler][spoiler]


    Die Deklaration eines Property-Objekts würde dann so aussehen: Property<std::wstring, Window> Text{ this, &Window::getText, &Window::setText };. Window repräsentiert eine Klasse, die ein Fenster erstellt und "verwalten" kann. Das Property-Feld Text wird somit den Titel des Fensters ändern.

    Nun bin ich etwas pingelig. Angenommen ich habe eine Property Size (Fenstergröße), die statt mit std::wstring mit einer eigens definierten Rectangle-Struktur mit den Member-Variablen X, Y, Width und Height arbeitet, und möchte diese Size-Property dann mit einer schon vorhandenen Rectangle-Struktur vergleichen. Property<Rectangle, Window> Size{ this, &Window::getRectangle, &Window::setRectangle }

    C-Quellcode

    1. if ((this->Size).X == Drawing::Rectangle{ 10, 20, 30, 40 }.X)
    2. {
    3. }


    Dann stehe ich vor dem Problem, dass eher ein Datenvergleich zwischen den Typen Property<Rectangle, Window> (this->Size, wobei this ein Objekt der Klasse Window ist) und Rectangle{ 10, 20, 30, 40 } stattfindet, statt zwischen dem "zerfallenen" (siehe Function-Call operator overloading in Zeile 54.) Property-Element Size und Rectangle{ 10, 20, 30, 40 }.
    Das Visual Studio gibt verständlicherweise das hier aus: "C++ Kein-Operator stimmt mit diesen Operanden überein. Operandentypen sind: Property<Rectangle, Window> == Rectangle".

    Die Lösung ist relativ einfach, ich nehme einfach die C-style-conversion und ummantele mein Property-Feld damit:

    C-Quellcode

    1. if (((Rectangle)this->Size).X == Rectangle{ 10, 20, 30, 40 }.X)
    2. {
    3. }


    Zugegeben, das Problem ist völlig abstrakt, in leicht abgewandelter Art und Weise begegnet es mich aber gerade.
    Es funktioniert ja soweit. Aber in der Programmierung heißt es ja nicht: "Funktioniert und alles gut.", sondern man sollte schon hinterfragen, ob das, was man da gemacht hat, auch sauber ist. Die Frage würde ich mal an euch weiter stellen. Ist das so sauber, wie ich das mache? Oder gibt es da gar kein "dreckig"?

    Vielen Dank schon mal vorab!