Здравствуйте, Kswapd, Вы писали:
С>>добавить дополнительных проверок в compiled time
K>Это именно то, что убивает C++ — из-за нечеловеческого синтаксиса и ограничений. Если возникла потребность в метапрограммировании и декларативности — пора менять инструмент.
Аннотация:
В статье предложено решение унификации функций доступа к внутренним переменным класса, рассмотрен вопрос локализации в одном файле типов переменных класса. Предложенное решение может быть актуальным для классов, содержащих большое количество внутренних переменных.
У меня вопрос.
Допустим, в новой спецификации поменяли тип одного из полей с двух на четырех-байтный. Ok, для этого поля меняем параметр шаблона ParameterFactory.
Но мне нужно оставить совместимость и со старой версией спецификации, как быть?
Re[2]: Автоматическая генерация интерфейсов классов
5er>У меня вопрос. 5er>Допустим, в новой спецификации поменяли тип одного из полей с двух на четырех-байтный. Ok, для этого поля меняем параметр шаблона ParameterFactory. 5er>Но мне нужно оставить совместимость и со старой версией спецификации, как быть?
К сожалению не будут они полностью совместимыми. Последствия такого изменения надо рассматривать в каждом конкретном случае отдельно. Примером может быть параметр TTG (transmit/receive transition gap -- задержка времени переключения антенны с режима передачи на приём) в сообщении DCD. В стандарте IEEE 802.16-2004 размер поля был 1 байт, через пять лет внесли изменения в стандарт и в IEEE 802.16-2009 размер поля стал 2 байта для станций, работающих во временном дуплексе, и 4 байта для станций, работающих в частотном полудуплексе.
Сериализация сообщения происходит на базовой станции, которая посылает в эфир сообщение DCD с обновлённым полем TTG, например, длиной 2 байта. Приём DCD происходит на абонентской станции, к примеру, программное обеспечение которой не было обновлено, и она ожидает размер поля TTG 1 байт. Далее возможно несколько сценариев:
Сценарий 1. Разработчики абонентской станции предвидели, что стандарт новый и заложили динамическое формирование параметров. Встретив в бинарном потоке байт "тип" 0х07, и следующий байт "длина параметра" 0х02, создают через new() двухбайтовую структуру.
Сценарий 2. Под поле TTG жестко выделен 1 байт. Если значение превысит 255, произойдёт срезка.
Сценарий 3. Приёмник контролирует значение длины поля и ожидает размер поля TTG 1 байт. Произойдёт исключение в режиме исполнения (run-time), (как это сейчас реализовано в моём случае) проигнорирует это поле с занесением в лог, посчитав его ошибочным. Здесь всё зависит от принятой политики обработки исключений.
В данном случае самый безболезненный пример Сценария 1 -- полная совместимость.
то произойдёт срезка (Сценарий 2). Хуже того, компилятор, например gcc 4.6.2 "g++ -O0 -g3 -Wall -c -fmessage-length=0 -o src\test_info.o ..\src\test_info.cpp", даже не предупредит о возможных проблемах.
В таком случае я бы рекомендовал переход к POD типам осуществлять следующим способом, который не влечёт никаких накладных расходов:
Здравствуйте, Чигринец Владислав Александрович, Вы писали:
ЧВА>Из пяти функций класса MessageDcd четыре функции относятся к интерфейсу доступа на чтение переменных класса. К ним еще необходимо добавить четыре функции доступа на запись переменных, которые реализуются по аналогии.
DcdMsgType,
DcdConfigChangeCount,
//for TVL parameters:
DcdEirp = 2,
DcdFrequency = 12
public Parameter<DcdParameterIds, DcdMsgType>,
public Parameter<DcdParameterIds, DcdConfigChangeCount>,
public Parameter<DcdParameterIds, DcdEirp>,
public Parameter<DcdParameterIds, DcdFrequency>
чем это, лучше чем нормальный простой аггрегирующий класс (с нормальными типами полей, а не short/char/int/long как у вас) + сериализирующая функция?
И в вашем случае, и в случае с простым аггрегирующим классом, инфа дублируется дважды.
5er>У меня вопрос. 5er>Допустим, в новой спецификации поменяли тип одного из полей с двух на четырех-байтный. Ok, для этого поля меняем параметр шаблона ParameterFactory. 5er>Но мне нужно оставить совместимость и со старой версией спецификации, как быть?
совместимости не будет
даже если взять шаблон
template <int>
class Storage;
то классы:
Storage<1>
и
Storage<2>
это разные типы.
можно писать свои же конверторы, но типы изначально разные
Re[2]: Автоматическая генерация интерфейсов классов
Здравствуйте, Piko, Вы писали:
P>Здравствуйте, Чигринец Владислав Александрович, Вы писали:
ЧВА>>Из пяти функций класса MessageDcd четыре функции относятся к интерфейсу доступа на чтение переменных класса. К ним еще необходимо добавить четыре функции доступа на запись переменных, которые реализуются по аналогии.
P>
Как говорится, за деревьями леса не видно: чем проще пример, тем легче объяснить идею, но, с другой стороны, тем нелепей могут быть методики решения проблемы -- "здесь всё просто, зачем ещё что-то городить?!." Конечно, если структура простая, состоит из простых типов, то класс не нужен, передаём struct и обращаемся по имени к полям структуры. Ни getter'ы ни setter'ы в таком случае не нужны, и здесь я с Вами полностью согласен.
Но что даёт нам объявление имён в перечислении? Вопервых можно автоматически регистрировать обработчики, как это сделано в статье Нетривиальное использование шаблонов. Часть первая., ссылка на которую дана у меня в статье (только будьте внимательны при чтении этой статьи, т.к. html несколько исказил в ней исходные коды). На этом принципе у меня построен десериализатор, для которого не хватило формата статьи. Читабельное имя нужно только для программиста, просматривающего код. Для компилятора и логики работы программы оно не несёт никакой информации. Развивая дальше мысль, скажу, что доступ к параметру, не завязанный на его имя, облегчает строить параметрические (generic) алгоритмы.
P>2. инфа дублируется, DRY нарушен:
P>
P> DcdMsgType,
P> DcdConfigChangeCount,
P> //for TVL parameters:
P> DcdEirp = 2,
P> DcdFrequency = 12
P> public Parameter<DcdParameterIds, DcdMsgType>,
P> public Parameter<DcdParameterIds, DcdConfigChangeCount>,
P> public Parameter<DcdParameterIds, DcdEirp>,
P> public Parameter<DcdParameterIds, DcdFrequency>
P>
P>чем это, лучше чем нормальный простой аггрегирующий класс (с нормальными типами полей, а не short/char/int/long как у вас) + сериализирующая функция? P>И в вашем случае, и в случае с простым аггрегирующим классом, инфа дублируется дважды.
Тоже согласен, ибо выглядит это некрасиво и про дублирование всё верно. Но я неоднократно упоминал как в самой статье (второе предложение заключения), так и здесь на форуме, что дублирования и наследования можно избежать. Наверно надо привести пример, т.к. утверждение получается голословным и не воспринимается. Вот далеко не изящный пример, написаный "на коленке", в котором избегаем дублирование без решения проблемы с наследованием:
enum DcdParameterIds
{
DcdMsgType,
DcdConfigChangeCount,
//for TVL parameters:
DcdEirp = 2,
DcdFrequency = 12
, DCD_ID_NR
};
template<>
class MessageSet<DcdId>:
public ParameterAutoAdd<DCD_ID_NR>
{};
template<DcdParameterIds _IdMax>
class ParameterAutoAdd :
public Parameter<DcdParameterIds, static_cast<DcdParameterIds>(_IdMax - 1)>,
public ParameterAutoAdd<static_cast<DcdParameterIds>(_IdMax - 1)>
{};
template<>
class ParameterAutoAdd<static_cast<DcdParameterIds>(0)>
public Parameter<DcdParameterIds, static_cast<DcdParameterIds>(0)>
{};
Всё, теперь добавляем новое имя параметра, например new_mem_var, как элемент перечисления перед DCD_ID_NR, и определяем его характеристики в классе Parameter<DcdParameterIds, new_mem_var>. Никакого дублирования, компилятор сам добавит этот параметр в класс MessageSet.
Понял о чём идёт речь. Ответ мой будет кратким: "Избегайте макросов". У меня в статье есть ссылка номер 2 на книгу "Саттер Г., Александреску А. Стандарты программирования на С++. 101 правило и рекомендация", посмотрите рекомендацию номер 16 на странице 44 (желательно посмотреть её развёрнутое обоснование, в http://www.rus-lib.com/book/355501 лишь аннотация к рекомендации. От макросов не уйти в С программировании, но в С++ со многими задачами справляются шаблоны.
Re[4]: Автоматическая генерация интерфейсов классов
Здравствуйте, смит, Вы писали:
С>... P>>>3. вы слышали о технике X-Macros ( http://stackoverflow.com/questions/264269/what-is-a-good-reference-documenting-patterns-of-use-of-x-macros-in-c-or-possib ) ? С>>сейчас почитаю,.. С>Понял о чём идёт речь. Ответ мой будет кратким: "Избегайте макросов". У меня в статье есть ссылка номер 2 на книгу "Саттер Г., Александреску А. Стандарты программирования на С++. 101 правило и рекомендация", посмотрите рекомендацию номер 16 на странице 44 (желательно посмотреть её развёрнутое обоснование, в http://www.rus-lib.com/book/355501 лишь аннотация к рекомендации. От макросов не уйти в С программировании, но в С++ со многими задачами справляются шаблоны.
знаю я все ваши аргументы про макросы, и Саттера и Аллександреску я читал.
Макросы нужно использовать обоснованно, также как и шаблоны.
И, если макросы многократно упрощают реализацию и интерфейс по сравнению с шаблонами, то почему бы и нет?
Re[3]: Автоматическая генерация интерфейсов классов
Здравствуйте, смит, Вы писали:
ЧВА>>>Из пяти функций класса MessageDcd четыре функции относятся к интерфейсу доступа на чтение переменных класса. К ним еще необходимо добавить четыре функции доступа на запись переменных, которые реализуются по аналогии. P>>
P>>1. зачем здесь вообще getter'ы и setter'ы ? С>Как говорится, за деревьями леса не видно: чем проще пример, тем легче объяснить идею, но, с другой стороны, тем нелепей могут быть методики решения проблемы -- "здесь всё просто, зачем ещё что-то городить?!." Конечно, если структура простая, состоит из простых типов, то класс не нужен, передаём struct и обращаемся по имени к полям структуры. Ни getter'ы ни setter'ы в таком случае не нужны, и здесь я с Вами полностью согласен.
Если вы сами знаете, что они вам не нужны, то зачем в статье оставили их?
Ведь вы даже это используете как "недостаток":
Из пяти функций класса MessageDcd четыре функции относятся к интерфейсу доступа на чтение переменных класса. К ним еще необходимо добавить четыре функции доступа на запись переменных, которые реализуются по аналогии. Итого восемь функций интерфейса доступа.
:::
Другим недостатком явного объявления типа параметра является то, что изменение размера переменной или ее типа в исходном техническом задании влекут за собой соответствующие изменения в листингах 1 и 2, а именно: в объявлении переменной, в интерфейсах доступа к ней, и во всех местах кода, где она назначается.
Если бы у вас использовались нормальные типы, а не char/short/int/long:
1. не нужны были бы getter'ы/setter'ы. Вы ведь их тут влепили на случай всякий валидаций, инвариантов?
2. Изменение типа параметра не "повлекло бы изменения в листингах 1 и 2", так как изменение нижележащих типов (внутри аггрегируемых классов) в интерфейсе просто были бы не видны.
С>Но что даёт нам объявление имён в перечислении? Вопервых можно автоматически регистрировать обработчики, как это сделано в статье Нетривиальное использование шаблонов. Часть первая., ссылка на которую дана у меня в статье (только будьте внимательны при чтении этой статьи, т.к. html несколько исказил в ней исходные коды). На этом принципе у меня построен десериализатор, для которого не хватило формата статьи. Читабельное имя нужно только для программиста, просматривающего код. Для компилятора и логики работы программы оно не несёт никакой информации.
тоже самое дало бы: аггрегирующий класс + сереализирующая функция (на самом деле даже не функция, нужно просто представить схему данных в таком формате, в котором её можно было бы многократно использовать в разных местах. сереализирующая функция может автоматом "генерироваться" на основе этой схемы. и тут как раз метапрограммирование уместно)
С>Развивая дальше мысль, скажу, что доступ к параметру, не завязанный на его имя, облегчает строить параметрические (generic) алгоритмы.
ну так определите простой аггрегирующий класс + схема данных в каком либо формате: и интерфейс для пользователя будет простым, и что главное намного менее подверженный ошибкам, и схему можно обходить как только вздумается.
P>>чем это, лучше чем нормальный простой аггрегирующий класс (с нормальными типами полей, а не short/char/int/long как у вас) + сериализирующая функция? P>>И в вашем случае, и в случае с простым аггрегирующим классом, инфа дублируется дважды. С>Тоже согласен, ибо выглядит это некрасиво и про дублирование всё верно. Но я неоднократно упоминал как в самой статье (второе предложение заключения), так и здесь на форуме, что дублирования и наследования можно избежать. Наверно надо привести пример, т.к. утверждение получается голословным и не воспринимается. Вот далеко не изящный пример, написаный "на коленке", в котором избегаем дублирование без решения проблемы с наследованием:
:: С>Всё, теперь добавляем новое имя параметра, например new_mem_var, как элемент перечисления перед DCD_ID_NR, и определяем его характеристики в классе Parameter<DcdParameterIds, new_mem_var>. Никакого дублирования, компилятор сам добавит этот параметр в класс MessageSet.
дублирование осталось, но уже меньше: нужно указывать первый (либо 0) и последний (либо следующий за последним) элементы. можно конечно и не указывать и обходить заведомо больший диапазон compile time, но это то ещё уродство.
Также, нет никакой гарантии для пользователя что для всех элементов enum'а есть специализация parameter<>, что нет лишней специализации (например DcdEirp+3 ). В общем error-prone
А главное вы не ответили чем это всё лучше простого аггрегирующего класса (с нормальной реализацией, а не как у вас с getters and setters).
P>>4. вы слышали о code generation? С>может быть, был бы благодарен за ссылочку.
Код C++ можно генерировать программой/скриптом
1. Делаете описание схемы с помощью какого-нибудь подходящего формата, да хоть простого xml.
2. Парсите xml, естественно не вручную.
3. Генерируете код (необязательно весь. при этом до сих пор можно использовать рукописные шаблоны, так где это уместно)
А главное, результат будет на порядок более надёжный и на порядок более лёгкий в поддержке и использовании. Как следствие, намного более дешёвый в плане $$$$
Re[5]: Автоматическая генерация интерфейсов классов
Здравствуйте, Piko, Вы писали:
P>Здравствуйте, смит, Вы писали:
С>>... P>>>>3. вы слышали о технике X-Macros ( http://stackoverflow.com/questions/264269/what-is-a-good-reference-documenting-patterns-of-use-of-x-macros-in-c-or-possib ) ? С>>>сейчас почитаю,.. С>>Понял о чём идёт речь. Ответ мой будет кратким: "Избегайте макросов". У меня в статье есть ссылка номер 2 на книгу "Саттер Г., Александреску А. Стандарты программирования на С++. 101 правило и рекомендация", посмотрите рекомендацию номер 16 на странице 44 (желательно посмотреть её развёрнутое обоснование, в http://www.rus-lib.com/book/355501 лишь аннотация к рекомендации. От макросов не уйти в С программировании, но в С++ со многими задачами справляются шаблоны.
P>знаю я все ваши аргументы про макросы, и Саттера и Аллександреску я читал. P>Макросы нужно использовать обоснованно, также как и шаблоны. P>И, если макросы многократно упрощают реализацию и интерфейс по сравнению с шаблонами, то почему бы и нет?
Я не призываю к священной войне против макросов, и нет у меня фанатизма по отношению к шаблонам. Макросы активно применяются в библиотеке boost, у Алксандреску линеаризация создания списков типов и диагностика статических проверок построены на макросах. Просто, зная недостатки макросов, а в вышеупомянутой ркомендации Саттер их красноречиво расписал, цитата "...Основная проблема с макросами С++ заключается в том, что они выглядят гораздо привлекательнее, чем являются таковыми на самом деле...", лично отдаю предпочтение шаблонам. Здесь есть полный текст: http://lib.ec/b/355501/read
Макросы тоже применял для генерации классов, но это был случай, когда надо было скрыть некоторые детали реализации библиотеки (недостаток шаблонов в том, что код нельзя скрыть в *.срр файле). Пришлось скрывать через pimpl-идиому и макросы.
Re[6]: Автоматическая генерация интерфейсов классов
Здравствуйте, смит, Вы писали:
P>>>>>3. вы слышали о технике X-Macros ( http://stackoverflow.com/questions/264269/what-is-a-good-reference-documenting-patterns-of-use-of-x-macros-in-c-or-possib ) ? С>>>>сейчас почитаю,.. С>>>Понял о чём идёт речь. Ответ мой будет кратким: "Избегайте макросов". У меня в статье есть ссылка номер 2 на книгу "Саттер Г., Александреску А. Стандарты программирования на С++. 101 правило и рекомендация", посмотрите рекомендацию номер 16 на странице 44 (желательно посмотреть её развёрнутое обоснование, в http://www.rus-lib.com/book/355501 лишь аннотация к рекомендации. От макросов не уйти в С программировании, но в С++ со многими задачами справляются шаблоны. P>>знаю я все ваши аргументы про макросы, и Саттера и Аллександреску я читал. P>>Макросы нужно использовать обоснованно, также как и шаблоны. P>>И, если макросы многократно упрощают реализацию и интерфейс по сравнению с шаблонами, то почему бы и нет?
С>Я не призываю к священной войне против макросов, и нет у меня фанатизма по отношению к шаблонам.
А я как бы тоже не призываю просто в этом конкретном случае пара макросов уменьшило бы повторяемость
С>Макросы активно применяются в библиотеке boost, у Алксандреску линеаризация создания списков типов и диагностика статических проверок построены на макросах.Просто, зная недостатки макросов, а в вышеупомянутой ркомендации Саттер их красноречиво расписал, цитата "...Основная проблема с макросами С++ заключается в том, что они выглядят гораздо привлекательнее, чем являются таковыми на самом деле...",
Я же сказал, я знаю все ваши аргументы про макросы
С>лично отдаю предпочтение шаблонам.
и? я тоже предпочитаю шаблоны, а не макросы, но в некоторых случаях макросы необходимы
С>Здесь есть полный текст: http://lib.ec/b/355501/read
я вам показал технику X-Macro, использование которой привело бы, к более рациональной реализации и интерфейсу, я не пойму к чему вы начали рассказывать про ваш вкусы и опыт работы с макросами
Re[4]: Автоматическая генерация интерфейсов классов
Здравствуйте, Piko, Вы писали:
P>Здравствуйте, смит, Вы писали:
ЧВА>>>>Из пяти функций класса MessageDcd четыре функции относятся к интерфейсу доступа на чтение переменных класса. К ним еще необходимо добавить четыре функции доступа на запись переменных, которые реализуются по аналогии. P>>>
P>>>1. зачем здесь вообще getter'ы и setter'ы ?
... P>Если бы у вас использовались нормальные типы, а не char/short/int/long: P>1. не нужны были бы getter'ы/setter'ы. Вы ведь их тут влепили на случай всякий валидаций, инвариантов? P>2. Изменение типа параметра не "повлекло бы изменения в листингах 1 и 2", так как изменение нижележащих типов (внутри аггрегируемых классов) в интерфейсе просто были бы не видны.
... С>>Всё, теперь добавляем новое имя параметра, например new_mem_var, как элемент перечисления перед DCD_ID_NR, и определяем его характеристики в классе Parameter<DcdParameterIds, new_mem_var>. Никакого дублирования, компилятор сам добавит этот параметр в класс MessageSet.
P>дублирование осталось, но уже меньше: нужно указывать первый (либо 0) и последний (либо следующий за последним) элементы. можно конечно и не указывать и обходить заведомо больший диапазон compile time, но это то ещё уродство. P>Также, нет никакой гарантии для пользователя что для всех элементов enum'а есть специализация parameter<>, что нет лишней специализации (например DcdEirp+3 ). В общем error-prone
Последний элемент перечисления надо указывать! Лишних специализаций не будет, либо класс будет пустым без вопрощения в объект, либо будет регистрироваться класс по умолчанию, шаблонами сделать это легко. Время компиляции увеличится. Генерация кода через макросы и code generation тоже не уменьшают время пре-компиляции.
P>А главное вы не ответили чем это всё лучше простого аггрегирующего класса (с нормальной реализацией, а не как у вас с getters and setters).
Приведите, пожалуйста, пример Вашего кода, может я неправильно интерпретирую Ваши мысли.
Могу сделать инкапсуляцию шаблонами не через наследование, а через механизм включения. Предыдущий пример был лишь примером "на скорую руку", как избавиться от дублирования кода. Меня терзают смутные сомнения, что в контексте решения Ваших задач идеи, изложенные в статье, не имеют практической значимости. Вероятно мне надо дальше дописывать статью, чтобы показать выгоды от использования предложенного интерфейса доступа (но с этим не так быстро). Повторюсь, без использования шаблонных алгоритмов при дальнейшей работе с классом Message<>, большого смысла в предложенном мной интерфейсе доступа действительно нет. Надеюсь у Вас нет "аллергии" на шаблоны?
P>>>4. вы слышали о code generation? С>>может быть, был бы благодарен за ссылочку. P>Код C++ можно генерировать программой/скриптом P>1. Делаете описание схемы с помощью какого-нибудь подходящего формата, да хоть простого xml. P>2. Парсите xml, естественно не вручную. P>3. Генерируете код (необязательно весь. при этом до сих пор можно использовать рукописные шаблоны, так где это уместно)
P>А главное, результат будет на порядок более надёжный и на порядок более лёгкий в поддержке и использовании. Как следствие, намного более дешёвый в плане $$$$
Вам кажется, что так лучше и надёжней... Вероятно отпугивает не простой код, требующий квалификации для развития и поддержки... Но мы же не ищем лёгких путей!? ... Важны идеи!.. Прийдёт час и они окажутся востребованы... Всё новое рождается в муках... Методику генерации С++ кода из xml файлов с помощью xslt преобразования тоже применяем.
Re[5]: Автоматическая генерация интерфейсов классов
Здравствуйте, смит, Вы писали:
ЧВА>>>>>Из пяти функций класса MessageDcd четыре функции относятся к интерфейсу доступа на чтение переменных класса. К ним еще необходимо добавить четыре функции доступа на запись переменных, которые реализуются по аналогии. P>>>>
P>>>>1. зачем здесь вообще getter'ы и setter'ы ? С>... P>>Если бы у вас использовались нормальные типы, а не char/short/int/long: P>>1. не нужны были бы getter'ы/setter'ы. Вы ведь их тут влепили на случай всякий валидаций, инвариантов? P>>2. Изменение типа параметра не "повлекло бы изменения в листингах 1 и 2", так как изменение нижележащих типов (внутри аггрегируемых классов) в интерфейсе просто были бы не видны. С>Частично на это я ответил здесь... нет?..
вы делаете акцент на политику хранения.
А вы можете перевести практические варианты хранения о которых вы думаете?
И чем они лучше чем простой аггрегирующий класс?
Если вы делаете абстракцию на политику хранения, ведь значит что вы предполагаете разные варианты политики хранения.
С>... С>>>Всё, теперь добавляем новое имя параметра, например new_mem_var, как элемент перечисления перед DCD_ID_NR, и определяем его характеристики в классе Parameter<DcdParameterIds, new_mem_var>. Никакого дублирования, компилятор сам добавит этот параметр в класс MessageSet. P>>дублирование осталось, но уже меньше: нужно указывать первый (либо 0) и последний (либо следующий за последним) элементы. можно конечно и не указывать и обходить заведомо больший диапазон compile time, но это то ещё уродство. P>>Также, нет никакой гарантии для пользователя что для всех элементов enum'а есть специализация parameter<>, что нет лишней специализации (например DcdEirp+3 ). В общем error-prone С>Лишних специализаций не будет,
где гарантии того, что кто-нибудь не сделает лишнюю специализацию, для числа которого нет в enum, например "DcdEirp+3" ?
С>либо класс будет пустым без вопрощения в объект, либо будет регистрироваться класс по умолчанию,
как будет регистрироваться класс по-умолчанию, когда например программист забыл сделать специализацию parameter для DcdConfigChangeCount ?
С>шаблонами сделать это легко. Время компиляции увеличится. Генерация кода через макросы и code generation тоже не уменьшают время пре-компиляции.
А я ничего не говорил про время компиляции. я наоборот готов жертвовать временем компиляции ради надёжности
P>>А главное вы не ответили чем это всё лучше простого аггрегирующего класса (с нормальной реализацией, а не как у вас с getters and setters). С>Приведите, пожалуйста, пример Вашего кода, может я неправильно интерпретирую Ваши мысли.
с getter'ами, setter'ами (ну не прям тут, но вы сказали что setter'ы нужны), с конкретным low-level типами.
Сами же сказали что в этом коде проблемы с getters/setters, и low-level типами. Показали нагромождённое решение.
Я же предлагаю вам подумать об более простом и элегантном способе:
в котором нет тех двух проблем с getters/setters и привязки к low-level типам, которыми вы оправдываете свою статью.
С>Могу сделать инкапсуляцию шаблонами не через наследование, а через механизм включения. Предыдущий пример был лишь примером "на скорую руку", как избавиться от дублирования кода. Меня терзают смутные сомнения, что в контексте решения Ваших задач идеи, изложенные в статье, не имеют практической значимости. Вероятно мне надо дальше дописывать статью, чтобы показать выгоды от использования предложенного интерфейса доступа (но с этим не так быстро).
Я где-нибудь здесь говорил про решение моих задач?
С>Повторюсь, без использования шаблонных алгоритмов при дальнейшей работе с классом Message<>, большого смысла в предложенном мной интерфейсе доступа действительно нет.
То есть цель вашего интерфейса: использование шаблонных алгоритмов?
Не решение конкретных задач, а именно использование техники?
С>Надеюсь у Вас нет "аллергии" на шаблоны?
А с чего вы взяли, что у меня может быть аллергия на шаблоны?
То что я вам указал на то, что ваша реализация неадекватная (пусть она и использует шаблоны), разве говорит о том, что мне не нравятся шаблоны?
это похоже на:
(1): — Ты зачем бьёшь молотком себе по пальцам?
(2): — Надеюсь у тебя не аллергии на молоток?
P>>>>4. вы слышали о code generation? С>>>может быть, был бы благодарен за ссылочку. P>>Код C++ можно генерировать программой/скриптом P>>1. Делаете описание схемы с помощью какого-нибудь подходящего формата, да хоть простого xml. P>>2. Парсите xml, естественно не вручную. P>>3. Генерируете код (необязательно весь. при этом до сих пор можно использовать рукописные шаблоны, так где это уместно) P>>А главное, результат будет на порядок более надёжный и на порядок более лёгкий в поддержке и использовании. Как следствие, намного более дешёвый в плане $$$$ С>Вам кажется, что так лучше и надёжней... Вероятно отпугивает не простой код, требующий квалификации для развития и поддержки... Но мы же не ищем лёгких путей!? ... Важны идеи!.. Прийдёт час и они окажутся востребованы... Всё новое рождается в муках... Методику генерации С++ кода из xml файлов с помощью xslt преобразования тоже применяем.
Вы понимаете, что сложные в поддержке техники адекватно использовать, когда они реально дают преимущество, которое трудно достичь более простыми способами.
Не один адекватный профессиональный программист не будет необоснованно использовать сложные техники, только ради использования, когда есть более элегантные, надёжные, и простые решения.
Тот же Александреску говорит, что он хочет упростить техники метапрограммирования, например добавить static if, чтобы заменить enable_if, неестественное наследование и т.п.
В вашем примере практического обоснования я не увидел. Быть может потому, что вы не ведите простых и элегантных решений. Вкус приходит с опытом.
Re[6]: Автоматическая генерация интерфейсов классов
Здравствуйте, Piko, Вы писали:
P>Здравствуйте, смит, Вы писали:
ЧВА>>>>>>Из пяти функций класса MessageDcd четыре функции относятся к интерфейсу доступа на чтение переменных класса. К ним еще необходимо добавить четыре функции доступа на запись переменных, которые реализуются по аналогии. P>>>>>
P>>>>>1. зачем здесь вообще getter'ы и setter'ы ? С>>... С>>>>Всё, теперь добавляем новое имя параметра, например new_mem_var, как элемент перечисления перед DCD_ID_NR, и определяем его характеристики в классе Parameter<DcdParameterIds, new_mem_var>. Никакого дублирования, компилятор сам добавит этот параметр в класс MessageSet. P>>>дублирование осталось, но уже меньше: нужно указывать первый (либо 0) и последний (либо следующий за последним) элементы. можно конечно и не указывать и обходить заведомо больший диапазон compile time, но это то ещё уродство. P>>>Также, нет никакой гарантии для пользователя что для всех элементов enum'а есть специализация parameter<>, что нет лишней специализации (например DcdEirp+3 ). В общем error-prone С>>Лишних специализаций не будет,
P>где гарантии того, что кто-нибудь не сделает лишнюю специализацию, для числа которого нет в enum, например "DcdEirp+3" ?
Так специализацию делать нельзя. Специализация -- это добавление новой информации, делаем её ручками и используем идентификаторы из перечисления. Поэтому эти идентификаторы имеют удобочитаемое для программиста имя, иначе писали бы программы так:
Компилятор "скушает" такой код, и программа может работать правильно. Но сам по себе код "DcdEirp+3" не безопасный и ничем не лучшеприведённого перечисления. "DcdEirp+3" допустимо только в том случае, если в исходном стандарте (ТЗ, IEEE 802.16) указаны относительные смещения, тогда мы следуем строго стандарту. И даже в этом случае специализировать надо DcdTTG, а не DcdEirp+3. Но это уже банальности.
С>>либо класс будет пустым без вопрощения в объект, либо будет регистрироваться класс по умолчанию, С>>шаблонами сделать это легко. P>как будет регистрироваться класс по-умолчанию, когда например программист забыл сделать специализацию parameter для DcdConfigChangeCount ?
: "...В языке С концепция перечислений выглядит незаконченной. В первоначальном варианте языка их не было. Перечисления ввели без всякой охоты в качестве уступки тем, кто настойчиво требовал более основательных символических констант, чем препроцессорные макросы без параметров..."
В данном случае приходится идти на компромисс: либо ручками добавляем каждый параметр и компилятор проверяет, все ли параметры специализированы, как у меня описано в статье. Либо делаем автоматическое добавление параметров с помощью рекурсии шаблонов, и по умолчанию вставляем заглушку. Во втором случае можно добавить препроцессорную директиву а-ля #warning "stub detected". #warning допустим, только (!) если заглушек немного, иначе вреда может быть больше чем пользы -- привыкание не обращать внимания на предупреждения. ( любые, указанные мной недостатки, могут быть использованы не в мою пользу )
P>А я ничего не говорил про время компиляции. я наоборот готов жертвовать временем компиляции ради надёжности
P>>>А главное вы не ответили чем это всё лучше простого аггрегирующего класса (с нормальной реализацией, а не как у вас с getters and setters). С>>Приведите, пожалуйста, пример Вашего кода, может я неправильно интерпретирую Ваши мысли.
P>Я же предлагаю вам подумать об более простом и элегантном способе: P>
"Я Вас умаляю...", присмотритесь, этот стакан наполовину наполнен... Класс Message<> является контейнером, у которого метод GetParametr<>() играет ту же роль, что и opertor[]() или метод at() у std::vector, выбор по индксу. И название метода GetParametr можно поменять на operator или at(), или добавить методы operator и at(). Как и vector, обладающий методами push_back(), begin() и т.д. класс Message имеет свои методы, которые не завязаны только на доступ к полям, нарпимер: SetPdu(), CreatePdu(), Reset() и т.п.
Я не против того, чтобы использовать простые структуры аналогично тому, как в ряде случаев мы используем массивы
Junior programmer не напишет класс std::vector с нуля (хотя, попадаются разные кадры), но использовать у себя в программе std::vector вполне ему по силам. Класс Message<> не простой в реализации, но пользоваться его методами не сложно. Не сложен и алгоритмом добавления параметров.
Следует заметить, что класс Message<> в методе выбора полей возвращает типизированные объекты, а не указатель или ссылку на базовый класс (как было бы в случае массива или вектора), что позволяет реализовать статическую (в режиме компиляции) диспетчеризацию обработки полей.
Другими столави, без контекста использования класса Message<> наша дискуссия будет не очень продуктивна, а в простых ситуациях с Вами трудно не согласиться.
Re[7]: Автоматическая генерация интерфейсов классов
Здравствуйте, смит, Вы писали:
С>Здравствуйте, Piko, Вы писали:
P>>Здравствуйте, смит, Вы писали:
С>Следует заметить, что класс Message<> в методе выбора полей возвращает типизированные объекты, а не указатель или ссылку на базовый класс (как было бы в случае массива или вектора), что позволяет реализовать статическую (в режиме компиляции) диспетчеризацию обработки полей. С>Другими словами, без контекста использования класса Message<> наша дискуссия будет не очень продуктивна, а в простых ситуациях с Вами трудно не согласиться.
Поправлю себя, правильно было бы написать возвращает не типизированные объекты, а объекты разных типов. Ну и в догонку написанному, перечисление enum в моём случае играет роль списка параметров, на основании его легко строить рекурсивные параметрические алгоритмы обработки полей, пробегание по списку, что позволяет в режиме компиляции (по индексу) автоматически регистрировать в классе новый параметр и все обработчики событий, с которыми этот параметр связан, только создав его определение. В случае простой структуры, приведённой Вами, при добавлении нового параметра необходимо будет регистрировать его обработчики в конструкторе вручную, искать по коду и в ручную добавлять его во все места, где требуется однотипная обработка или обработка списком, сложнее применить шаблонные функции. Здесь как раз касаемся деталей внутреннего устройства и контекста использования класса Message.
Re[7]: Автоматическая генерация интерфейсов классов
Здравствуйте, Kswapd, Вы писали:
P>>В вашем примере практического обоснования я не увидел. Быть может потому, что вы не ведите простых и элегантных решений. Вкус приходит с опытом.
K>Поддерживаю. Типичный наворот ради наворота. Для использования и поддержки надо затратить больше труда, чем если тупо вручную размножать классы.
Да кто ж с этим спорит. Разработка идёт поэтапно, от простого к сложному. Какой смысл городить сложные конструкции и generic алгоритмы в начале пути. К ним приходишь по мере развития проекта, композиции и обобщения разработанных структур и алгоритмов. Никто не спорит, что тупо накопи-пастить -- труда много не потребует. Первые реализации и были такими, как указывает Piko. Если обобщение позволяет кратно сократить код и добавить дополнительных проверок в compiled time не добавляя кода, упрощает масштабирование проекта, то смысл опять возвращаться к "мнимой простоте". Имеющий уши слышать, да услышит, имеющий глаза видеть — да увидит...
Re[8]: Автоматическая генерация интерфейсов классов
С>добавить дополнительных проверок в compiled time
Это именно то, что убивает C++ — из-за нечеловеческого синтаксиса и ограничений. Если возникла потребность в метапрограммировании и декларативности — пора менять инструмент.
Re[7]: Автоматическая генерация интерфейсов классов
Здравствуйте, смит, Вы писали:
P>>>>Также, нет никакой гарантии для пользователя что для всех элементов enum'а есть специализация parameter<>, что нет лишней специализации (например DcdEirp+3 ). В общем error-prone С>>>Лишних специализаций не будет, P>>где гарантии того, что кто-нибудь не сделает лишнюю специализацию, для числа которого нет в enum, например "DcdEirp+3" ? С>Так специализацию делать нельзя. Специализация -- это добавление новой информации, делаем её ручками и используем идентификаторы из перечисления. Поэтому эти идентификаторы имеют удобочитаемое для программиста имя, иначе писали бы программы так
:::: С>Компилятор "скушает" такой код, и программа может работать правильно. Но сам по себе код "DcdEirp+3" не безопасный и ничем не лучшеприведённого перечисления. "DcdEirp+3" допустимо только в том случае, если в исходном стандарте (ТЗ, IEEE 802.16) указаны относительные смещения, тогда мы следуем строго стандарту. И даже в этом случае специализировать надо DcdTTG, а не DcdEirp+3. Но это уже банальности.
вы меня не поняли.
вот есть у вас enum, в нём разные элементы и в конце DCD_ID_NR.
вот смотрит пользователь на этот enum и не знает есть ли специализация для DcdConfigChangeCount(то есть вообще сохраняется ли он), есть ли ошибочная специализация "DcdEirp+3", и т.п. Дублирование никуда не делось.
С>>>либо класс будет пустым без вопрощения в объект, либо будет регистрироваться класс по умолчанию, С>>>шаблонами сделать это легко. P>>как будет регистрироваться класс по-умолчанию, когда например программист забыл сделать специализацию parameter для DcdConfigChangeCount ? С>К сожалению перечисления сами по себе имеют ряд недостатков.
ну то есть вы сами понимаете что это не решение проблемы, и упорно продолжаете грызть кактус?
С>В данном случае приходится идти на компромисс: либо ручками добавляем каждый параметр и компилятор проверяет, все ли параметры специализированы, как у меня описано в статье. Либо делаем автоматическое добавление параметров с помощью рекурсии шаблонов, и по умолчанию вставляем заглушку.
ну то есть вы понимаете что оба варианта пахнут, и проигрывают X-Macros и Code Generation, но продолжаете их поддерживать?
Кстати, они проигрывают и аггрегирующему классу, хотя у него и те же проблемы, но он ведь намного легче и элегантей, и менее подвержен ошибкам.
P>>>>А главное вы не ответили чем это всё лучше простого аггрегирующего класса (с нормальной реализацией, а не как у вас с getters and setters). С>>>Приведите, пожалуйста, пример Вашего кода, может я неправильно интерпретирую Ваши мысли. P>>Я же предлагаю вам подумать об более простом и элегантном способе: P>>
А главное вы не ответили чем это всё лучше простого аггрегирующего класса (с нормальной реализацией, а не как у вас с getters and setters).
С>Junior programmer не напишет класс std::vector с нуля (хотя, попадаются разные кадры), но использовать у себя в программе std::vector вполне ему по силам. Класс Message<> не простой в реализации, но пользоваться его методами не сложно. Не сложен и алгоритмом добавления параметров.
так зачем его использовать, если есть намного более простые и надёжные варианты?
С>Следует заметить, что класс Message<> в методе выбора полей возвращает типизированные объекты, а не указатель или ссылку на базовый класс (как было бы в случае массива или вектора), что позволяет реализовать статическую (в режиме компиляции) диспетчеризацию обработки полей.
вы не поверите, простой аггрегирующий класс предоставляет простой доступ к типизированным объектам.
схема для обхода его полей может быть реализованны разными вариантами, например как
1. В boost::Serialization (обход полей runtime)
template<class Archive>
void serialize(Archive & ar, const unsigned int version)
{
ar & degrees;
ar & minutes;
ar & seconds;
}
2. либо в стиле tuple, c возможным compile-time обходом полей.
и замете, везде статическая типизация
С>Другими столави, без контекста использования класса Message<> наша дискуссия будет не очень продуктивна, а в простых ситуациях с Вами трудно не согласиться.
ну так вы ведь целую статью написали, а простого примера привести чем ваша реализация лучше простого агрегирующего класса не можете.
Re[8]: Автоматическая генерация интерфейсов классов
Здравствуйте, смит, Вы писали:
С>>Следует заметить, что класс Message<> в методе выбора полей возвращает типизированные объекты, а не указатель или ссылку на базовый класс (как было бы в случае массива или вектора), что позволяет реализовать статическую (в режиме компиляции) диспетчеризацию обработки полей. С>>Другими словами, без контекста использования класса Message<> наша дискуссия будет не очень продуктивна, а в простых ситуациях с Вами трудно не согласиться. С>Поправлю себя, правильно было бы написать возвращает не типизированные объекты, а объекты разных типов. Ну и в догонку написанному, перечисление enum в моём случае играет роль списка параметров, на основании его легко строить рекурсивные параметрические алгоритмы обработки полей, пробегание по списку, что позволяет в режиме компиляции (по индексу) автоматически регистрировать в классе новый параметр и все обработчики событий, с которыми этот параметр связан, только создав его определение. С>В случае простой структуры, приведённой Вами, при добавлении нового параметра необходимо будет регистрировать его обработчики в конструкторе вручную, искать по коду и в ручную добавлять его во все места, где требуется однотипная обработка или обработка списком, сложнее применить шаблонные функции. Здесь как раз касаемся деталей внутреннего устройства и контекста использования класса Message.
В случае простой структуры, всё то же самое тоже реализуемо, вы просто этого не понимаете.
Я привёл в предыдущем примере два варианта как это всё реализуется. И заметьте, у пользователя в руках всё ещё очень простой и надёжный интерфейс, которого нет в вашем варианте.
Re[8]: Автоматическая генерация интерфейсов классов
Здравствуйте, смит, Вы писали:
P>>>В вашем примере практического обоснования я не увидел. Быть может потому, что вы не ведите простых и элегантных решений. Вкус приходит с опытом. K>>Поддерживаю. Типичный наворот ради наворота. Для использования и поддержки надо затратить больше труда, чем если тупо вручную размножать классы.
С>Да кто ж с этим спорит. Разработка идёт поэтапно, от простого к сложному. Какой смысл городить сложные конструкции и generic алгоритмы в начале пути. К ним приходишь по мере развития проекта, композиции и обобщения разработанных структур и алгоритмов.
проблема не обобщении. проблема в том, что у вас действительно "наворот ради наворота", который не решает никаких проблем, которые были в более элегантном варианте.
С>Никто не спорит, что тупо накопи-пастить -- труда много не потребует. Первые реализации и были такими, как указывает Piko.
А я не призывал копипастить
Проблема в том, что вы не понимаете, что те же самые задачи решаются более элегантно и надёжно, и без копипасты.
С>Если обобщение позволяет кратно сократить код и добавить дополнительных проверок в compiled time не добавляя кода, упрощает масштабирование проекта, то смысл опять возвращаться к "мнимой простоте". Имеющий уши слышать, да услышит, имеющий глаза видеть — да увидит...
ну вот опять...
Вы увидели сложное и ненадёжное решение, но не видите более лёгких и надёжных, в которых также нет копипасты, есть compile time проверки, и масштабируемость лучше чем вашем варианте, так как проще на порядок.
У вас почему-то более простое решение, ассоциируется с копипастой, а любое решение с шаблонами как "обобщение разработанных структур..." и априори более лучшее.
Re[9]: Автоматическая генерация интерфейсов классов
Здравствуйте, Piko, Вы писали:
P>Здравствуйте, смит, Вы писали:
P>>>>В вашем примере практического обоснования я не увидел. Быть может потому, что вы не ведите простых и элегантных решений. Вкус приходит с опытом. K>>>Поддерживаю. Типичный наворот ради наворота. Для использования и поддержки надо затратить больше труда, чем если тупо вручную размножать классы.
... P>ну вот опять... P>Вы увидели сложное и ненадёжное решение, но не видите более лёгких и надёжных, в которых также нет копипасты, есть compile time проверки, и масштабируемость лучше чем вашем варианте, так как проще на порядок. P>У вас почему-то более простое решение, ассоциируется с копипастой, а любое решение с шаблонами как "обобщение разработанных структур..." и априори более лучшее.
C копипастой я малость утрировал. Я прекрасно понимаю Ваше решение, понимаю и то, что моё решение Вы считаете сложным и ненадёжным, наворотом ради наворота. Я не утверждал, что любое решение с шаблонами как "обобщение разработанных структур..." априори более лучшее.
Re[10]: Автоматическая генерация интерфейсов классов
P>>>>>В вашем примере практического обоснования я не увидел. Быть может потому, что вы не ведите простых и элегантных решений. Вкус приходит с опытом. K>>>>Поддерживаю. Типичный наворот ради наворота. Для использования и поддержки надо затратить больше труда, чем если тупо вручную размножать классы. С>... P>>ну вот опять... P>>Вы увидели сложное и ненадёжное решение, но не видите более лёгких и надёжных, в которых также нет копипасты, есть compile time проверки, и масштабируемость лучше чем вашем варианте, так как проще на порядок. P>>У вас почему-то более простое решение, ассоциируется с копипастой, а любое решение с шаблонами как "обобщение разработанных структур..." и априори более лучшее.
С>C копипастой я малость утрировал. Я прекрасно понимаю Ваше решение, понимаю и то, что моё решение Вы считаете сложным и ненадёжным, наворотом ради наворота.
У вас есть какие-то конкретные обоснования, почему вы собираетесь использовать в реальном проекте ваше решение, а не более простое и надёжное?
Какие, по вашему мнению, ваше решение имеет преимущества по сравнению с более простым?
Приведите конкретный список, мы пройдёмся по нему и решим что верно, а что нет.
Re[11]: Автоматическая генерация интерфейсов классов
Здравствуйте, Piko, Вы писали:
P>У вас есть какие-то конкретные обоснования, почему вы собираетесь использовать в реальном проекте ваше решение, а не более простое и надёжное?
P>Какие, по вашему мнению, ваше решение имеет преимущества по сравнению с более простым? P>Приведите конкретный список, мы пройдёмся по нему и решим что верно, а что нет.
Понятие сложный и простой -- качественные, что для меня может быть сложным, для вас оказывается простым и наоборот. И "сложный" -- не синоним "ненадёжный". Но не в этом суть. Я попробую кратко объяснить, каким мне видится Ваша позиция, чтобы лучше понимать друг друга. Есть замечательная, на мой взгляд, статья Нетривиальное использование шаблонов. В конце статьи автор приводит пример:
И пусть мне автор докажет, что его решение личше моего... Ах, унего по условию массив операций,.. и что?.. надо городить шаблоны? switch!!1 куда проще! любой джуниор поймёт, с поддержкой проблем нет... А условие "нельзя использовать" -- надуманное, дабы написать навороты-ради-наворотов!
Но это к слову, о чём я упомянул ранее, написав "...в простых ситуациях с Вами трудно не согласиться..."
Я не утверждаю, что Ваше решение не имеет право на жизнь, но простота Вашего решения сомнительна. Какова мера отвественности вашего класса (структуры) Message? прямо скажем, небольшая, набор параметров. Что эта структура может с ними делать? хранить их вместе, все алгоритмы прописываем вручную для каждого структуры. Таких структур может быть больше двухсот (сейчас стандатром IEEE 802.16 определено 70 типов сообщений). Поменялся алгоритм сериализации -- пробежались по 70 классам, внесли изменения. Добавили в структуру ещё одно поле, посмотрели методы, где используются соседние поля структуры, добавили в алгоритм и новое поле. Это же структура, набор несвязанных полей, объединенных только бизнес-логикой. В DCD структуре поля, характеризующие нисходящий радиоканал, в UCD структуре поля, характеризующие восходящий радиоканал, в RNG_REQ структуре поля, описывающие параметры запроса юстировки (ranging) и т.д.. Очень простое решение, не спорю с этим, всё добавляем вручную.
То, что я предлагаю, не сильно отличается от того, что предложили Вы, только снял часть ручной работы, возложил её на компилятор. Человек, легко оперирующий шаблонами не встретит здесь трудностей. Не опускаться же нам всё время в своей работе на уровень тех, кто не понимает достоинств параметрического программирования.
И так, мера ответственности класса Message:
1. это контейнер, хранящий набор параметров,
2. должен уметь сериализировать-десериализировать параметры, которые в себе содержит, в соответствии с протоколом IEEE 802.16.
3. должен беспечить доступ к параметрам.
И так, я создаю новое сообщение, например UCD:
template<>
class Message<UcdId> : public MessageInterface<MessageSet<UcdId>, UcdParameterIds>
{};
Всё, сообщение создано, компилятор знает идентификатор сообщения UcdId (понадобится дли диспетчера сообщений), и решил все три задачи:
1. создал контейнер, для хранения набора параметров,
2. создал функции сериализации-десериализации, ещё несколько функций, специфичных для работы с messages
3. создал функции, обеспечивающие доступ к полям (параметрам).
Задачи решены, а я не так много написал кода, только задействовал заголовки описанной в статье библиотеки, поскольку библиотека отлажена, сообщение должно сформироваться правильно.
Сам компилятор не придумает параметры, читать стандарт IEEE 802.16 не умеет, мне необходимо ему сообщить список параметров:
enum UcdParameterIds
{
UcdMsgType,
UcdConfigChangeCount,
UcdRngBackoffStart,
UcdRngBackoffSEnd,
...
//for TVL parameters:
ContentionTimeout,
UcdFrequency,
ULRadioResources
...
,UCD_ID_NR // может быть адаптирован под макрос, чтобы не определять его в классе ParameterAutoAdd<>
};
Список готов. Чтобы сериализаторы-дасериализаторы и аллокаторы памяти для параметров работали правильно, необходимо их описать. Все параметры описывать не буду, для примера достаточно одного:
template <>
class Parameter<UcdId, UcdFrequency>
: public ParameterFactory<UcdId, UcdFrequency, Tlv, 4, Unsigned>
{};
Всё, компилятор получил всю необходимую для него информацию: какому сообщению принадлежит параметр, имя (идентификатор доступа), тип сериализации-десериализации, минимальный объём памяти в байтах для аллокатора (он же размер поля Length сериализатора), контроль знаковое/беззнаковое значение.
Повторюсь, в коде нигде я ничего не правлю, каждая новая строка кода -- это добавление новой информации и только, всю остальную (рутинную) работу делает за меня компилятор: добавление проверок (compiled time, run-time для проверки входных и выходных инвариантов, генерация событий а-ля notify(Detected<UcdParameterIds>() и т.п.),.
Что дальше? А дальше действуем согласно бизнес логики, которая класса Message<> уже не касается. Класс Message<> выполняет все возложенные на него задачи, и всё, что мы добавим, не должно его волновать и на него влиять, иначе будeт нарушены принципы SRP и OCP из SOLID. С другой стороны, все объекты и функции, которые работают с параметрами не должны знать о внутреннем устройстве Message<>. В качестве примера беру тот же UcdFrequency. Видоизменяю его, добавляю в него интерфейс внешней логики (сообщаю новую информацию, не влияя на старую):
template <>
class Parameter<UcdId, UcdFrequency>
: public ParameterFactory<UcdId, UcdFrequency, Tlv, 4, Unsigned>
public Frequency
{
private:
int get_frequency_Hz_units()
{
// compiled/run time validationreturn GetValue() * 1000;
}
void set_frequency_Hz_units(int value){/* соответствующий код */}
};
Более подробно по интерфейсу Frequency можно глянуть ветку обсуждений здесь, т.к. объяснения идеи и предлагаемого принципа разработки это не важно.
Представим себе, что где-то другой программист написал HAL модуль управления высокочастотным синтезатором:
class Radiohead
{
public:
void SetSynthesizer(Frequency fq)
{
int driver_rg = fq.get_value<MHz_units>());
...
io_ctl(IO_WR, ..., driver_rg);
}
};
Пример программы, реализующей логику и взаимодействие модулей:
Всё контролируемо, независимо, управляемо. В коде добавляю только новую информацию, больше относящуюся к предметной области, внутреннее устройство скрыто за библиотекой...
Примерно так...
Re[12]: Автоматическая генерация интерфейсов классов
Здравствуйте, смит, Вы писали:
P>>У вас есть какие-то конкретные обоснования, почему вы собираетесь использовать в реальном проекте ваше решение, а не более простое и надёжное? P>>Какие, по вашему мнению, ваше решение имеет преимущества по сравнению с более простым? P>>Приведите конкретный список, мы пройдёмся по нему и решим что верно, а что нет.
С>Понятие сложный и простой -- качественные, что для меня может быть сложным, для вас оказывается простым и наоборот. И "сложный" -- не синоним "ненадёжный". Но не в этом суть. Я попробую кратко объяснить, каким мне видится Ваша позиция, чтобы лучше понимать друг друга. Есть замечательная, на мой взгляд, статья Нетривиальное использование шаблонов. В конце статьи автор приводит пример:
С>
С>Бегло просматриваю статью... шаблоны, специализация, уродский интерфейс вызова операций в примере... Во наворотил, так здесь есть простое решение:
С>
С>И пусть мне автор докажет, что его решение личше моего... Ах, унего по условию массив операций,.. и что?.. надо городить шаблоны? switch!!1 куда проще! любой джуниор поймёт, с поддержкой проблем нет... А условие "нельзя использовать" -- надуманное, дабы написать навороты-ради-наворотов!
Я статью полностью не читал, но вот буквально первое предложение:
"В общем, такая тема: мне нужно сделать 2 класса: Stack_Numbers и Stack_Operations. Как вы уже поняли в одном хранятся числа, в другом -арифметические операции. Нужно вытаскивать из Stack_Numbers два числа, потом из Stack_Operations операцию, делать ее и результат обратно в Stack_Numbers."
то есть операции хранить нужно явно в динамической структуре, это задача, причём операции могут быть самые разные, и как я понял приходить они могут из вне
Внимание, а теперь вопрос, как вообще какая-то левая статья относится к нашему обсуждению и вашей задаче? Вы просто мастер художественного передёргивания.
С>Но это к слову, о чём я упомянул ранее, написав "...в простых ситуациях с Вами трудно не согласиться..."
вы до сих пор меня не поняли. Вы знаете про expression templates? Так вот, я только за использование этой техники, естественно там где это обоснованно. Да, эта техника может показаться навороченной, но это не наворот ради наворота как у вас.
Re[12]: Автоматическая генерация интерфейсов классов
Здравствуйте, смит, Вы писали:
С>Я не утверждаю, что Ваше решение не имеет право на жизнь, но простота Вашего решения сомнительна. Какова мера отвественности вашего класса (структуры) Message? прямо скажем, небольшая, набор параметров. Что эта структура может с ними делать? хранить их вместе, все алгоритмы прописываем вручную для каждого структуры.
Вы явно даже не пытаетесь понять то о чём я говорю:
1. В boost::Serialization (обход полей runtime)
template<class Archive>
void serialize(Archive & ar, const unsigned int version)
{
ar & degrees;
ar & minutes;
ar & seconds;
}
2. либо в стиле tuple, c возможным compile-time обходом полей.
и замете, везде статическая типизация
:::
3. вы слышали о технике X-Macros?
4. вы слышали о code generation?
В моём подходе схема данных тоже присутствует, и её можно обходить и в хвост и в гриву.
И алгоритмы пишутся без оперирования вручную элементами структуры, всё автоматизированно
И у пользователя, простой и надёжный интерфейс
С>Таких структур может быть больше двухсот (сейчас стандатром IEEE 802.16 определено 70 типов сообщений). Поменялся алгоритм сериализации -- пробежались по 70 классам, внесли изменения.
я не предлагал этого, это ваши юношеские фантазии.
например в случае boost::Serialization — поменялась сериализация — допустим новый формат, просто пишется новый Archive, и всё, не по каким классам бегать не надо
С>Добавили в структуру ещё одно поле, посмотрели методы, где используются соседние поля структуры, добавили в алгоритм и новое поле.
опять фантазии.
1 добавили поле в структуру, 2 добавили поле в сехму и всё!
да, есть дублирование, но я уже предлагал и code generation, и X-Macros.
И это дублирование точно также присутствует и в вашем варианте недостаточно просто добавить новое поле в enum.
С>Это же структура, набор несвязанных полей, объединенных только бизнес-логикой. В DCD структуре поля, характеризующие нисходящий радиоканал, в UCD структуре поля, характеризующие восходящий радиоканал, в RNG_REQ структуре поля, описывающие параметры запроса юстировки (ranging) и т.д.. Очень простое решение, не спорю с этим, всё добавляем вручную.
не в ручную, facepalm, вы меня тупо не понимаете
С>То, что я предлагаю, не сильно отличается от того, что предложили Вы, только снял часть ручной работы, возложил её на компилятор.
в моем варианте нет ручной работы, о которой вы фантазируете
но при этом у пользователя намного более простой и надёжный интерфейс
С>Человек, легко оперирующий шаблонами не встретит здесь трудностей. Не опускаться же нам всё время в своей работе на уровень тех, кто не понимает достоинств параметрического программирования.
Вы опять меня не слышите, я понимаю достоинства шаблонного мета-программирования. Но:
С>>Надеюсь у Вас нет "аллергии" на шаблоны?
P>А с чего вы взяли, что у меня может быть аллергия на шаблоны?
P>То что я вам указал на то, что ваша реализация неадекватная (пусть она и использует шаблоны), разве говорит о том, что мне не нравятся шаблоны?
P>это похоже на:
P>(1): — Ты зачем бьёшь молотком себе по пальцам?
P>(2): — Надеюсь у тебя не аллергии на молоток?
[skipped]
С>Задачи решены, а я не так много написал кода, только задействовал заголовки описанной в статье библиотеки, поскольку библиотека отлажена, сообщение должно сформироваться правильно.
в моём варианте много кода писать тоже не нужно
С>Сам компилятор не придумает параметры, читать стандарт IEEE 802.16 не умеет, мне необходимо ему сообщить список параметров:
С>
С>enum UcdParameterIds
С>{
С> UcdMsgType,
С> UcdConfigChangeCount,
С> UcdRngBackoffStart,
С> UcdRngBackoffSEnd,
С> ...
С> //for TVL parameters:
С> ContentionTimeout,
С> UcdFrequency,
С> ULRadioResources
С> ...
С> ,UCD_ID_NR // может быть адаптирован под макрос, чтобы не определять его в классе ParameterAutoAdd<>
С>};
С>
С>Список готов. Чтобы сериализаторы-дасериализаторы и аллокаторы памяти для параметров работали правильно, необходимо их описать. Все параметры описывать не буду, для примера достаточно одного:
С>
И что делать если один из параметров забыли так описать?
Что делать если сделали ошибку типа: template <> class Parameter<UcdId, ULRadioResources + 13 > ... ;
В общем DRY нарушен. Пиф-паф и труп.
С>Повторюсь, в коде нигде я ничего не правлю, каждая новая строка кода -- это добавление новой информации и только, всю остальную (рутинную) работу делает за меня компилятор: добавление проверок (compiled time, run-time для проверки входных и выходных инвариантов, генерация событий а-ля notify(Detected<UcdParameterIds>() и т.п.),.
ну и? в моём случае тоже всё автоматом генерируется, но для пользователя намного более проще и надёжно
[skipped]
С>Всё контролируемо, независимо, управляемо. В коде добавляю только новую информацию, больше относящуюся к предметной области, внутреннее устройство скрыто за библиотекой... С>Примерно так...
А теперь всё таки приведите список преимуществ, которые вы думаете ваша реализация с ненадёжным пользовательским интерфейсом лучше более простого и надёжного варианта?
Re[13]: Автоматическая генерация интерфейсов классов
Здравствуйте, Piko, Вы писали:
P>Внимание, а теперь вопрос, как вообще какая-то левая статья относится к нашему обсуждению и вашей задаче? Вы просто мастер художественного передёргивания.
ничего не передёргиваю, просто привёл в пример, каким мне показалась Ваша позиция,.. и не более того.
С>>Но это к слову, о чём я упомянул ранее, написав "...в простых ситуациях с Вами трудно не согласиться..."
P>вы до сих пор меня не поняли. Вы знаете про expression templates? Так вот, я только за использование этой техники, естественно там где это обоснованно. Да, эта техника может показаться навороченной, но это не наворот ради наворота как у вас.
пользуюсь expression templates активно и легко
Re[13]: Автоматическая генерация интерфейсов классов
Здравствуйте, Piko, Вы писали:
P>Здравствуйте, смит, Вы писали:
С>>Я не утверждаю, что Ваше решение не имеет право на жизнь, но простота Вашего решения сомнительна. Какова мера отвественности вашего класса (структуры) Message? прямо скажем, небольшая, набор параметров. Что эта структура может с ними делать? хранить их вместе, все алгоритмы прописываем вручную для каждого структуры.
P>Вы явно даже не пытаетесь понять то о чём я говорю:
P>[q] P>1. В boost::Serialization (обход полей runtime) P>
P>template<class Archive>
P>void serialize(Archive & ar, const unsigned int version)
P>{
P> ar & degrees;
P> ar & minutes;
P> ar & seconds;
P>}
P>
P>2. либо в стиле tuple, c возможным compile-time обходом полей.
я приводил пример сериализации в моей библиотеке? в моей библиотеке нет проблем с использованием любого сериализатора, добавьте сериализацию как стратегию. В boost::Serialization смущает только одно: http://mdf-i.blogspot.com/2011/02/boostserialization-hell.html
P>И что делать если один из параметров забыли так описать? P>Что делать если сделали ошибку типа: template <> class Parameter<UcdId, ULRadioResources + 13 > ... ; P>В общем DRY нарушен. Пиф-паф и труп.
Ничего смертельного не произойдёт. Компилятор либо укажет на проблему в режиме компиляции, либо в run-time поле проигнорируется с уведомлением в лог, что нет обработчика для принятого параметра. Это зависит от того, какую стратегию реализации выберет программист. Писать так "ULRadioResources + 13" не надо, об этом я уже упоминал.
P>А теперь всё таки приведите список преимуществ, которые вы думаете ваша реализация с ненадёжным пользовательским интерфейсом лучше более простого и надёжного варианта?
Считаете приведённый мною пример-алгоритм работы с библиотекой непонятным, неудобным и сложным? Я никого не призываю пользоваться непонятным, неудобным и сложным. Представил на суд публики ряд идей. К примеру, аггрегация переменных в библиотеке неявно присутствует, она скрыта в классе ParameterValue<>. В самом же классе Message<> аггрегация приведёт к появлению переменных и их имён, а классу Message<> имена не важны, для его алгоритмов достаточно знать, что есть параметр с именнованным (удобство для кодирующего) индексом Parameter<indexName>. Сам стандарт IEEE 802.16 построен на событийной модели машины состояний. На индексы (сообщений, параметров) легко в compiled time реализовать диспетчеризацию, регистрировать обработчики (handlers) и только в теле самих обработчиков мы начинаем оперировать реальными классами (объектами), пример с UcdFrequency. Такое разделение мне кажется разумным и удобным.
Re[2]: Автоматическая генерация интерфейсов классов
Здравствуйте, Piko, Вы писали:
P>>>>>А главное вы не ответили чем это всё лучше простого аггрегирующего класса (с нормальной реализацией, а не как у вас с getters and setters). С>>>>Приведите, пожалуйста, пример Вашего кода, может я неправильно интерпретирую Ваши мысли. P>>>Я же предлагаю вам подумать об более простом и элегантном способе: P>>>
P>>>в котором нет тех двух проблем с getters/setters и привязки к low-level типам, которыми вы оправдываете свою статью.
У меня к Вам встречный вопрос, пусть, для простоты, описанная Вами структура в дополнение к четырём полям имеет два открытых метода, назовём их:
{
void update();
void reset();
...
};
1. Приведите, пожалуйста, пример Вашей реализации этих методов, пробегание по полям.
2. Добавьте в структуру MessageDcd новое поле TGap ttg; как при этом изменятся методы update() и reset().
3. Одно из полей при модификации должно сигнализировать, необходимо написать обработчик, срабатывающий на эту модификацию. Как вы это реализуете?
Это всё не сложно, я примерно представляю Ваш ответ, но могу заблуждаться. Спрашиваю Вас без единой доли сарказма и ехидства, мне просто интересно Ваше мнение.
Re[14]: Автоматическая генерация интерфейсов классов
Здравствуйте, смит, Вы писали:
С>>>Я не утверждаю, что Ваше решение не имеет право на жизнь, но простота Вашего решения сомнительна. Какова мера отвественности вашего класса (структуры) Message? прямо скажем, небольшая, набор параметров. Что эта структура может с ними делать? хранить их вместе, все алгоритмы прописываем вручную для каждого структуры. P>>Вы явно даже не пытаетесь понять то о чём я говорю:
::: С>я приводил пример сериализации в моей библиотеке? в моей библиотеке нет проблем с использованием любого сериализатора, добавьте сериализацию как стратегию.
а я не говорил что у вас это не возможно. я вам привёл пример того, что это возможно в моих, более простых вариантах, чтобы опровергнуть ваше утверждение о том, что в более простых вариантах будет копипаста:
Что эта структура может с ними делать? хранить их вместе, все алгоритмы прописываем вручную для каждого структуры.
во-первых в доках boost::serialization написано, что нет поддержки формирования более старых архивов, даже не в доках, а прям вот на заглавных страницах фича-листа.
во-вторых если вам нужны какие-то особые фичи которых нет там, то можно спокойно расширить, создать альтернативу с похожей идеей и т.п.
P>>И что делать если один из параметров забыли так описать? P>>Что делать если сделали ошибку типа: template <> class Parameter<UcdId, ULRadioResources + 13 > ... ; P>>В общем DRY нарушен. Пиф-паф и труп. С>Ничего смертельного не произойдёт. Компилятор либо укажет на проблему в режиме компиляции, либо в run-time поле проигнорируется с уведомлением в лог, что нет обработчика для принятого параметра. Это зависит от того, какую стратегию реализации выберет программист.
а как он вообще узнает что должен быть обработчик для конкретного enum поля? телепатией?
я говорю про случай, когда пользователь не делает GetParameter на это поле.
С>Писать так "ULRadioResources + 13" не надо, об этом я уже упоминал.
вы ещё скажите "делать ошибки не надо, я уже упоминал".
в случае с кодогенерацией из схемы (допустим xml, или хоть X-macros), такие ошибки в принципе не возможно пропустить из compile-time. и не надо говорить "писать так не надо".
P>>А теперь всё таки приведите список преимуществ, которые вы думаете ваша реализация с ненадёжным пользовательским интерфейсом лучше более простого и надёжного варианта?
С>Считаете приведённый мною пример-алгоритм работы с библиотекой непонятным, неудобным и сложным? Я никого не призываю пользоваться непонятным, неудобным и сложным.
для меня он понятен. но дело в том, что варианты предложенные мной намного более удобные и надёжные, без потери гибкости, разве нет? Так зачем платить больше?
С>Представил на суд публики ряд идей. К примеру, аггрегация переменных в библиотеке неявно присутствует, она скрыта в классе ParameterValue<>. В самом же классе Message<> аггрегация приведёт к появлению переменных и их имён, а классу Message<> имена не важны, для его алгоритмов достаточно знать, что есть параметр с именнованным (удобство для кодирующего) индексом Parameter<indexName>.
ну так имена ведь по-любому нужны пользователю в контексте данной задачи?
в моих вариантах, для внутренних классов, обрабатывающих схему (допустим для сериализации) имена тоже не нужны для этих алгоритмов важно чтобы данные были в схеме.
С>Сам стандарт IEEE 802.16 построен на событийной модели машины состояний. На индексы (сообщений, параметров) легко в compiled time реализовать диспетчеризацию, регистрировать обработчики (handlers) и только в теле самих обработчиков мы начинаем оперировать реальными классами (объектами), пример с UcdFrequency. Такое разделение мне кажется разумным и удобным.
так в моих примерах вся эта диспетчеризация, и обработчики также прекрасно генерируются.
Re[9]: Автоматическая генерация интерфейсов классов
Здравствуйте, смит, Вы писали:
P>>>>>>А главное вы не ответили чем это всё лучше простого аггрегирующего класса (с нормальной реализацией, а не как у вас с getters and setters). С>>>>>Приведите, пожалуйста, пример Вашего кода, может я неправильно интерпретирую Ваши мысли. P>>>>Я же предлагаю вам подумать об более простом и элегантном способе: P>>>>
P>>>>в котором нет тех двух проблем с getters/setters и привязки к low-level типам, которыми вы оправдываете свою статью. С>У меня к Вам встречный вопрос, пусть, для простоты, описанная Вами структура в дополнение к четырём полям имеет два открытых метода, назовём их: С>
эти update и reset, будут передавать специальный объект, либо просто тип аргумента шаблона в метод serialize (который может быть как non-member, так и member). этот объект/тип будет знать что делать с каждым полем которое в него прилетит.
для разных объектов эти методы будут довольно одинаковые, и их можно реализовать один раз и использовать многократно разными способами.
это в том варианте, если я правильно догадываюсь о том, что представляют ваши методы update/reset — они выполняют некоторую однотипную операцию для каждого поля?
С>2. Добавьте в структуру MessageDcd новое поле TGap ttg; как при этом изменятся методы update() и reset().
эти методы (либо non-member functions) при этом никак не изменятся — они абстрагированны от набора полей. Набором полей (схемой) здесь заведует только gps_position::serialize.
если добавляется поле принципиально нового типа, то естественно нужно создать этот тип, точно также как и вашем варианте.
С>3. Одно из полей при модификации должно сигнализировать, необходимо написать обработчик, срабатывающий на эту модификацию. Как вы это реализуете?
Это зависит от того, что вы понимаете под обработчиком. А это могут быть действительно разные вещи:
1. сохранение инварианта класса — в этом случае, просто класс сам должен заботится об своём инварианте. Ошибочно, для этих целей часто используются "внешние" для класса getters/setters
2. сигнализация некого внешнего объекта, допустим как один из простых вариантов — простой log событий. В этом случае все обработчики будут автоматом регистрироваться с помощью специального объекта(который может быть использован для разных классов, без изменений, и который сам по себе ничего не знает об конкретных аттрибутах) переданного в gps_position::serialize.
Непосредственно для сигналов можно использовать Boost.Signals2.
3. возможны и другие варианты того, что вы под этим подразумеваете. для них возможно будут разумными другие решения.
Как вы видите всё просто и надёжно, и без копипасты.
С>Это всё не сложно, я примерно представляю Ваш ответ, но могу заблуждаться. Спрашиваю Вас без единой доли сарказма и ехидства, мне просто интересно Ваше мнение.
Эти ответы сходятся с вашим представлением?
Ваша схема имеет какие-либо преимущества над эти вариантом (это собственно главный вопрос нашей дискуссии) ?
Re[10]: Автоматическая генерация интерфейсов классов
Здравствуйте, Piko, Вы писали:
P>Здравствуйте, смит, Вы писали:
P>>>>>>>А главное вы не ответили чем это всё лучше простого аггрегирующего класса (с нормальной реализацией, а не как у вас с getters and setters). С>>>>>>Приведите, пожалуйста, пример Вашего кода, может я неправильно интерпретирую Ваши мысли. P>>>>>Я же предлагаю вам подумать об более простом и элегантном способе: P>>>>>
P>>>>>в котором нет тех двух проблем с getters/setters и привязки к low-level типам, которыми вы оправдываете свою статью. С>>У меня к Вам встречный вопрос, пусть, для простоты, описанная Вами структура в дополнение к четырём полям имеет два открытых метода, назовём их: С>>
С>>1. Приведите, пожалуйста, пример Вашей реализации этих методов, пробегание по полям.
P>я приводил четыре разных варианта.
P>Например вариант похожий на boost serialization:
P>у нас есть схема описанная в шаблонном методе типа gps_position::serialize ( http://www.boost.org/doc/libs/1_49_0/libs/serialization/doc/tutorial.html#simplecase )
P>эти update и reset, будут передавать специальный объект, либо просто тип аргумента шаблона в метод serialize (который может быть как non-member, так и member). этот объект/тип будет знать что делать с каждым полем которое в него прилетит. P>для разных объектов эти методы будут довольно одинаковые, и их можно реализовать один раз и использовать многократно разными способами.
P>это в том варианте, если я правильно догадываюсь о том, что представляют ваши методы update/reset — они выполняют некоторую однотипную операцию для каждого поля?
Хорошо, конкретизирую задачу, чуть видоизменив сигнатуру. Пусть метод int update() вызывает для каждого поля структуры метод этого поля is_relevant() и если эта операция возвратит true, вызывает, метод calc_size(). Перебрав все элементы структуры возвращаем size. Пусть для статистики (или отладки) структура MessageDcd имеет поле m_update_call_count, подсчитывающее количество обращений к методу int update(), например
int MessageDcd::update()
{
++m_update_call_count;
int size = 0;
...
if (eirp.is_relevant())
size += eirp.calc_size();
...
return size;
}
Уточню, поля, имеющие тип сериализации Plain всегда возвращают true в методе is_relevant(). Структура MessageDcd имеет два таких поля type и count.
Не будем усложнять, пусть метод void reset() вызовет для каждого поля метод этого поля drop(), например eirp.drop(), только в том случае, если поле имеет TLV структуру сериализации. Пусть такими полями будут eirp и добавленное во второй части задачи поле ttg.
Приведите код Вашего решения задачи с учётом вышеописанного уточнения. Как у Вас код не зависит от имён полей и количества полей?
С>>2. Добавьте в структуру MessageDcd новое поле TGap ttg; как при этом изменятся методы update() и reset().
P>эти методы (либо non-member functions) при этом никак не изменятся — они абстрагированны от набора полей. Набором полей (схемой) здесь заведует только gps_position::serialize. P>если добавляется поле принципиально нового типа, то естественно нужно создать этот тип, точно также как и вашем варианте.
serialise пока что не трогаем, но поле ttg добавляем.
С>>3. Одно из полей при модификации должно сигнализировать, необходимо написать обработчик, срабатывающий на эту модификацию. Как вы это реализуете?
эту задачу не решаем, достаточно для начала решить первые две.
4. Пусть для структуры MessageRng будет аналогичный метод int update() с аналогичным алгоритмом работы, но количество и имена полей другие. Какой алгоритм создания новой структуры MessageRng?
P>Как вы видите всё просто и надёжно, и без копипасты.
Пока что не вижу (может туплю ), приведите, пожалуйста, Ваш код, чтобы был предмет обсуждения.
Re[11]: Автоматическая генерация интерфейсов классов
Здравствуйте, смит, Вы писали:
С>Хорошо, конкретизирую задачу, чуть видоизменив сигнатуру. Пусть метод int update() вызывает для каждого поля структуры метод этого поля is_relevant() и если эта операция возвратит true, вызывает, метод calc_size(). Перебрав все элементы структуры возвращаем size. Пусть для статистики (или отладки) структура MessageDcd имеет поле m_update_call_count, подсчитывающее количество обращений к методу int update(), например
А зачем вообще m_update_call_count нужен в контексте нашего обсуждения?
С>Уточню, поля, имеющие тип сериализации Plain всегда возвращают true в методе is_relevant(). Структура MessageDcd имеет два таких поля type и count.
да как бы всё-равно, это всё ненужные детали
С>Не будем усложнять, пусть метод void reset() вызовет для каждого поля метод этого поля drop(), например eirp.drop(), только в том случае, если поле имеет TLV структуру сериализации. Пусть такими полями будут eirp и добавленное во второй части задачи поле ttg.
тоже ненужные детали, реализуются тривиально.
С>Приведите код Вашего решения задачи с учётом вышеописанного уточнения. Как у Вас код не зависит от имён полей и количества полей?
закомментируйте строчки
60: CrapParam<4> crappyShit4;
и
68: ar & crappyShit4;
Код drop и update хоть как-то поменялся?
С>>>2. Добавьте в структуру MessageDcd новое поле TGap ttg; как при этом изменятся методы update() и reset().
играйтесь комментируйте/раскомментируйте строчки 60, 68
P>>эти методы (либо non-member functions) при этом никак не изменятся — они абстрагированны от набора полей. Набором полей (схемой) здесь заведует только gps_position::serialize. P>>если добавляется поле принципиально нового типа, то естественно нужно создать этот тип, точно также как и вашем варианте. С>serialise пока что не трогаем, но поле ttg добавляем.
да, поле нужно описывать в схеме, я вам это уже не раз говорил. в данном конкретном случае, схема описана в ParamAggregator::serialize.
В вашем случае, поле тоже нужно добавлять в схему, недостаточно просто добавить в enum
Я вам уже не раз говорил, что как раз в этих случаях X-Macro/Code Generation сильно поможет
С>4. Пусть для структуры MessageRng будет аналогичный метод int update() с аналогичным алгоритмом работы, но количество и имена полей другие. Какой алгоритм создания новой структуры MessageRng?
В моём коде ParamAggregator::update это member function, один из вариантов это сделать из ParamAggregator::update просто non-member function:
template<class Container>
int update(Container&);
и она может использоваться для разных структур без изменений (хоть для MessageDcd, хоть для MessageRng)
P>>Как вы видите всё просто и надёжно, и без копипасты. С>Пока что не вижу (может туплю ), приведите, пожалуйста, Ваш код, чтобы был предмет обсуждения.
Я думаю этого кода что я привёл — достаточно.
Re[12]: Автоматическая генерация интерфейсов классов
Здравствуйте, Piko, Вы писали:
P>Здравствуйте, смит, Вы писали:
С>>Хорошо, конкретизирую задачу, чуть видоизменив сигнатуру. Пусть метод int update() вызывает для каждого поля структуры метод этого поля is_relevant() и если эта операция возвратит true, вызывает, метод calc_size(). Перебрав все элементы структуры возвращаем size. Пусть для статистики (или отладки) структура MessageDcd имеет поле m_update_call_count, подсчитывающее количество обращений к методу int update(), например
P>А зачем вообще m_update_call_count нужен в контексте нашего обсуждения?
P>вот код:
P>http://ideone.com/NWmgk
P>
С>>Уточню, поля, имеющие тип сериализации Plain всегда возвращают true в методе is_relevant(). Структура MessageDcd имеет два таких поля type и count.
P>да как бы всё-равно, это всё ненужные детали
С>>Не будем усложнять, пусть метод void reset() вызовет для каждого поля метод этого поля drop(), например eirp.drop(), только в том случае, если поле имеет TLV структуру сериализации. Пусть такими полями будут eirp и добавленное во второй части задачи поле ttg.
P>тоже ненужные детали, реализуются тривиально.
С>>Приведите код Вашего решения задачи с учётом вышеописанного уточнения. Как у Вас код не зависит от имён полей и количества полей?
P>закомментируйте строчки P>60: CrapParam<4> crappyShit4; P>и P>68: ar & crappyShit4;
P>Код drop и update хоть как-то поменялся?
...
P>Я думаю этого кода что я привёл — достаточно.
Да, этого достаточно. Хороший код, логика понятна.
Статья написана была полтора года назад,поэтому я постараюсь представить свой код Message<> после небольшого рефакторинга, который упростит сигнатуру классов. Сейчас замечу, что у нас с Вами применены немного разные принципы кодирования, но одно другому не помеха.
1-е отличие: контроль доступа к внутренним переменным. Все параметры я мог бы сделать открытыми, как у Вас, и в этом плане наши классы мало чем бы друг от друга отличались. Сокрытие полей и доступ через интерфейсную функцию позволяет безболезненно добавить новый функционал не влияя на код, использующий наш класс, т.к. его интерфейс-контракт остался неизменным. Например, сам объект класса Message<> может иметь невалидное состояние. Добавляем в тело метода вызов закрытого метода, например onGet(), отладочные функции и т.п.:
Message<>::getParameter<frequency>() {onGet(); writeLog(); /* творим что хотим с классом или полем */; return ...;}
Message<>::setParameter<frequency>(Frequency fq) {onSet(); writeLog(fq); /* творим что хотим */; m_fq = fq; /* творим что хотим */; postSet();}
при этом не надо по коду искать, где обращались к полям класса. Обратите внимание, я не сказал, что невалидные поля, я сказал, что сам класс сообщения может иметь невалидное состояние. Интерфейсы -- это часть архитектуры. В Вашем случае код (архитектура) менее гибки.
2-е отличие: в моём случае идёт декларативное программирование. Через шаблонные параметры я объявляю характеристики полей класса сообщения. Поскольку класс ParameterFactory<Plain, DcdParameterIds, DcdConfigChangeCount, 1, Unsigned> -- это шаблон, то мы можем использовать все достоинства, как Вы ранее упомянули, C++ Expression Templates. Что я имею в виду: передал в шаблон параметр Plain и не надо по коду комментировать (не ошибусь нужное закомментировать), компилятор сам за меня в нужном месте создаст или проигнорирует код. Передал в шаблон параметр Size, в режиме компиляции вычислится размер сообщения, возможен даже контроль проверки: поместиться ли это сообщение во фрейм. Значит в режиме компиляции я уже узнаю минимально допустимый размер фрейма. Без этих шаблонных параметров все проверки уходят в run-time и требуется квалифицированная команда тестировщиков. Последним тестировщиком будет пользователь, и по телефону "лечить" проблему ох как не просто.
Но класс ParameterFactory<Plain, Dcd::configChangeCount, 1, Unsigned> -- неудобный для работы шаблон. Вся информация о поле уже внесена, и таскать её за собой по коду не хотелось бы, поэтому и был введён класс заместитель Parameter<Dcd::configChangeCount>.
Ещё раз повторю: мне нравится Ваш код. Два основных отличия от моего кода и обоснование я привёл. Код после рефакторинга (с более привлекательной сигнатурой классов) представлю чуть позже, но идея и принципы (доступ к полям через интерфейс и шаблонные параметры полей) осталась прежними и не исключают использование приведённых Вами механизмов работы с полями (плюс добавляется expression templates).
Re[13]: Автоматическая генерация интерфейсов классов
Здравствуйте, смит, Вы писали:
P>>Я думаю этого кода что я привёл — достаточно. С>Да, этого достаточно. Хороший код, логика понятна.
это один из вариантов о которых я говорил. есть и другие, более "мощные", например схема в виде tuple/type list, но они точно также как и этот вариант предоставляют пользователю простой интерфейс (структура с полями).
С>Статья написана была полтора года назад,поэтому я постараюсь представить свой код Message<> после небольшого рефакторинга, который упростит сигнатуру классов. Сейчас замечу, что у нас с Вами применены немного разные принципы кодирования, но одно другому не помеха.
С>1-е отличие: контроль доступа к внутренним переменным. Все параметры я мог бы сделать открытыми, как у Вас, и в этом плане наши классы мало чем бы друг от друга отличались. Сокрытие полей и доступ через интерфейсную функцию позволяет безболезненно добавить новый функционал не влияя на код, использующий наш класс, т.к. его интерфейс-контракт остался неизменным.
так в моём варианте код тоже добавляется безболезненно, без влияния на использующий код
наружу выставляется только, как вы говорите "контракт".
С>Например, сам объект класса Message<> может иметь невалидное состояние. Добавляем в тело метода вызов закрытого метода, например onGet(), отладочные функции и т.п.:
о каком именно невалидном состоянии вы говорите?
1. конкретные поля содержат неправильную информацию
2. есть некий инвариант класса, который нарушен. то есть индивидуальные поля содержат допустимые значения для этих полей, но они сами между собой несовместимы
Эти проблемы вообще абстрактны от сериализации, и прекрасно решаются (вы почитайте книги Страуструпа, или хотя бы вот эту статью — http://www.artima.com/intv/goldilocks.html ).
В первом случае — нужно править класс конкретного поля, он не должен допускать установку невалидного состояния.
Во втором, если есть некий инвариант, то речь уже идёт не об аггрегирующем классе (обычной структуре), а об полноценном классе, который должен сам заботится о своём инварианте (геттеры, сеттеры, onGet тут лепить не надо).
С>
С>Message<>::getParameter<frequency>() {onGet(); writeLog(); /* творим что хотим с классом или полем */; return ...;}
С>Message<>::setParameter<frequency>(Frequency fq) {onSet(); writeLog(fq); /* творим что хотим */; m_fq = fq; /* творим что хотим */; postSet();}
С>
С>при этом не надо по коду искать, где обращались к полям класса. Обратите внимание, я не сказал, что невалидные поля, я сказал, что сам класс сообщения может иметь невалидное состояние.
то есть речь идёт о втором варианте — класс с инвариантом, как я уже сказал, геттеры, сеттеры, onGet, onSet тут вообще не нужны.
С>Интерфейсы -- это часть архитектуры. В Вашем случае код (архитектура) менее гибки.
нет.
С>Что я имею в виду: передал в шаблон параметр Plain и не надо по коду комментировать (не ошибусь нужное закомментировать), компилятор сам за меня в нужном месте создаст или проигнорирует код. Передал в шаблон параметр Size, в режиме компиляции вычислится размер сообщения, возможен даже контроль проверки: поместиться ли это сообщение во фрейм. Значит в режиме компиляции я уже узнаю минимально допустимый размер фрейма.
если в моём случае перейти от
ar & p1;
ar & someShit2;
ar & superCrap3;
ar & crappyShit4;
всё вычисляется на этапе компиляции
С>Без этих шаблонных параметров все проверки уходят в run-time и требуется квалифицированная команда тестировщиков. Последним тестировщиком будет пользователь, и по телефону "лечить" проблему ох как не просто.
ну так я же не против шаблонов.
1. я привёл пример выше как это делается в моём первом вариате в стиле boost::serialization
2. в случаях с tuple/type list, X-Macro helpers, Code Generation, это точно также реализуется compile-time
и везде, у пользователя остаётся ПРОСТОЙ интерфейс.
С>Ещё раз повторю: мне нравится Ваш код. Два основных отличия от моего кода и обоснование я привёл.
первое отличие — не зачёт (я всё выше объяснил).
второе отличие — "compile time вычисление размера" такую задачу вы не ставили, я её и не решал, как и не решал кучу других задач — а всего лишь привёл простой пример идеи boost::serialization. Но, она решается во всех предложенных мной вариантах, выше я описал как.
То есть у вашего подхода нет ни первого ни второго преимущества, пользовательский интерфейс менее надёжный, менее простой, менее элегантный.
Так зачем платить больше?
Re[14]: Автоматическая генерация интерфейсов классов
Здравствуйте, Piko, Вы писали:
... P>первое отличие — не зачёт (я всё выше объяснил). P>второе отличие — "compile time вычисление размера" такую задачу вы не ставили, я её и не решал, как и не решал кучу других задач — а всего лишь привёл простой пример идеи boost::serialization. Но, она решается во всех предложенных мной вариантах, выше я описал как.
P>То есть у вашего подхода нет ни первого ни второго преимущества, пользовательский интерфейс менее надёжный, менее простой, менее элегантный. P>Так зачем платить больше?
Хорошо, убираю первое отличие, делаю поля открытыми и вместо dcd.get_parameter<name_id>() будет как у Вас структура с открытым доступам к полям dcd.parameter<name_id>; делается это удалением двух шаблонных set/get методов, если поля действительно не связаны и не влияют на инвариант класса. Видать пример у меня неудачный. У Вас механизм обработки построен на функции serialize и перегрузке operator& для архивов, хороший приём. Я просто привёл пример обращения к полю по индексу без привязки к имени. В дополнение к сказаному, по индексу в режиме компиляции можно создавать связанные с полями сигналы и события event_changed<dcdFrequency>(), signal_received_new<dcdChangeCount>(), execute(Command<dcdTtg>()), а также обработчики handler<dcdChangeCount>() и т.п... Или опять всё не так?
Re[15]: Автоматическая генерация интерфейсов классов
Здравствуйте, смит, Вы писали:
С>У Вас механизм обработки построен на функции serialize и перегрузке operator& для архивов, хороший приём.
это один из вариантов описания схемы, при котором в интерфейсе обычные аггрегирующие структуры. есть и другие, которые позволяют делать больше работы compile-time, сохраняя при этом простой интерфейс.
С>Я просто привёл пример обращения к полю по индексу без привязки к имени.
в вашем примере используется индекс с именем, ведь так? значит есть привязка к имени.
С>В дополнение к сказаному, по индексу в режиме компиляции можно создавать связанные с полями сигналы и события event_changed<dcdFrequency>(), signal_received_new<dcdChangeCount>(), execute(Command<dcdTtg>()), а также обработчики handler<dcdChangeCount>() и т.п... Или опять всё не так?
всё тоже самое можно и без индексов, сохраняя простой интерфейс.
Re[16]: Автоматическая генерация интерфейсов классов
Здравствуйте, Piko, Вы писали:
P>Здравствуйте, смит, Вы писали:
С>>У Вас механизм обработки построен на функции serialize и перегрузке operator& для архивов, хороший приём. P>это один из вариантов описания схемы, при котором в интерфейсе обычные аггрегирующие структуры. есть и другие, которые позволяют делать больше работы compile-time, сохраняя при этом простой интерфейс.
С>>Я просто привёл пример обращения к полю по индексу без привязки к имени. P>в вашем примере используется индекс с именем, ведь так? значит есть привязка к имени.
С>>В дополнение к сказаному, по индексу в режиме компиляции можно создавать связанные с полями сигналы и события event_changed<dcdFrequency>(), signal_received_new<dcdChangeCount>(), execute(Command<dcdTtg>()), а также обработчики handler<dcdChangeCount>() и т.п... Или опять всё не так? P>всё тоже самое можно и без индексов, сохраняя простой интерфейс.
Хорошо, с сериализацией проблем нет, слишком простая операция, но как быть с десериализацией? Начальные условия: на вход десериализатора поступает массив байт, например std::vector<unsigned char> vec_dcd;
Упрощённо правило десериализации можно описать так:
1. plain поля обязательно присутствуют, формат сериализации big-endian, размер может быть разным, но больше 4 байт не встречал.
2. количество plain полей в каждом сообщении разное, но их всегда определённое число.
3. tlv поля могут присутствовать во входном векторе, могут не присутствовать.
4. может встретится неизвестное tlv поле.
5. tlv поле может содержить вложенные plain и tlv поля, где plain обязательные, а tlv могут не присутствовать, могут быть неизвестны.
6. вложенные tlv поля могут иметь идентификаторы type, совпадающие с идентификаторами type уровня выше, но это принципиально другие поля, может и с другим length.
7. tlv поле на одном уровне (любом уровне вложенности) может повторяться, т.е. структура Message<> может содержать поля и вектора полей, и поля, содержащие вектора полей.
8. в tlv поле, поле length может быть комплексным, т.е. быть один байт или несколько байт, тогда первый байт указывает длину поля length а остальные байты длину поля value. Признаком комплексности поля является "1" в старшем разряде байта первого байта поля length.
Описывая поля через идентификаторы я автоматически создаю десериализатор и регистрирую обработчики полей. Может я велосипед написал и есть библиотека, которая работает по этим правилам? (эти правила не я придумал, таков стандарт IEEE 802.16)
Re[17]: Автоматическая генерация интерфейсов классов
Здравствуйте, смит, Вы писали:
P>>всё тоже самое можно и без индексов, сохраняя простой интерфейс. С>Хорошо, с сериализацией проблем нет, слишком простая операция, но как быть с десериализацией? Начальные условия: на вход десериализатора поступает массив байт, например std::vector<unsigned char> vec_dcd; С>Упрощённо правило десериализации можно описать так: С>1. plain поля обязательно присутствуют, формат сериализации big-endian, размер может быть разным, но больше 4 байт не встречал. С>2. количество plain полей в каждом сообщении разное, но их всегда определённое число. С>3. tlv поля могут присутствовать во входном векторе, могут не присутствовать. С>4. может встретится неизвестное tlv поле.
вы можете абстрагироваться от этой plain/tlv мути?
в статье кстати тоже весь трэш из IEEE, нужно вынести в маленький раздел, только для примера применимости, а не разбрасывать по всей статье — иначе очень нудно получается.
С>Описывая поля через идентификаторы я автоматически создаю десериализатор и регистрирую обработчики полей.
используя простой вариант на подобие boost::serialization можно сделать тоже самое (для этого даже tuple/type_list не нужны).
С>Может я велосипед написал и есть библиотека, которая работает по этим правилам? (эти правила не я придумал, таков стандарт IEEE 802.16)
Если эти правила реализуемы с вашим подходом, то я уверен на 99.9999% что они реализуются и в описанных мной вариантах (при этом получается более простой интерфейс).
Re[18]: Автоматическая генерация интерфейсов классов
Здравствуйте, Piko, Вы писали:
P>Здравствуйте, смит, Вы писали:
P>>>всё тоже самое можно и без индексов, сохраняя простой интерфейс. С>>Хорошо, с сериализацией проблем нет, слишком простая операция, но как быть с десериализацией? Начальные условия: на вход десериализатора поступает массив байт, например std::vector<unsigned char> vec_dcd; С>>Упрощённо правило десериализации можно описать так: С>>1. plain поля обязательно присутствуют, формат сериализации big-endian, размер может быть разным, но больше 4 байт не встречал. С>>2. количество plain полей в каждом сообщении разное, но их всегда определённое число. С>>3. tlv поля могут присутствовать во входном векторе, могут не присутствовать. С>>4. может встретится неизвестное tlv поле.
...
P>вы можете абстрагироваться от этой plain/tlv мути?
Type из структуры TLV это единственный атрибут, который указывает на то, что в бинарном потоке поле присутствует. Вам в любом случае приходится сопоставлять идентификаторы либо по иерархии, либо полным путём с полями. Согласен, что правило сериализации-десериализации мудрёные, или Вы хотели, чтобы я Вам абстрактно описал условия?
С>>Описывая поля через идентификаторы я автоматически создаю десериализатор и регистрирую обработчики полей. P>используя простой вариант на подобие boost::serialization можно сделать тоже самое (для этого даже tuple/type_list не нужны).
С>>Может я велосипед написал и есть библиотека, которая работает по этим правилам? (эти правила не я придумал, таков стандарт IEEE 802.16) P>Если эти правила реализуемы с вашим подходом, то я уверен на 99.9999% что они реализуются и в описанных мной вариантах (при этом получается более простой интерфейс).