Перейти к содержанию
    

Стили оформления программ на C/C++ и их применимость

В 05.01.2024 в 05:59, dxp сказал:

всякие интересные штуки типа замыканий (closure -- захват контекста)

А я вот как-то не соображу. В Java замыкания и лямбды я часто использую, для отложенного выполнения кода (например, чтобы не в ГУИ потоке его выполнять). Ну то есть, создается лямбда, ставится в очередь на исполнение и потом, когда-то там исполняется...

Но в C++ просто создать лямбду в области видимости, запихать ее в очередь на исполнение, а потом уйти из области видимости не получится? Лямбда локальная переменная и она исчезнет как и все локальные переменные. Сходу погуглив, нашел лишь примеры, когда после создания лямбды и исполнения ее в другом потоке, из зоны видимости не выходят.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

13 часов назад, Quasar сказал:

Но в C++ просто создать лямбду в области видимости, запихать ее в очередь на исполнение, а потом уйти из области видимости не получится? Лямбда локальная переменная и она исчезнет как и все локальные переменные. Сходу погуглив, нашел лишь примеры, когда после создания лямбды и исполнения ее в другом потоке, из зоны видимости не выходят.

Ну, если делаете захват по значению, то проблем вроде и нет. А по ссылке -- да, тут так просто в С++ нельзя делать, нужно следить за тем, чтобы переменная, захваченная по ссылке, существовала в момент обращения к ссылке. Это обычное правило C/С++, когда не нужно передавать во внешний контекст вызываемой функции указатели на локальные объекты, т.к. после выхода из функции эти объекты уже не существуют.

UPD. Перечитал цитату. Выше ответил не на этот вопрос. Но принцип там тот же. Да, лямбда -- это такой же объект, как и другие, подчиняется общим правилам. Поэтому если это локальный объект внутри, например, функции, то передавать указатель (или ссылку) на него за пределы функции нельзя. Но вполне можно, к примеру, вернуть такой объект из функции по значению.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Позвольте еще немного подушнить😃

В 05.01.2024 в 08:59, dxp сказал:

Перечислимый тип в С -- это и не тип никакой, а просто способ создавать именованные литералы.

Ну почему. Изначально планировался именно как тип. Целый перечисляемый тип. И в стандарте описан как тип.

Цитата

А если пара enum'ов, описывающих состояния конечного автомата, в обоих есть по смыслу одинаковое состояние WAIT, то вот он и конфликт имён сразу же.

:: от нужного enum? Или Вы всегда их безымянными делаете? Тогда да, никак. Но у меня привычка enum eName { ... };🙂

Я стараюсь enum (и даже enum class) держать близко к месту использования, чтобы все эти имена не торчали в окружающем пространстве имен.

А пространство имен модуля ограничиваю namespace-ом этого модуля (модуль == некая единица трансляции или их группа). Удобно, вроде как.

 

Насчет предикатов, функторов, лямбд: насколько я понял, их ценность кроется, в основном, при работе со стандартной библиотекой.

А вот constexpr действительно, хорошая штука. Глубина "прохода" компилятора в этих выражениях выше, чем у обычных const в Си. Поэтому даже на малых уровнях оптимизации (не помню как насчет -O0) целый алгоритм с вызовами функций, циклами, условными выражениями и т.д., сворачивается в финальную константу и в финале там 2 ассемблерные инструкции. Единственное, какого-то вменяемого полного описания constexpr-выражений и функций, constexpr if() найти не просто, т.к. между C++11 и C++17 с этими constexpr что-то произошло (че то там передумали в стандарте), поэтому в интернетах натыкаюсь на противоречивые статьи.

Например: почему следующая строчка не вычисляется в compile-time?

void func() {
  u32 constexpr i = ... ; // ... тут может быть некое выражение, где над числом издеваются (<<, >>, |, ~, & и т.д.), но это точно константа
}

Стоит добавить static перед объявлением, как выражение полностью становится compile-time.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

12 минут назад, Arlleex сказал:

Но у меня привычка enum eName { ... };

А пофиг. Только enum class ограничивает область видимости имен.

14 минут назад, Arlleex сказал:

Изначально планировался именно как тип. Целый перечисляемый тип. И в стандарте описан как тип.

Но неявное преобразование к int и из int, плюс хранение в ячейке памяти такого же размера, как int свело все планы на нет. Получили "птицу, которая выглядит как утка, плавает как утка и крякает как утка", но назвали ее, почему-то, не уткой.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1 час назад, Arlleex сказал:

Насчет предикатов, функторов, лямбд: насколько я понял, их ценность кроется, в основном, при работе со стандартной библиотекой.

И при написании своих библиотек. В рядовом коде они, действительно, выглядят не очень уместно. Но когда с ними на ты, то так и подмывает применить.

1 час назад, Arlleex сказал:

почему следующая строчка не вычисляется в compile-time?

В смысле?

