ideas icon indicating copy to clipboard operation
ideas copied to clipboard

Добавить свойства в C++

Open klappdev opened this issue 2 years ago • 6 comments

При работе с классами, хорошей практикой, является объявление полей закрытыми. Для чтения значения полей из вне, в классе объявляют геттеры. А для записи объявляют сеттеры.

Возьмём для примера класс QPoint с фреймворка Qt.

class QPoint {
public:
    // construct, dectructor, other
    inline int x() const { return xp; }
    inline int y() const { return yp; }
    inline void setX(int x) { this.xp = x; }
    inline void setY(int y) { this.yp = y; } 
  
private:
   int xp;
   int yp;
};

https://code.woboq.org/qt5/qtbase/src/corelib/tools/qpoint.h.html

При этом в большинстве случаев, геттеры и сеттеры очень простые. И не хочется писать руками.

Для того, чтобы каждый раз не писать геттеры, сеттеры, ничего не мешает реализовать оберточки.

template<typename T>
class Property {
public:
    constexpr explicit Property(const T& value) : value(value) {}

    constexpr Property& operator=(const T& newValue) {
        this->value = newValue;
        return *this;
    }

    constexpr operator const T&() const { return value; }
private:
    T value;
};

class QPoint {
public:
    // construct, dectructor, other
   
   Property<int> x;
   Property<int> y;
};

https://github.com/klappdev/firearrow/blob/master/app/src/main/cpp/util/property/Property.hpp https://github.com/klappdev/firearrow/blob/master/app/src/main/cpp/util/property/Getter.hpp https://github.com/klappdev/firearrow/blob/master/app/src/main/cpp/util/property/Setter.hpp

Концепция свойств(геттеров, сеттеров) существует во многих языках, таких например как Swift, Kotlin, C# и д.р., на уровне языка. Хочется что б в С++ завезли такую возможность на уровне языка. Например, это могло выглядить как в С#.

class QPoint {
public:
    // construct, dectructor, other
private:  
   int x { set; get; }; 
   int y { set; get; };
};

Предлагаю добавить два контекстных ключевых слова - set, get. Которые были бы ключевыми словами, только при объявлении поля класса. По аналогии с обычными методам, можно бы объявлять с разными квалификаторами - &, &&, const, volatile, noexcept. В случае если нужно не тривиальный геттер, сеттер, то код мог бы выглядеть следующим образом.

class QPoint {
public:
    // construct, dectructor, other
private:  
   int x { 
      set(int value) {
          std::cout << "invoke setter - x" << std::endl; 
          this->x = value;
      }; 
      get() const {
          std::cout << "invoke getter - x" << std::endl; 
          return x;
      }; 
   }; 
   int y { set; get; };
};

Под капотом, компилятор генерировал те самые методы геттеры, сеттеры. Современный С++ вносит много новых фич, которые позволяет писать меньше шаблонного кода. Концепция свойств(геттеров, сеттеров) как раз позволяет писать меньше кода, и при этом код остается таким же читаемым. Очень хочется видеть в С++.

Полезные ссылки:

  • https://docs.microsoft.com/ru-ru/dotnet/csharp/properties
  • https://kotlinlang.org/docs/properties.html
  • https://docs.swift.org/swift-book/LanguageGuide/Properties.html

klappdev avatar Jun 11 '22 11:06 klappdev

tl;dr: пример и аргументация кажутся неудачными, идею критике подвергать пока не буду.

При работе с классами, хорошей практикой, является объявление полей закрытыми. Возьмём для примера класс QPoint с фреймворка Qt.

Спорный переход: QPoint больше похож на структуру данных (внешний функционал), которую Qt почему-то реализовал как класс. Получился "квазикласс" в терминах этой статьи: http://www.idinews.com/quasiClass.pdf

Концепция свойств(геттеров, сеттеров) существует во многих языках, таких например как Swift, Kotlin, C# и д.р., на уровне языка.

Заметим, что во многих не менее замечательных языках концепция свойств годами отсутствует.

Концепция свойств(геттеров, сеттеров) как раз позволяет писать меньше кода, и при этом код остается таким же читаемым.

Если полей в структуре немного, то и кода для автоматических геттеров/сеттеров нужно немного, особенно с наличием auto и decltype. Если полей в структуре много... то с ней чего-то не то.

class QPoint {
public:
    // construct, dectructor, other
private:  
   int x { 
      set(int value) {
          std::cout << "invoke setter - x" << std::endl; 
          this->x = value;
      }; 
      get() const {
          std::cout << "invoke getter - x" << std::endl; 
          return x;
      }; 
   }; 
   int y { set; get; };
};

В этом примере следующий шаг, который хочется сделать — применить этот геттер/сеттер к y. В этот момент естественно ввести класс QCoordinate, в котором определить operator= и operator int().

pavelkryukov avatar Jun 11 '22 13:06 pavelkryukov

class QPoint {
public:
    // construct, dectructor, other
private:  
   int x { set; get; }; 
   int y { set; get; };
};

Много вопросов, как это будет сочетаться с конструкциями, специфичными для C++.

Раз:

QPoint p;
std::vector<int*> pointers;
pointers.push_back(&p.x); // что это?

Два:

// что будет делать такой код?
auto pointer_to_member = &QPoint::x;
p.*pointer_to_member = 3;

Три:

QPoint::QPoint(int a, int b) : x(a), y(b) { } // вызывать ли сеттер?

pavelkryukov avatar Jun 11 '22 13:06 pavelkryukov

Старые предложения об автоматических геттерах/сеттерах и более общем случае неявных функций, все не позднее 2005 года:

  • PME: Properties, Methods and Events: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2002/n1384.pdf
  • C++/CLI Properties http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1600.html (сразу говорят, что не идёт в стандарт C++)
  • Implicitly-Callable Functions: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1611.pdf
  • C++ Properties -- a Library Solution: https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1615.pdf

В последнем предложении содержится много критики к решениям уровня языка

pavelkryukov avatar Jun 11 '22 13:06 pavelkryukov

Геттеры/сеттеры на уровне языка работают как буто простые методы. Поэтому не должно конфликтовать с другими частями языка. В С++23 добавили deducing this. Что является тоже пересмотром методов в частности. И позволяет меньше писать шаблонного кода. Поэтому почему не пересмотреть и в сторону свойств.

klappdev avatar Jun 11 '22 15:06 klappdev

Тривиальные геттеры и сеттеры это ошибка проектирования и бесполезный код. В джаву С++ не нужно превращать Сеттер должен делать что-то осмысленное и называться не SetX, например vector::resize/reserve, это вообще говоря в ваших терминах сеттеры, но называются они осмысленно и делают логичные для инкапсуляции логики вещи P.S. в расте есть vec::set_len просто ору

kelbon avatar Jun 27 '22 12:06 kelbon

Вообще геттеры и сетеры очень удобны если нужно автоматически оповещать об изменении в данных. И на C++ довольно не удобно получать указатель на this класса, который содержит подобные геттеры и сеттеры.

kov-serg avatar Aug 01 '22 15:08 kov-serg