Вопросы по С/С++ (СИ)

Если ваш вопрос не влез ни в одну из вышеперечисленных тем, вам сюда.
Ответить
Мудрый кот
Сообщения: 1731
Зарегистрирован: Вт авг 15, 2017 10:51:13

Сообщение jcxz »

[uquote="veso74",url="/forum/viewtopic.php?p=4169769#p4169769"]Метод (или пр. код) для расчета a * b / c? Три переменные: uint32_t, цифры большие. Нет uint64_t и умножение > 32 бита, а если делится первым, теряю разрядность ниже. С float ОК (в первом приближении), но по нескольким причинам избегаю использовать.[/uquote]Умножение в столбик в школе не учили? Вот и здесь можно умножить в столбик.
Деление тоже можно реализовать на циклических сдвигах вправо и вычитаниях. Примеры процедур можно найти в инете.
И разрядность можно сделать любую. Даже если компилятор такую не поддерживает.
Реклама
Поставщик валерьянки для Кота
Сообщения: 2089
Зарегистрирован: Вс июн 19, 2016 09:32:03

Сообщение Reflector »

[uquote="ПростоНуб",url="/forum/viewtopic.php?p=4169756#p4169756"]Даже элементарный pipeline не сделать. Сваливаемся на процедурный код с объектного.[/uquote]
У людей постоянно какие-то непонятные хотелки, то исключения хотят на С++ для AVR, но поскольку они не тянут, то нужно писать не на С++ без исключений, а на С. Другой товарищ хочет писать на Java, тоже для AVR, а пишет в итоге не на С++ и даже не на С, а на ассме :) У меня исключений нет, динамического выделения памяти нет, виртуальная функций несколько на все либы, при этом какой фрагмент кода не возьми он весь привязан к фичам С++ и ощущения, что я сваливаюсь на процедурный код тоже почему-то не возникает.
ПростоНуб писал(а):Если же речь про параметр value, то в упор не понимаю, в чем тут разница между передачей указателя по значению в С и передачей ссылки в С++, которая в скомпилированном коде так же будет передаваться по значению адреса (того же значения указателя) в ней содержащегося.
Речь про value, но не в том смысле, что нельзя подобную функцию переписать с небольшими изменениями на С, просто эта и множество других функций уже написаны профессионалами и доступны всем желающим, многие из них шаблонные и работают на этапе компиляции, потому на С не могут быть написаны в принципе. А если могут, то сначала нужно написать или найти, в обоих случаях надежность и производительность будет под вопросом.
ПростоНуб писал(а):Другое дело, что при жестких требованиях к производительности и памяти, не возникает никакого желания тащить такого монстра, как from_chars, себе в код. Например, зачем мне куча кода для парсинга чисел по основанию 36, если мне нужен парсинг только целых десятичных чисел? Как это не компилируй, но ворох лишнего кода всегда прибежит.
Во-первых, то что там много шаблонного кода(еще и проскакивают constexpr функции) еще не означает, что хоть какой-то код имеющий отношение к парсингу чисел по основанию 36 попадет в бинарник. Во-вторых, from_chars() не бросает исключения, динамически не выделяет память и не работает с локалью, так что в каком-то смысле она эмбедд ориентированная :) В-третьих, далеко не факт, что другие функции, в том числе сишные, лучше справятся с парсингом чисел с плавающей точкой... Наконец в данный момент сама функция не constexpr, а в следующем стандарте уже может таковой стать и тогда можно будет без проблем парсить любые числа хоть на 4-х битных мк. У меня в либе портов на этапе компиляции одной строкой сортируется массив структур по трем параметрам при помощи std::ranges::sort() и лямбды, а несколько лет назад использования такой тяжелой функции на мк казалось маловероятным.
ПростоНуб писал(а):Было бы смешно, если не было бы так грустно. Для примера, в MS SQL, PostgreSQL или Oracle подключить скомпилированное расширение в виде so/dll - элементарно, причем динамически без остановки сервиса. Потому что они написаны на C и ABI стандартизирован. А вот к Clickhouse - только с полной перекомпиляцией всего Clickhouse и рестартом сервиса, так как он написан на C++, а ABI в C++ меняется даже от версии к версии одного компилятора, не говоря уже о разных.
Тоже самое можно сказать и про ESP32, и про старшие МК на ARM, где расширения могут быть востребованы. а полная рекомпиляция или не желательна, или даже невозможна, так как все исходники недоступны.
О чем и речь, к мк все сказанное практически никакого отношения не имеет, тем более официально распространяемый gcc для ARM именуется не иначе как gcc-arm-none-eabi :)
ПростоНуб писал(а):Речь не о простоте для изучения (не думаю, что к C это относится, так же, как, для примера, к Lisp). А о простоте синтакиса.
Только не нужно Линуса приплетать, он сам на С++ не пишет и цитата относится к 2010 году, вероятно еще про С++03, по сути совершенно другой язык. А синтаксис у С то заметно проще, но это если брать языки в полном объеме, а если взять С и накатить поверх множество мелких улучшений из С++, то получится некое подмножество С++ с синтаксисом проще, чем у С. Последнее время мне даже попадалась парочка книг с подобным подходом, в одной из них автор называл такой С++ кажется Super C, а в D есть ключ компилятора отключающий часть возможностей и превращающий язык в Better C :)
Реклама
Собутыльник Кота
Аватара пользователя
Сообщения: 2562
Зарегистрирован: Вт май 01, 2018 19:44:47