auto constexpr X = 100;
auto Y = 100;

auto func() 
{
  int constexpr i = X<<2;  // Компайлтайм, Ok
  int constexpr I = Y<<2;  // Рантайм, ошибка
}

 

1 час назад, Arlleex сказал:

Стоит добавить static перед объявлением, как выражение полностью становится compile-time.

Ни в коем случае, это сделает локальную переменную фактически глобальной для данной функции, но она всё так же должна быть инициализирована вычисленным на этапе компиляции значением.

1 час назад, Arlleex сказал:

не помню как насчет -O0

и это не зависит от оптимизации. При любой оптимизации должно быть вычислено на этапе компиляции.

1 час назад, Arlleex сказал:

Единственное, какого-то вменяемого полного описания constexpr-выражений и функций, constexpr if() найти не просто

Да вроде всё просто:

1. constexpr переменные (читай константы). Они всегда должны быть инициализированы значением вычисленным на этапе компиляции.

2. constexpr функции. Они могут работать как в компайлтайме, так и в рантайме. В первом случае (когда все условия сложились) их значения можно использовать там где подразумевается константа этапа компиляции: параметры шаблонов, инициализация constexpr переменных, if constexpr()... Если значение нельзя в компайлтайме вычислить, то работает как обычная функция в рантайме. В С++20 появилось более жёсткое consteval, которое не пускает функцию в рантайм.

3. if constexpr() Тут всё просто. Выражение "под условием" должно быть вычислено на этапе компиляции. 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

А кто-нибудь здесь пишет event-driven с использованием средств C++? Лямбды и замыкания напрашиваются, чтобы ими записывать цепочки callback функций с сохранением контекста. Иногда интересуюсь, но попробовать времени не хватает.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1 час назад, Сергей Борщ сказал:

Получили "птицу, которая выглядит как утка, плавает как утка и крякает как утка", но назвали ее, почему-то, не уткой.

Ну, наверное.
 

44 минуты назад, VladislavS сказал:

Ни в коем случае, это сделает локальную переменную фактически глобальной для данной функции, но она всё так же должна быть инициализирована вычисленным на этапе компиляции значением.

Ввел в заблуждение, извиняюсь. Сейчас открыл проект, еще раз глянул, в чем был вопрос, а он и не в constexpr на самом то деле.

inline void initGPIO() {
  cpu_alloc_critical_section_context(csc);
  
  cpu_entry_critical_section(csc);
  
  u32 constexpr moderCLSBSet = 1u << GPIO_MODER_MODER10_Pos |
                               1u << GPIO_MODER_MODER12_Pos;
  
  upd_bit(GPIOC->MODER, rst(0x3 * moderCLSBSet), set(0x2 * moderCLSBSet), inv(0));
  
  __DSB();
  
  cpu_leave_critical_section(csc);
}
Спойлер
0x08000634 B085      SUB           sp,sp,#0x14
   122:     cpu_entry_critical_section(csc); 
   123:      
0x08000636 E7FF      B             0x08000638
   401:   __ASM volatile ("MRS %0, primask" : "=r" (result) ); 
