Comments 44
Мне кажется, что основная «эстетическая» проблема тут — отсутствие видимой декларации геттеров и сеттров. Это рвет шаблон.
А насчет макросов не негодуйте. Макросы — зло, их развивать никто не будет. Это даже хорошо, что нет перегрузки. Представьте какой зоопрак был бы.
А насчет макросов не негодуйте. Макросы — зло, их развивать никто не будет. Это даже хорошо, что нет перегрузки. Представьте какой зоопрак был бы.
Думаю, можно было бы сделать синтаксис такой, чтоб smartfield принимал имена геттера и сеттера в качестве аргументов. Опять же если сделать так то если программист забудет написать один из методов то компилятор выведет что-то страшное, а так как сделано сейчас только то, что такой-то метод не реализован.
На счет макросов на самом деле склонен с вами согласиться, но мне кажется здесь они к месту. Можно было обойтись почти без всех макросов начинающих с "__", они все появились в процессе проб и ошибок и пока я придумывал код использовал такие блоки как детали конструктора.
На счет макросов на самом деле склонен с вами согласиться, но мне кажется здесь они к месту. Можно было обойтись почти без всех макросов начинающих с "__", они все появились в процессе проб и ошибок и пока я придумывал код использовал такие блоки как детали конструктора.
Было бы круто сделать объявления пропертей как в C#, но я не представляю как :)) Кстати, если вы завязаны на конкретный компайлер, то можно полазить по докам. Майкрософтовский С++, например, поддерживает проперти.
Ну вы же написали, что это просто этюд. Так что реализацию можно и не обсуждать глубоко.
Ну вы же написали, что это просто этюд. Так что реализацию можно и не обсуждать глубоко.
Я тоже сначала хотел сделать по образцу C#, но меня смутил тот факт, что в С++, в отличии от шарпа, принято разделять декларирование и реализацию.
Вообще я почти всегда использую студию, но кроссплатформенностью жертвовать не хотелось бы, поэтому надеюсь найти время и написать вторую версию для любого компилятора и с возможностью указания имен геттеров и сеттеров и может еще какими-нибудь плюшками.
Вообще я почти всегда использую студию, но кроссплатформенностью жертвовать не хотелось бы, поэтому надеюсь найти время и написать вторую версию для любого компилятора и с возможностью указания имен геттеров и сеттеров и может еще какими-нибудь плюшками.
Это не сильно распрострено, но не является чем-то чуждым для С++. Так поступают с инлайнами в классах или с шаблонами.
Я вот это имел в виду:
Такое на С++ не напишешь :)
Я вот это имел в виду:
class TimePeriod
{
private double seconds;
public double Hours
{
get { return seconds / 3600; }
set { seconds = value * 3600; }
}
}
Такое на С++ не напишешь :)
Нуууу… Может и не напишешь. Сходу я придумал что-то похожее только для доступа по имени из ран-тайма. Заведем в базовом классе (а я считаю, что наследование от базового класса это не плохо) виртуальный метод void _fieldsection(), который будем вызывать из конструктора. Заведем макрос #define fieldsection _fieldsection(). Теперь мы в классе можем написать так:
здесь field — макрос, который разворачивается в вызов метода добавление поля в класс, а get и set разворачиваются в лямбда функции, которые сохраняются соответственно текущему полю. Не могу только придумать, как сделать обращение через. или ->.
class Foo
{
fieldsection
{
field(int, a)
{
get {...}
set {...}
}
}
...
}
здесь field — макрос, который разворачивается в вызов метода добавление поля в класс, а get и set разворачиваются в лямбда функции, которые сохраняются соответственно текущему полю. Не могу только придумать, как сделать обращение через. или ->.
А что, мне нравится.
В принципе, если мне удастся запилить что-то вроде рефлексии — сделать действительно красиво как в шарпе будет не сложно.
Подскажите, может я торможу, но нельзя ли сделать конкатизацию при редекларировании дефайна предыдущего значения с новым:
#define MACRO мясо
#define MACRO есть_##MACRO
Надо чтоб получилось «есть_мясо», а генерится «есть_MACRO»
Подскажите, может я торможу, но нельзя ли сделать конкатизацию при редекларировании дефайна предыдущего значения с новым:
#define MACRO мясо
#define MACRO есть_##MACRO
Надо чтоб получилось «есть_мясо», а генерится «есть_MACRO»
Какая-то жуткая смесь Си, C++ и С++0x с регулярным эксплуатированием багов компилятора студии. Гигантские макросы, которые разворачиваются в шаблоны, которые инстанциируются для классов, которые в свою очередь сгенерированы препроцессором. Мечта для любителей отладки.
А причина появления статьи просто гениальна: «в C++ нет средства описания полей класса с контролируемым доступом, как например property в C#». Конец объяснений (к слову, в прошлой статье то же самое). Опять же, полный игнор комментариев к предыдущей статье. Извините, но это совсем не fun.
А причина появления статьи просто гениальна: «в C++ нет средства описания полей класса с контролируемым доступом, как например property в C#». Конец объяснений (к слову, в прошлой статье то же самое). Опять же, полный игнор комментариев к предыдущей статье. Извините, но это совсем не fun.
Понятное дело, что никто не собирается это использовать в реальном проекте. Что плохого в том, чтобы в свое удовольствие написать что-то такое жуткое, но интересное? Кому-то это просто нравится, вот и все.
Плохое в том, что перед написанием кода неплохо бы задаться реальной практической целью, с которой пишется код. Люди видят, что в языках есть интроспекция и сразу накручивают макросы добавления метаданных к классу «чтобы просто были». Люди видят, что в языках есть рефлексия и сразу на каждое поле данных класса приделывают класс с теми самыми метаданными. Зачем нужна интроспекция и рефлексия никого не волнует.
А ведь можно было задаться целью написать систему сериализации/десериализации классов, в которых поля определены как «field(float, f);». Или приделать систему динамического добавления полей. В любом случае нужно задаться реальной целью до начала написания кода.
А ведь можно было задаться целью написать систему сериализации/десериализации классов, в которых поля определены как «field(float, f);». Или приделать систему динамического добавления полей. В любом случае нужно задаться реальной целью до начала написания кода.
Искусство ради искусства.
Интересно получилось. Как автор предыдущей статьи скажу, что у вас получилось гораздо красивее с точки зрения синтаксиса. Я, кстати, на той статье не остановился и немного развил идею . Красивее не стало, зато теперь намного безопаснее. К ваше реализации есть пара претензий:
1) Использование чисто майкрософтовских «фич», которые нарушают кроссплатформенность. В студии и так есть свои проперти.
3) Неявное задание сеттеров и геттеров, которое ещё и обязывает по-вашему называть методы.
2) Не смог до конца разобраться, но как создать readonly свойство?
У меня сейчас бродят идеи реализации на основе новшеств C++11. Лямбды вместе с std::function помогут сделать обьяления методов доступа красивее, а возможность вызывать конструкторы друг из друга — выделить инициализацию свойств в отдельный конструктор.
1) Использование чисто майкрософтовских «фич», которые нарушают кроссплатформенность. В студии и так есть свои проперти.
3) Неявное задание сеттеров и геттеров, которое ещё и обязывает по-вашему называть методы.
2) Не смог до конца разобраться, но как создать readonly свойство?
У меня сейчас бродят идеи реализации на основе новшеств C++11. Лямбды вместе с std::function помогут сделать обьяления методов доступа красивее, а возможность вызывать конструкторы друг из друга — выделить инициализацию свойств в отдельный конструктор.
1) Уже взял на заметку и собираюсь с этим бороться
3) Я думал это фича, но судя по комментариям следует дать возможность программисту самостоятельно описывать методы get и set.
2) Не задумывался об этом, но smartfield + пустой сеттер (или который например кидает эксепшн)
На счет развития идеи — тоже взял на заметку, позже разберусь.
И на счет использования нового стандарта — эта идея мне действительно кажется хорошей и может быть получится сделать действительно пригодную для использования реализацию.
3) Я думал это фича, но судя по комментариям следует дать возможность программисту самостоятельно описывать методы get и set.
2) Не задумывался об этом, но smartfield + пустой сеттер (или который например кидает эксепшн)
На счет развития идеи — тоже взял на заметку, позже разберусь.
И на счет использования нового стандарта — эта идея мне действительно кажется хорошей и может быть получится сделать действительно пригодную для использования реализацию.
Зачем!?
Я очень негодую отсутствую в препроцессоре возможности перегрузки макросов хотя бы по числу аргументов
На сколько я знаю, с помощью boost preprocessor'а можно указать параметр с любым количеством параметров или что-то похожее. Перегрузить конечно макрос нельзя, но можно вроде обойтись листом параметров. Вот небольшая тема как реализовать подобное на gcc без boost'а.
>«Например поле типа int с именем «x». Нас вполне устроит такая запись: field(int,x);»
«отучаемся говорить за всех»©FIDO
меня устроит запись get_x() и set_x(v)
«отучаемся говорить за всех»©FIDO
меня устроит запись get_x() и set_x(v)
То есть, мы забили на все плюшки статической типизации, и сделали странный, кривой и неустойчивый динамический сеттер/геттер. А в чем профит?
По-моему, достаточно просто заполнить map<string, T>, и добавить к этому T &operator()(string). А в конструкторе заполнить мапу известными полями. Тогда такая запись:
obj("fieldName") = value; // имеет место быть
Ссылку на что вернет ваш оператор, если поля не существует?
По идее исключение можно вызвать. А можно просто T()
Т.е. малейшая опечатка и узнаете об этом только во время выполнения программы (исключение). Либо вообще не узнаете, а будете час судорожно искать почему у вас не работает приложение (в случае T()).
Такие вещи должны во время компиляции всплывать.
Я уж не говорю о том, что никакие вижуал ассисты и интеллисенсы не смогут вам подсказать имя поля.
Такие вещи должны во время компиляции всплывать.
Я уж не говорю о том, что никакие вижуал ассисты и интеллисенсы не смогут вам подсказать имя поля.
Чем больше я смотрю на C++, тем больше нравится мне Ruby © перефразировал классика
class Foo
attr_accessor :x
end
class Foo
attr_accessor :x
end
Ну вы хотя бы из любви к рациональности заюзали в базовом классе хеш-таблицу вместо вектора с линейным поиском и сравнением строк на каждой итерации.
К слову, нечто отдаленно похожее реализовано в .NET Framework в классе DependencyObject. Но там цель всего этого заключалась в том, что бы можно было описывать граф распространения значений свойств набора объектов, как набор других объектов, строить цепочки связанных свойств. Проще говоря, для организации Data Binding'а.
К слову, нечто отдаленно похожее реализовано в .NET Framework в классе DependencyObject. Но там цель всего этого заключалась в том, что бы можно было описывать граф распространения значений свойств набора объектов, как набор других объектов, строить цепочки связанных свойств. Проще говоря, для организации Data Binding'а.
Касаемо перегрузки макросов, есть такая штука как variadic macro.
Полного решения проблемы это не даёт, но можно через этот variadic macro сделать вызов разных перегруженных методов. Я это использовал в своей библиотеке журналирования для Qt, о которой пока не успел написать на Хабре.
Выглядит это примерно так:
В вызываемом коде:
Проверка типов на этапе компиляции работает. В принципе, никто не мешает вместо пачки статических функций сделать #define на шаблонизированную функцию.
Полного решения проблемы это не даёт, но можно через этот variadic macro сделать вызов разных перегруженных методов. Я это использовал в своей библиотеке журналирования для Qt, о которой пока не успел написать на Хабре.
Выглядит это примерно так:
#define LOG_DEBUG(...) Logger::write(Logger::Debug, __FILE__, __LINE__, Q_FUNC_INFO, ##__VA_ARGS__)
...
class Logger
{
public:
...
static void write(LogLevel logLevel, const char* file, int line, const char* function,
const QString& message);
static void write(LogLevel logLevel, const char* file, int line, const char* function,
const char* message);
static QDebug write(LogLevel logLevel, const char* file, int line, const char* function);
...
}
В вызываемом коде:
LOG_DEBUG("Test1");
LOG_DEBUG(tr("Test2")); // QObject::tr() возвращает QString
LOG_DEBUG() << "Test 3:" << testValue; // У объекта QDebug есть перегруженный operator<<
Проверка типов на этапе компиляции работает. В принципе, никто не мешает вместо пачки статических функций сделать #define на шаблонизированную функцию.
Очередной костыль для С++. Ради чуть лучшей читаемости наворотили чёрти-чего и сбоку бантик. Зачем? С++ прекрасен и без этих конструкций.
Может пора пересесть на C#? Или на Delphi? Там это лет 15 назад уже было.
PS: Впрочем, как академические упражнения в написании макросов и шаблонов, довольно занимательно.
Может пора пересесть на C#? Или на Delphi? Там это лет 15 назад уже было.
PS: Впрочем, как академические упражнения в написании макросов и шаблонов, довольно занимательно.
У майкрософта есть своя версия
В одном из своих проектов использую такой вариант:
Тоже не бог весть что, но позволяет достигнуть желаемого эффекта («описания полей класса с контролируемым доступом»).
class FullStrategyMapScene : public QGraphicsScene
{
Q_OBJECT;
public:
// Инициализируем свойства
FullStrategyMapScene() : properties(this) {;}
~FullStrategyMapScene();
// Объявляем свойства
PROPERTIES(FullStrategyMapScene,
// Простое свойство
PROPERTY(QSize, CellSize)
// Простое свойство с инициализацией
PROPERTY_I(Unit const*, CurrentUnit, NULL)
// Свойство с геттером и сеттером
RW_PROPERTY(QPoint, CursorPos, GetCursorPos, SetCursorPos)
// Свойство с сеттером
WO_PROPERTY(IPlanet const*, Planet, SetPlanet)
RO_PROPERTY(ISurfaceMap const*, SurfaceMap, GetSurfaceMap)
);
private:
void SetPlanet(IPlanet const* planet);
QPoint const& GetCursorPos() const;
void SetCursorPos(QPoint const& pos);
ISurfaceMap const* GetSurfaceMap() const;
};
Тоже не бог весть что, но позволяет достигнуть желаемого эффекта («описания полей класса с контролируемым доступом»).
Sign up to leave a comment.
Поля класса доступные по имени с setter и getter в C++