Сообщение VladislavS »

У меня в проектах для микроконтроллеров отключены эксепшены, rtti и куча установлена в ноль. При этом кто-то мне запрещает использовать класы, шаблоны, auto, лямбды, constexpr, range-based for, вариативные шаблоны, концепты, перегрузку, вывод типов и т.д. и т.п. Да ну на! Эксепшенов им не хватает. То что есть сначала переварите.
Поставщик валерьянки для Кота
Сообщения: 2089
Зарегистрирован: Вс июн 19, 2016 09:32:03

Сообщение Reflector »

Погодите еще вместе с непонятно почему запаздывающим gcc для ARM подъедут модули, которые уже работают в VS, хотя есть мелкие недочеты. И они настолько быстрее, что практически вся стандартная библиотека импортится одной

Код: Выделить всё

import core.std;
Более того в gcc 12 еще обещают новый на порядок более быстрый линкер...
Реклама
Эиком - электронные компоненты и радиодетали
Собутыльник Кота
Аватара пользователя
Сообщения: 2723
Зарегистрирован: Пт сен 07, 2018 20:20:02
Откуда: деревня в Тульской губернии

Сообщение ПростоНуб »

[uquote="Reflector",url="/forum/viewtopic.php?p=4169901#p4169901"]У людей постоянно какие-то непонятные хотелки[/uquote]
Одна эта фраза говорит уже об очень многом...