0x08000638 F3EF8010  MRS           r0,PRIMASK
0x0800063C 9002      STR           r0,[sp,#0x08]
   402:   return(result); 
0x0800063E 9802      LDR           r0,[sp,#0x08]
0x08000640 9001      STR           r0,[sp,#0x04]
0x08000642 F3EF8010  MRS           r0,PRIMASK
0x08000646 B672      CPSID         I
0x08000648 9003      STR           r0,[sp,#0x0C]
0x0800064A E7FF      B             0x0800064C
0x0800064C F04F7088  MOV           r0,#0x1100000
   124:     u32 constexpr moderCLSBSet = 1u << GPIO_MODER_MODER10_Pos | 
   125:                                  1u << GPIO_MODER_MODER12_Pos; 
   126:  
0x08000650 9000      STR           r0,[sp,#0x00]
0x08000652 F6400000  MOVW          r0,#0x800
0x08000656 F2C40002  MOVT          r0,#0x4002
   127:     upd_bit(GPIOC->MODER,   rst(0x3 * moderCLSBSet),   set(0x2 * moderCLSBSet),   inv(0)); 
   128:      
0x0800065A 6801      LDR           r1,[r0,#0x00]
0x0800065C F021714C  BIC           r1,r1,#0x3300000
0x08000660 F1017108  ADD           r1,r1,#0x2200000
0x08000664 6001      STR           r1,[r0,#0x00]
   129:     __DSB(); 
   130:      
0x08000666 F3BF8F4F  DSB.W         
   131:     cpu_leave_critical_section(csc); 
0x0800066A 9801      LDR           r0,[sp,#0x04]
0x0800066C 9004      STR           r0,[sp,#0x10]
   429:   __ASM volatile ("MSR primask, %0" : : "r" (priMask) : "memory"); 
0x0800066E 9804      LDR           r0,[sp,#0x10]
   429:   __ASM volatile ("MSR primask, %0" : : "r" (priMask) : "memory"); 
0x08000670 F3808810  MSR           PRIMASK,r0
   132:   } 
   133:    
0x08000674 B005      ADD           sp,sp,#0x14
0x08000676 4770      BX            lr

Видна пара MOV 0x1100000 + последующий STR в стек. Как только добавляю static, эти две инструкции пропадают. Поведение логично. Оно и с обычным const так.

Видимо, я когда-то в отладке хотел избавиться от этих лишних запихиваний в стек на -O0. Не нашел другого способа, как добавить static, map-файл говорит, что линкер после линковки удалил эти 4 байта.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

48 минут назад, VladislavS сказал:

Ни в коем случае, это сделает локальную переменную фактически глобальной для данной функции, но она всё так же должна быть инициализирована вычисленным на этапе компиляции значением.

Опять же - это ведь не переменная, а константа. static const или static constexpr - это объявление не переменной, а константы. Видимость ограничена блоком (функцией), время жизни - глобальное, супер.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

22 минуты назад, amaora сказал:

А кто-нибудь здесь пишет

Не знаю это ли вы имеете в виду. Вот у меня в одной из библиотек обработчик прерываний USB. В нём IRQHandlers это список типов. Для каждого элемента списка выполняется лямбда.image.thumb.png.e87d0779ee24b07eb56ae9eaa5099ef5.png

А сам список зависит от реализуемого класса. Например, для CDC обработчики двух конечных точек.

image.thumb.png.c6fd84ce0b4a5c27c4325cf08500ff71.png

 

 

 

3 минуты назад, Arlleex сказал:

Опять же - это ведь не переменная, а константа.

Вообще-то переменная. Изменять нельзя, правда. :)))

4 минуты назад, Arlleex сказал:

Видимость ограничена блоком (функцией), время жизни - глобальное, супер.

Это и в православном С так было со статическими локальными переменными.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

3 hours ago, VladislavS said:

Не знаю это ли вы имеете в виду. Вот у меня в одной из библиотек обработчик прерываний USB. В нём IRQHandlers это список типов. Для каждого элемента списка выполняется лямбда.

Ну здесь я вижу передачу тела цикла в свой foreach который видимо работает в compile-time. Не об этом спрашивал.

Для примера, если бы при обработке прерывания возникла необходимость подождать некоторого события (например сброс BUSY флажка), а после сделать ещё какие-то действия. Вот эти действия можно записать лямбдой, захватить в неё необходимый контекст и передать планировщику в качестве обработчика события "BUSY=0". Код будет похож на линейный, как если бы ждали флажок в цикле.

Интересна имитация блокирующих функций в таком стиле, подписка на событие и продолжение работы в обработчике. Как бы тогда мог выглядеть какой-нибудь сильно разветвленный код с множеством вложенных вызовов? Например, реализация printf, которая на нижнем уровне вызывает putc. Не будут ли слишком велики накладные расходы на передачу контекста? Как достаточно красиво делать длинные цепочки обработчиков?

Примитивный псевдокод.

Spoiler
void putc(char x, ehtype eh)
{
	uart_queue.put(x);
	uart_waiting.put(eh);
}
...

void UART_ISR()
{
	...
	while (uart_queue.space() > 0 && uart_waiting.len() > 0) {

		auto eh = uart_waiting.get();

		eh();
	}
}

...
putc('0' + i, [=] () {
	i++;
	putc('0' + i, [=] () {
			...
		}); });

 

Изменено пользователем amaora

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

уважаемые плюсоводы, подскажите... файл *.cpp, код такой

int *array[] =
{
  new int(1),
  0
};

объявлен и определён массив указателей вне тела функции. с компилятором arm-none-eabi-g++ -mcpu=cortex-m4 -std=gnu++17 работает. с IAR ANSI C/C++ Compiler V9.40.1.364/W64 for ARM  (--cpu=Cortex-M4, --c++,  с++17) зависает до майна на операторе new. На одном и том же МК.

 

 

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Неужели нет внутрисхемного отладчика, чтобы запустить под ним программу и посмотреть, куда ее унесло? Вангую, что не выделена память под кучу, new int(1) не смог зарезервировать память и свалился в заглушку с бесконечным циклом.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Да вроде всё работает. Настройки по умолчанию. Память под кучу выделена?

image.thumb.png.25d0d3abef18f7ff9a7c00a2f3f219fc.png 

image.thumb.png.1fc96ced224bd12a5d6f84d4135a0181.png

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Локализовал проблему. Память под хип выделена, но во внешней SDRAM, которая инитится в main. Всем спасибо.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Во внешней SDRAM хранить одиночные переменные или малые объемы данных - неэффективно. SDRAM имеет много издержек на одиночные обращения - выбор строки, выбор столбца, задержки CAS/RAS, чтение/запись. 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

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

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...