Пользовательская реализация assert(): не выводит сообщение об ошибке
Я пишу свой собственный макрос assert() на Arduino, как показано ниже.
Когда я запускаю его из main (), он всегда работает правильно (печатает сообщение и попадает в бесконечный цикл), однако, когда я вызываю его в функции класса, он застревает, ничего не печатая. Почему это так?
assert.cpp/.h
#ifndef _ASSERT_H_
#define _ASSERT_H_
void assertion_failure(const char* expr, const char* file, int linenum);
#define assert(expr) \
if (expr) ; \
else assertion_failure(#expr, __FILE__, __LINE__)
#endif /* _ASSERT_H_ */
#include "assert.h"
#include "Arduino.h"
void assertion_failure(const char* expr, const char* file, int linenum)
{
Serial.print("Assertion failed in '");
Serial.print(file);
Serial.print("' on line ");
Serial.print(linenum);
Serial.print(": ");
Serial.println(expr);
Serial.flush();
while (1);
}
И контекст, в котором assert застревает ниже (однако он хорошо работает, когда я вызываю его в main ()):
int ADS1256::assert_debug()
{
uint8_t id = read_register(STATUS) >> 4;
assert(false); // Застревает здесь, ничего не печатая на экран
return id;
}
Редактировать: похоже, это как-то связано с конфигурацией сборки. Я использую систему сборки PlatformIO. Это не работает, когда я использую эту файловую структуру:
├── include
│ └── assert.h
├── lib
│ ├── ADS1256
│ ├── ads1256.cpp
│ ├── ads1256.h
│ └── ads1256_regmap.h
├── src
├── assert.cpp
└── main.cpp
Однако, если я перемещу assert.cpp/.h в выделенный каталог в lib/, он будет работать нормально. В чем причина этого? Как я могу выяснить, почему это произошло?
Он работает с этим макетом (опять же, используя конфигурацию сборки PlatformIO по умолчанию):
├── include
├── lib
│ ├── ADS1256/
│ ├── Assert/
│ ├── assert.cpp
│ └── assert.h
├── src
└── main.cpp
@Jurc192, 👍1
Обсуждение1 ответ
Лучший ответ:
Согласно комментарию к вопросу, это не так закончено, как хотелось бы, но это начало.
Я глубоко подозреваю,что вы столкнулись с конфликтом между именем вашего заголовка и стандартным заголовком assert.h
. Вы используете то же имя заголовка и те же имена макросов, что и в стандартном заголовке c, который является частью как c++, так и "Arduino". Стандартный assert()
с тем, как все обычно настраивается на ядре AVR Arduino, в основном просто входит в плотный цикл и ничего не делать.
Я бы подумал о том, чтобы назвать ваш макрос чем-то иным, чем assert
, и назвать ваш заголовок чем-то иным, чем assert.h
. Если вы хотите получить точные сведения о том, почему, нам нужно увидеть такие вещи, как ваш порядок пути включения и сами директивы включения. Даже тогда совет будет примерно таким же.
Диагностика
--save-temps
В platform.io вы должны иметь возможность добавить --save-temps
в командную строку avr-gcc, чтобы заставить ее производить (или сохранять) вывод этапа предварительной обработки. GCC комментирует их, показывая, откуда взялся каждый раздел всего вывода, так что вы можете видеть, какие заголовки
фактически включала каждая
директива #include и в каком порядке все это происходило.
НДЕБУГ
Что-то еще, что вы можете попробовать в качестве диагностической меры, - это определить NDEBUG
в настройках вашего проекта. Обычно это определяется IDE для "режима выпуска". Это в основном превращает assert()
в NOP, что, в свою очередь, означает, что он не будет делать обычную блокировку avr-libc. Если вы видите, что утверждение avr-libc расширяется вместо вашего собственного, то поведение изменится не только на блокировку, но и на то, что будет делать ваше собственное утверждение
.
Поддержка Arduino assert() на основе AVR
assert() avr-libc
пытается напечатать свое сообщение об утверждении на stderr, который не настроен на ардуино на основе AVR, чтобы что-то делать. Таким образом, внешнее поведение заключается в том, что он запирается. Тем не менее, вы можете завершить реализацию avr-libc stderr с точки зрения функции Serial.write Arduino
, и в этом случае обычное утверждение avr-libc будет работать просто отлично. Вот один из примеров завершения печати stdio avr-libc в терминах
Serial
.
__FILE__
Предостережение и SRAM
То, о чем вы должны знать при использовании __FILE__
и поэтому утверждать
(будь то avr-libc или ваш), заключается в том, что __FILE__
расширяется до строкового литерала, который является полным путем к исходному имени файла, которое может быть довольно длинным. На UNO (вы пометили) они идут в SRAM. Таким образом, если вы используете assert
в паре разных файлов, вы можете очень быстро съесть доступный SRAM на AVR.
По этой причине в моих собственных маленьких макросах типа assert я предпочитаю сообщать не о файле, а о пользовательском теге, который часто представляет собой всего лишь одну букву или цифру. Кроме того, часто мои утверждения мигают номером __LINE__
на светодиоде, где __FILE__
в любом случае непрактично в моем типичном случае использования.
Спасибо, что указали мне на реализацию avr-libc sterr/assert!
Таким образом, мы согласны на столкновение со стандартным заголовком assert.h. Но я хочу понять, в чем и как возникает проблема. Это имя файла, это имя макроса, и то, и другое?
Как я могу проверить, какая реализация была использована (например, проверка двоичных файлов, символов или sth)?
Я попытался использовать флаг препроцессора -H
(показывает абсолютные пути включенных заголовков), но не увидел в нем ничего полезного..., @Jurc192
Э-э-э, я постараюсь сделать обновление, чтобы решить эту проблему., @timemage
См.раздел Диагностика и --save-temps. Я не собираюсь (оставил это раньше) идти намного дальше с этим ответом, не выяснив точно, что попросить вас сделать или включить в вопрос, а затем попросить вас предоставить это, что, вероятно, добавит много шума к вопросу., @timemage
Я задал тот же вопрос в PlatformIO [community](https://community.platformio.org/t/custom-assert-cpp-h-clashing-with-avr-gccs-assert/21865/3) и столкновение с макросом assert avr-libc действительно является проблемой., @Jurc192
Если это действительно тот ответ, который вы ищете, то вы можете также опубликовать свой собственный ответ, вызывающий этот ответ, и я избавлюсь от этого. Вам нужно будет пометить свой как принятый., @timemage
- Как решить проблему «avrdude: stk500_recv(): programmer is not responding»?
- Как узнать частоту дискретизации?
- Что такое Serial.begin(9600)?
- Использовать все контакты как цифровые входы/выходы
- Float печатается только 2 десятичных знака после запятой
- Arduino как USB HID
- Serial1' was not declared in this scope
- Очень простая операция Arduino Uno Serial.readString()
Ваш источник называется “Assert.c” или “Assert.cpp? Вы действительно пишете
main ()
, а неsetup()
и " loop ()"? Если да, то вызываете ли выinit()
в началеmain()
?, @Edgar BonetЯ попробовал код, который вы опубликовали, с минимальной заглушкой для класса
ADS1256
: он работает так, как ожидалось. Таким образом, ваша проблема заключается в какой-то части вашего кода, которую вы не показываете., @Edgar BonetОткуда вызывается assert_debug ()?, @Majenko
@EdgarBonet Он назван assert.cpp (моя ошибка при написании поста, извините). Я действительно вызываю main (), а не setup+loop., @Jurc192
В этом процессе вы изменили свою директиву
#include
?, @timemage@timemage нет, я всегда использую одно и то же:
#include "assert.h"
. Я предполагаю, что он каким-то образом конфликтует с макросом assert (), определенным где-то в цепочке инструментов avr-gcc. Но как я могу это проверить/доказать?, @Jurc192Это в основном то, что я сейчас пишу. Она не закончена, но я думаю, что просто опубликую ее. Я могу попробовать обновить информацию., @timemage