Reflector писал(а):надежность и производительность будет под вопросом.
Уже видно по исходникам, что рассматриваемая from_chars() сделана излишне универсальной и перегруженной.
Во-вторых, плата за это - когда проект собирается даже на выделенном сервере часы. Тогда невольно жалеешь, что это не C, собирающийся по сравнению с C++ моментально, если не были затронуты корневые включаемые файлы, что происходит очень редко. Пробовали сделать крупный проект на QT? Сколько он собирался?
Reflector писал(а): Во-первых, то что там много шаблонного кода(еще и проскакивают constexpr функции) еще не означает, что хоть какой-то код имеющий отношение к парсингу чисел по основанию 36 попадет в бинарник.
А как он не попадет, если base будет переменная? Я могу ограничить пользователя, например, base<=10 при вводе, но у меня большие сомнения, что оптимизатор настолько умен, что не только увидит и запомнит это ограничение, но еще и применит его при оптимизации case в __to_chars_i()
Reflector писал(а):эта и множество других функций уже написаны профессионалами и доступны всем желающим
А ребята не знали. что профессионалы пишут только на С++ и их труды доступны всем желающим только на C++. А на остальных языках, включая C, профессионалы не пишут и труды не публикуются? )))
Reflector писал(а):В-третьих, далеко не факт, что другие функции, в том числе сишные, лучше справятся с парсингом чисел с плавающей точкой...
Вас несет. Возьмите и сравните c atof, как по размеру кода, так и по производительности, и уже после этого рассуждайте. А то уж очень демагогией попахивает. Если ничего не делать. то все будет "далеко не факт".
Reflector писал(а):
ПростоНуб писал(а):Было бы смешно, если не было бы так грустно. Для примера, в MS SQL, PostgreSQL или Oracle подключить скомпилированное расширение в виде so/dll - элементарно, причем динамически без остановки сервиса. Потому что они написаны на C и ABI стандартизирован. А вот к Clickhouse - только с полной перекомпиляцией всего Clickhouse и рестартом сервиса, так как он написан на C++, а ABI в C++ меняется даже от версии к версии одного компилятора, не говоря уже о разных.
Тоже самое можно сказать и про ESP32, и про старшие МК на ARM, где расширения могут быть востребованы. а полная рекомпиляция или не желательна, или даже невозможна, так как все исходники недоступны.
О чем и речь, к мк все сказанное практически никакого отношения не имеет
Для имеющих проблемы со зрением использовал полужирное начертание )))
Reflector писал(а): Только не нужно Линуса приплетать
Почему это не надо? Он достаточно четко выразился, что один и тот же код в C++ может, в зависимости от контекста, выполнять совершенно разные действия. Вы же сами это пропагандируете шаблонами. Вот только это очень удобно при написании программы, но сильно повышает трудоемкость при ее сопровождении, что как раз и подчеркнул Линус. Например, я сам не раз сталкивался с совершенно негуманоидой логикой реализации операций сложения или сравнения для некоторых объектов некоторыми программистами.

Добавлено after 10 minutes 29 seconds:
[uquote="VladislavS",url="/forum/viewtopic.php?p=4169988#p4169988"]Эксепшенов им не хватает. То что есть сначала переварите.[/uquote]
Не бойтесь, давно переварили. Потому и нарвавшись один раз на необходимость чуть-ли не полностью переписывать код, переносимый с Linux на ESP32, сразу исчезает желание с C++ связываться. Так как C код портируется на порядок легче.
Реклама
Собутыльник Кота
Аватара пользователя
Сообщения: 2562
Зарегистрирован: Вт май 01, 2018 19:44:47

Сообщение VladislavS »

Не надо QT тащить в микроконтроллеры и с головой будет полный порядок. У меня проекты под микроконтроллеры на современном многоядерном компьютере/ноутбуке с SSD собираются в пределах 5-10 секунд. Простые даже засечь не успеваю.

[uquote="ПростоНуб",url="/forum/viewtopic.php?p=4170286#p4170286"]Так как C код портируется на порядок легче.[/uquote]Неправда. Код С++ намного проще и быстрее выводится на уровень абстракции объекта, когда не надо думать на каком процессоре или порту он будет работать, а можно просто реализовывать именно его данные и поведение.

ПростоНуб, переход на личности не делает ваши аргументы весомее, а скорее наоборот :(

Добавлено after 32 minutes 28 seconds:
[uquote="ПростоНуб",url="/forum/viewtopic.php?p=4170286#p4170286"]А ребята не знали. что профессионалы пишут только на С++ и их труды доступны всем желающим только на C++. А на остальных языках, включая C, профессионалы не пишут и труды не публикуются? )))[/uquote]Функции стандартной библиотеки имеют преимущества. Во-первых, их не надо искать по помойкам, они всегда под рукой. Во-вторых, они написаны в тесной связи с разработчиками компилятора. Многии из них реализует именно компилятор как встроенные функции. В-третьих, они в большинстве своём шаблонные. Это значит они инстанцируются наиболее подходящим способом, они хорошо инлайнятся, они хорошо оптимизируются. В-четвёртых, их действительно пишут профессионалы, что в случае с найденным на помойке кодом может оказаться далеко не так.
Реклама
Друг Кота
Сообщения: 12867
Зарегистрирован: Сб дек 18, 2021 19:25:32

