Oleksandr Redchuk wrote to "Harry Zhurov" snipped-for-privacy@online.nsk.su> on Mon, 22 Sep 2003 18:26:59 +0000 (UTC):
HZ>> void uartInit(BYTE* Rx_buf, HZ>> BYTE Rx_size, HZ>> BYTE* Tx_buf, HZ>> BYTE Tx_size, HZ>> BYTE Baud_rate); OR> Я не понял - почему тут куча параметров, а у конструктора OR> ниже только baud rate? Значит, это _разные_ по поведению OR> системы, если там конструктор распределяет себе буфера по своему OR> усмотрению.
По функциональности одинаковые. Просто в сишном варианте все сделано в традициях С: работа с аппаратным UART'ом, кольцевые буфера, функциональность протокола обмена пакетами и прочее "живет" отдельно, и весь модуль представляет собой конгломерат этих частей. В плюсатом варианте все эти детали инкапсулированы внутри класса. Соответственно, на С вызывается функция, которая должна проинитить порт, ну и уж заодно, чтобы не делать две инициализирующие функции, т.к. и одной-то много, она инитит тот кусок, который отвечает за буфера. В плюсатом варианте все делается в конструкторах объемлющего и вложенных объектов, поэтому снаружи остается только передать то, что заранее никак неизвестно - скорость.
HZ>> void uartDisable_Rx(void); OR> [...] HZ>> BOOL uartWrite_Tx_data(BYTE* ptr, BYTE count); OR> Этих функций нет в открытой части класса ниже, значит OR> они могут быть static в файле uart.c и не блестеть тут.
HZ>> interrupt void UART_Rx_interrupt(void); HZ>> interrupt void UART_UDRE_interrupt(void); OR> Аналогично, если у тебя класс ниже работает через прерывания, то OR> эти функции есть. Блестеть наружу им нечего, спрятаны и так, OR> и так где-то в uart.c или uart.cpp
HZ>> BYTE UART_block_buf[BLOCK_BUFFER_SIZE]; OR> Опять - в "классовой" версии буфер из воздуха взялся? OR> Всё равно где-то кто-то его распределяет.
HZ>> void (*fptrCmd[])(void) = HZ>> { HZ>> read_SRAM, HZ>> write_SRAM, HZ>> log_op, HZ>> read_FLASH, HZ>> read_EEPROM, HZ>> write_EEPROM, HZ>> uart_cmd_user HZ>> }; OR> И реакцию на команды у класса так или иначе надо прописать.
Все это, конечно, есть (я и не говорил, что нет - наоборот, говорил, что определение класса столь же обширно, просто не стал загромождать письмо; у тебя кстати, afair, имеются обсуждаемые сорцы). Но... (см ответ на следующую цитату)
HZ>> Вот это все "живет" в глобальном пространстве имен. Там еще пара OR> ПОЧЕМУ? OR> Почему бОльшая часть этого не может быть static внутри файла uart.c??? OR> Если его аналоги спрятаны от доступа в классе, то они пользователю OR> не нужны.
[...]
HZ>> что нужно держать в голове) не выглядит "исчезающе малой"!!! OR> Ну так не показывай наружу всё! OR> На то и существует static в C, чтобы видимость имён ограничить OR> файлом, в котором они заданы.
Все это так. И в данном случае, это, конечно, выход для С. И все это (объявление внутренних программных элементов статическими) попытка сделать по
++ному. Т.е. сделать именно то, что на ++ получается нативно. И после этого еще не считать ++ную модель лучшей, чем сишную!
Но и тут при таком подходе в С два недостатка.
Во-первых, при обращении к переменной, инкапсулированной в модуле (единице трансляции), придется либо делать ее открытой, либо вводить функцию доступа. И то, и другое имеет недостатки, сам знаешь какие. ++ предоставляет простой и логичный способ достичь защищенности без потери эффективности. Это ли не преимущество?
Во-вторых, и в-главных, такой подход красиво выглядит для таких достаточно больших и законченных модулей, как описанный UART, когда для него можно выделить отдельный файл. А что делать, когда модулей много и они не такие крупные и функционально законченные? Каждому отдельный файл? Делал я так! Именно пытался растолкать модули по разным файлам, помещая в каждый не более двух-трех (в среднем). В итоге получил под дюжину исходных файлов и примерно столько же заголовков. Проект тогда был около 8 кбайт (прошивка). Впоследствии этот же проект развивался далее, и программа была переписана под ... Все удобно разместилось в трех файлах при размере (прошивки) около 12 кбайт.
Резюме: обсуждение с общих методик скатилось на конкретные приемы, что, конечно, тоже интересно, но не настолько, тем более, что конкретные приемы у каждого свои, а методики - они тяготеют к общности, что придает обсуждению большую значимость и интерес.
Таким образом, (возвращаясь к исходной точке обсуждения) на С можно пытаться проектировать программу в С++ном стиле только путем размножения исходных файлов, что тащит за собой неудобство в работе и оверхед. Если этого не придерживаться, то тогда получаем ворох в глобальной области видимости (проблемы растут от размера программы, где-то, по экспоненте, имхо).
Поэтому реально, afaik, придерживаются компромисса - пытаются рассовать по файлам наиболее взаимосвязанное, стремясь минимизировать количество файлов. Это компромиссное решение к реальным задачам подходит лучше, чем оба маргинальных. Но и того формализованного способа проектирования, который возможен в С++, тут уже нет! О чем и речь.
HZ>> 2. Используется один стек и для данных, и для адресов возвратов, что HZ>> вынуждает для указателя стека данных использовать убогий SP, из-за OR> Не намного хуже, чем у x86 в 16-битном режиме :-) Но и не лучше :-(( HZ>> которого HZ>> возникает приличный оверхед и по размеру, и по скорости. Кроме того, OR> Да нет, если стековый кадр нужен, то в прологе порождается OR> frame pointer на Y и дальше всё "как обычно". Если стековый кадр не OR> нужен, то Y используется как хочется.
А как тогда быть с прерываниями? Т.е. вот вошли в функцию, скопировали SP в Y, начали использовать Y, и тут - бац! - прерывание! Какой поинтер будет использоваться в прерывании?
[...]
BP>>> никакие указатели и обойдется простым (r)call. Ы ? (c) OR.
HZ>> Не знаю, что такое "Ы", поэтому не могу ничего на это ответить. OR> "Ы?" это такой эмоциональный аналог "ну и?"
Хм, никогда бы не подумал.
HZ>> остальной части абзаца комментарий следующий: HZ>> В энный раз повторяю: все зависит от конкретных условий!!! OR> О чём и разговор - в огромном количестве "конкретных условий" OR> за C++ могут быть только два аргумента - "не сбивать руку" OR> и "очень уж эта мысля понравилась". Остальные расписания "плюсов плюсов" OR> как-то мало в тех "конкретных условиях" дают.
Кому как... Но поскольку все уже высказались, то и... закончим, пожалуй?!
HZ>> По поводу количества аргументов выскажу свое субъективное мнение HZ>> (которое, впрочем, совпадает с мнением вполне серьезных дядек): HZ>> если параметров при HZ>> передаче более двух, то это уже повод задуматься о том, нет ли ошибки HZ>> проектирования; OR> memcpy(dst, src, len);
Весьма нечастая низкоуровневая библиотечная функция. Количество ее аргументов не говорит в ее пользу, просто по-другому делать потянет потерю универсальности (что неприемлемо для библиотечного кода) или оверхед (что неприемлемо для низкоуровневого кода). При прочих равных ей лучше предпочесть нечто более высокоуровневое и безопасное вроде операторов присваивания или конструкторов копирования.
И заметь, я сказал: повод задуматься. Повод - не причина. :)
HZ>> если больше трех, то это причина задуматься о том же; OR> AT45D_write( chip_no, offset, buf, len);
Да, а у меня:
TDataFlash::ReadBuffer(word addr, byte N); TDataFlash::ReadMemory(word PageAddr, word ByteAddr);
И т.д. (реализация, кстати, у тебя есть :)
OR> ну у меня два чипа стоит. И разбивать это на OR> AT45_select(chip_no); OR> AT45_write(offset, buf, len);
А если два чипа, то и тем более классы рулят! :) Заводишь два объекта, при создании (в конструкторе) указываешь, какой порт и пин используются для формирования chipselect'а, чтобы потом при AT45_1.ReadMemory(...) этот пин автоматом дергался как надо. А при AT45_2.ReadMemory(...) дергался другой пин, соответствующий другому чипу. И не нужно туда селекты метать. И обращаться можно хоть через указатель.
Обычно удается найти альтернативный вариант, который не уступает по эффективности и удобству использования.
OR> неохота, тем более что всё равно есть ошибка проектирования, так как ^^^^^^^^^^^^^^^^^^^^^ OR> во второй функции три параметра.
Не издевайся! :)) Повод задуматься еще на означает ошибки.
OR> Можно, конечно, сделать "поток" AT45 с модификаторами вывода, OR> но это уже перебор :-)
Эт точно.
HZ>> если больше четырех, то тут почти наверняка что-то не так! OR> Три параметра -- _очень_ частое явление.
У кого как. Вот только что посмотрел несколько своих проектов, только в одно из них была одна глобальная функция с тремя аргументами. Могу проект предъявить, если не веришь?!
OR> Четыре - реже, но не пугает.
Редкий случай, и тут уже не повод, тут причина! У меня за все время пару раз были такие функции, но обе они выполняли то, что обычно в С делается с помощью макроса. Обе они были встраиваемыми - в этом большой смысл - не производится копирования аргументов, просто тело функции разворачивается в точке вызова, и вся эта пачка аргументов эффективно используется по месту. Т.е. в точности, как макрос, только с контролем типов.
OR> Больше 4 -- действительно нечасто.
Вот и я говорю, что в этом случае почти наверняка что-то не так.
[...]
HZ>> а во втором нельзя - приходится 0x1F явно грузить в r19, и уж только HZ>> затем 'and'. Вот эта загрузка и есть та дополнительная команда.
HZ>> Таким образом, даже тут, при _непосредственном_ использовании HZ>> члена-данного HZ>> не просматривается источника оверхеда из-за C++'ного указателя this! OR> А это ещё вопрос - почему именно так сделано. Надо весь код смотреть, OR> может если бы он 'c' в C++ варианте поселил в r24, то ему пришлось бы OR> в r4 селить что-то ещё более важное, которому иначе не нашлось бы вверху OR> места именно из-за this :-))
На первый взгляд это и есть причина. Но я попробовал добавить в сишный вариант указатель в параметры, чтобы тоже не влезло в регистры, так компилятор все равно юзает r24, а указатель этот просто копирует из регистровой пары, сохранив ее в стеке в прологе (т.е. возникает то самое пресловутое копирование указателя и сохранение/восстановление регистровой пары в стеке).
Кстати, при объявлении функции-члена с модификатором __z стек для передачи не используется - this-то в Z помещается. И в обоих вариантах - и сишном, и
++ном Z указывает на 'rpl' и используется одинаково.
Так что, нет, не в this, как таковом, тут дело, а в выборе схемы размещения регистров. Как объясняют парни из иаровского саппорта, там у них эвристический анализатор, который оценивает ситуацию и решает, какую схему выбрать. Вот при использовании функций-членов он склонен к одной схеме, а в случае обычной функции - другую.
Так что, про оверхед тут говорить не приходится - все на уровне, как уже говорилось, разброса качества работы оптимизатора, и определяется не столько языком, сколько задачей.
OR> wbr,
Блин, я с этой дискуссией никак не могу толком взяться за изучение gcc (известно для чего ;-))
...so long!
### Если мужчина просит руки женщины, значит ему надоела своя.