Сообщение Martian »

Правильно, не надо Qt, надо .Net ;)

Добавлено after 41 minute 20 seconds:
VladislavS, с чего Вы взяли, что .Net не по существу? Ну или огласите весь список, пожалуйста, чтоб Вас не нервировать.
Контактная информация:
Поставщик валерьянки для Кота
Сообщения: 2089
Зарегистрирован: Вс июн 19, 2016 09:32:03

Сообщение Reflector »

ПростоНуб писал(а):Уже видно по исходникам, что рассматриваемая from_chars() сделана излишне универсальной и перегруженной.
Ок, можно ведь протестировать... Компилируем следующий код на F7:

Код: Выделить всё

PerfCounter perf;
perf.start();

static char arr[] = "1234567890";

volatile int val = atoi(arr);
	
auto t = perf.getElapsedTicks();
rtt.println(t, val);
Сама функция atoi, которая возвращает 0 при ошибке, потому непонятно как ей парсить "0", добавила к бинарнику 680 байт и исполнялась 309 тактов. Однако даже IDE рекомендует вместо atoi() использовать strtol(), которой ноль уже распарсить можно и для нее получаем +676 байт и 300 тактов. У меня есть собственная constexpr функция, с ней получается +128 байт и 182 такта. Наконец from_chars() показывает практически идентичный результат: +128 байт и 183 такта :)
ПростоНуб писал(а):Во-вторых, плата за это - когда проект собирается даже на выделенном сервере часы. Тогда невольно жалеешь, что это не C, собирающийся по сравнению с C++ моментально, если не были затронуты корневые включаемые файлы, что происходит очень редко. Пробовали сделать крупный проект на QT? Сколько он собирался?
Специально откопал старенький ноут, создаю в VisualGDB сишный проект на HAL мигающий светодиодом, по умолчанию он компилирует намного больше *.c файлов из HAL, чем нужно для светодиода, но тем не менее, полная пересборка с -O2 -flto занимает 8 сек. Теперь берем эмулятор спектрума на H750 который картину выводит одновременно на дисплей и VGA и где все начиная c пина и заканчивая либами USB/FMC/LTDC/SDMMC/FatFs в виде шаблонный классов и получаем 23 сек. Но зачем после каждого изменения полностью пересобирать проект, если что-то поменять в парочке файлов, то сама компиляция займет секунд 5 и еще 13 сек. линковка и время линковки можно существенно сократить отключив LTO. На нормально железе все будет происходить в разы быстрее и это даже без учета модулей...
ПростоНуб писал(а):А как он не попадет, если base будет переменная? Я могу ограничить пользователя, например, base<=10 при вводе, но у меня большие сомнения, что оптимизатор настолько умен, что не только увидит и запомнит это ограничение, но еще и применит его при оптимизации case в __to_chars_i()
В большинстве случаев парсят десятичные числа, собственно потому у base значение по умолчанию равно 10, так что скорее всего будет константа или base не будет вообще, в таком случае можно изначально попасть в перегруженную функцию принимающую на один параметр меньше.
ПростоНуб писал(а):Вас несет. Возьмите и сравните c atof, как по размеру кода, так и по производительности, и уже после этого рассуждайте. А то уж очень демагогией попахивает. Если ничего не делать. то все будет "далеко не факт".
Так сравнил уже, к сожалению поддержку float для from_chars() добавили только в gcc 11, которого для ARM еще нет, но я изначально ожидал возможный выигрыш именно на float, а оказалось он есть даже для целых чисел.
ПростоНуб писал(а):Для имеющих проблемы со зрением использовал полужирное начертание )))
Можно хоть все посты полужирным выделить и третий раз Линуса процитировать, все равно никто не перестанет писать на С++ для Cortex-M потому что возможны какие-то гипотетические проблемы с ABI на Cortex-A :) На ПК у меня никаких проблем с ABI нет, к С++ проектам линкуются либы от С, а C# грузит dll от C/C++, когда-то я даже портировал DirectX 9/10 на D, там с одной стороны либы в coff, а с другой в omf и ничего, большинство подобных проблем решаемо.
ПростоНуб писал(а):Почему это не надо? Он достаточно четко выразился, что один и тот же код в C++ может, в зависимости от контекста, выполнять совершенно разные действия. Вы же сами это пропагандируете шаблонами. Вот только это очень удобно при написании программы, но сильно повышает трудоемкость при ее сопровождении, что как раз и подчеркнул Линус. Например, я сам не раз сталкивался с совершенно негуманоидой логикой реализации операций сложения или сравнения для некоторых объектов некоторыми программистами.
Распространенный пример из мира эмбедда, пожалуйста. Лично у меня перегружены операторы для строгих перечислений, тут никаких вопросов быть не может, любой гайд по С++ будет рекомендовать не использовать сишные перечисления. Далее перегружен оператор "|" для пинов:

Код: Выделить всё

return sck | miso | mosi | nss;
Еще операторы сравнения для списка пинов и еще класс для работы с фиксированной точкой, там много чего перегружено, но в результате такой тип может работать там где и float:

Код: Выделить всё

constexpr auto f1 = sin(1.2345f);
constexpr auto f2 = sin(Fixed<12>(1.2345f));
rtt.println(f1, (float)f2);   // 0.94398332, 0.94397926
Вроде все, причем очень сильно подозреваю, что у других пишущих на полюсах в среднем еще меньше, а оказывается это не менее серьезная проблема, чем невозможность использовать исключения на AVR :)
Собутыльник Кота
Аватара пользователя
Сообщения: 2562
Зарегистрирован: Вт май 01, 2018 19:44:47

Сообщение VladislavS »

Перегрузка операторов для сложных типов как раз таки позволяет использовать их так же как простые, что повышает читаемость кода. Так то и название функций в С можно такими сделать, что запутаются сами K&R. Но мы же про программистов, а не обезьян, правда?
Поставщик валерьянки для Кота
Сообщения: 2089
Зарегистрирован: Вс июн 19, 2016 09:32:03

Сообщение Reflector »

Вспомнил про еще одну полезную особенность ссылок. Если передавать по ссылке массив, то информация о его размере не теряется, в частности стандартная функция size() использует эту особенность:

Код: Выделить всё

template<typename T, std::size_t N>
constexpr std::size_t inline size(const T(&)[N]) { return N; }

int arr[10];
int arr2[size(arr)];
Собутыльник Кота
Аватара пользователя
Сообщения: 2562
Зарегистрирован: Вт май 01, 2018 19:44:47

Сообщение VladislavS »

Знатоки стандарта, обязан ли первый кандидат всегда выигрывать перегрузку, если requires выполняется?

Код: Выделить всё

static inline void clear() requires requires() { base()->BRR; }
{
  base()->BRR = pin_mask;
}
static inline void clear()
{
  *((volatile uint16_t*)&base()->BSRR + 1) = pin_mask;
}
Поставщик валерьянки для Кота
Сообщения: 2089
Зарегистрирован: Вс июн 19, 2016 09:32:03

Сообщение Reflector »

Да, для первой функции добавлено уточнение, а при перегрузке выбирается наиболее подходящий тип.
Собутыльник Кота
Аватара пользователя
Сообщения: 2562
Зарегистрирован: Вт май 01, 2018 19:44:47

Сообщение VladislavS »

Смущает, что это не SFINAE, где специализация решает, а перегрузка. При перегрузке сигнатура решает или более короткий путь приведения к этой сигнатуре. Как на это requires влияет не очевидно.
Поставщик валерьянки для Кота
Сообщения: 2089
Зарегистрирован: Вс июн 19, 2016 09:32:03

Сообщение Reflector »

Допустим у функции есть аргумент:

Код: Выделить всё

void foo(auto val) requires requires() { val.brr; } {}
void foo(auto val) {}
Можно написать отдельно концепт и получить функции с разными сигнатурами:

Код: Выделить всё

static void foo(HasBrr auto val) {}
static void foo(std::integral auto val) {}
static void foo(auto val) {}
Тут довольно очевидно, что последняя функция выполняется по остаточному принципу, но вовсе не обязательно накладывать ограничения именно на аргумент...
Собутыльник Кота
Аватара пользователя
Сообщения: 2562
Зарегистрирован: Вт май 01, 2018 19:44:47

Сообщение VladislavS »

С auto аргументом это шаблон, то есть, тут sfinae работает.
Поставщик валерьянки для Кота
Сообщения: 2089
Зарегистрирован: Вс июн 19, 2016 09:32:03

Сообщение Reflector »

Концепты только с шаблонам и работают, не будет auto, значит функция должна находиться внутри шаблонного класса.

Код: Выделить всё

void foo() requires true {}	// Error: constraints on a non - templated function
А пример приведенный выше демонстрирует, что в принципе на requires можно смотреть как на часть сигнатуры, не важно есть у функции аргументы или нет.

ps. Вот еще пример подобных перегрузок без аргументов:
Спойлер

Код: Выделить всё

class Foo
{
public:
	void bar()& { rtt.println("foo&"); }
	void bar()const & { rtt.println("foo& const"); }
	void bar()&& { rtt.println("foo&&"); }
};

Foo foo;
foo.bar();		// "foo&"
	
constexpr Foo fooc;
fooc.bar();		// "foo& const"

Foo{}.bar();	// "foo&&" (временный объект)
Собутыльник Кота
Аватара пользователя
Сообщения: 2562
Зарегистрирован: Вт май 01, 2018 19:44:47

Сообщение VladislavS »

А как думаешь, вот такое законно или хулиганство и в любой момент может "отвалиться"?

Код: Выделить всё

static inline void clear()
{
  if constexpr(requires(){ base()->BRR; })
    base()->BRR = pin_mask;
  else
    *((volatile uint16_t*)&base()->BSRR+1)=pin_mask;
}
Поставщик валерьянки для Кота
Сообщения: 2089
Зарегистрирован: Вс июн 19, 2016 09:32:03

Сообщение Reflector »

Чего ему отваливаться, должно работать, главное чтобы base() возвращал не GPIO_TypeDef*, а T*, иначе если BRR в структуре не определен, то получим ошибку компиляция.
Собутыльник Кота
Аватара пользователя
Сообщения: 2562
Зарегистрирован: Вт май 01, 2018 19:44:47

Сообщение VladislavS »

if constexpr "ленивая" - не вычисляет значение функций и не выводит их типы в провалившейся ветке. Пока base() возвращает auto, можно любую синтаксически верную хрень написать в невыполняющейся ветке. Если возвращать GPIO_TypeDef* конечно зафэйлится на BRR.
Поставщик валерьянки для Кота
Сообщения: 2089
Зарегистрирован: Вс июн 19, 2016 09:32:03

Сообщение Reflector »

[uquote="VladislavS",url="/forum/viewtopic.php?p=4176007#p4176007"]if constexpr "ленивая" - не вычисляет значение функций и не выводит их типы в провалившейся ветке.[/uquote]
Речь не про ветки, а про условие. Когда компилируется base(), то компилятор может определить возвращаемый тип даже при том, что он auto, соответственно при проверке должна быть ошибка. Но по факту ты прав, ошибки нет, получается сами функции возвращающие auto немного "ленивые".
Ответить

Вернуться в «Разные вопросы по МК»