I am writing my own assert() macro on Arduino as shown below.
When I run it from the main() it always works correctly (prints the message and gets into an infinite loop), however when I call it within a class function it gets stuck without printing anything. Why is that?
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);
}
And the context in which the assert gets stuck below (it works well when I call it in main() however):
int ADS1256::assert_debug()
{
uint8_t id = read_register(STATUS) >> 4;
assert(false); // Gets stuck here without printing anything to screen
return id;
}
EDIT: looks like it has something to do with build configuration. I am using PlatformIO build system. It does not work when I use this file structure:
├── include
│ └── assert.h
├── lib
│ ├── ADS1256
│ ├── ads1256.cpp
│ ├── ads1256.h
│ └── ads1256_regmap.h
├── src
├── assert.cpp
└── main.cpp
However, if I move assert.cpp/.h into a dedicated directory within lib/ it works fine. What is the reason behind this? How can I investigate why this happened?
It works with this layout (again, using PlatformIO default build configuration):
├── include
├── lib
│ ├── ADS1256/
│ ├── Assert/
│ ├── assert.cpp
│ └── assert.h
├── src
└── main.cpp
1 Answer 1
As per comment on question, this is not as finished as I'd like, but it's a start.
I deeply suspect what you're running into is a conflict between the name of your header and the standard assert.h
header. You're using the same header name and same macro names as is used in the standard c header, that is part of both c++ and "Arduino.". The standard assert()
with the way things are normally set up on the AVR Arduino core does basically just go into a tight loop and do nothing.
I would consider naming your macro something other than assert
and naming your header something other than assert.h
. If you want to get into the precise details why, we need to see things like your include path order, and the include directives themselves. Even then, the advice would be about the same.
Diagnostics
--save-temps
In platform.io you should be able to add --save-temps
to the avr-gcc
command-line to get it to produce (or keep) the output of the preprocessing stage. GCC annotates these showing where each section of the entire output came from, so you can see which headers each #include
directive actually included and in what order all of this happened.
NDEBUG
Something else you can try, as a diagnostic measure, is to define NDEBUG
in your project settings. This is usually defined by IDEs for "release mode". It basically makes assert()
into a NOP, which in turn means that it will not do the usual avr-libc thing of locking up. If you're seeing the avr-libc assert being expanded instead of your own, then the behaviour will change to just not locking up, but also not doing what your own assert
would do.
AVR-based Arduino assert()
support
avr-libc's assert()
tries to print its assertion message on stderr, which is not configured on AVR based Arduinos to do anything. So the outward behaviour is that it locks up. However, you can complete avr-libc's implementation of stderr in terms of the Arduino's Serial.write
function, in which case the normal avr-libc assert
will work just fine. Here's one example of completing avr-libc's stdio printing in terms of Serial
.
__FILE__
Caveat and SRAM
Something you should be aware of in using __FILE__
, and therefore assert
(whether it be avr-libc's or yours) is that __FILE__
expands to a string literal which is the full path of the source filename, which can be quite long. On the UNO (you have tagged) these go into SRAM. So if you use assert
in a couple different files, you can very quickly eat up the available SRAM on the AVR.
For this reason, in my own little assert-type macros, I've taken to not reporting the file but instead a custom tag, which is often just a single letter or number. Besides, often my asserts are blinking the __LINE__
number on an LED, where __FILE__
is impractical in my typical use case anyway.
-
Thanks for pointing me to avr-libc's implementation of sterr/assert! So we agree on clashing with the standard assert.h header. But I want to understand what and how the problem arises. Is it the file name, is it macro name, both? How could I check which implementation was used (e.g. inspecting binary, symbols or sth)? I tried using
-H
preprocessor flag (shows absolute paths of included headers), but couldn't see anything useful from it...Jurc192– Jurc1922021年06月08日 13:14:08 +00:00Commented Jun 8, 2021 at 13:14 -
Uhh, I will try to make an update to address that.timemage– timemage2021年06月08日 13:23:34 +00:00Commented Jun 8, 2021 at 13:23
-
See under Diagnostics and --save-temps. I'm not (left that out prior) going to be able to go much further with this as an answer really without working out exactly what to ask you to do or include in the question and then having you provide that, which would probably add a lot of noise to the question.timemage– timemage2021年06月08日 13:48:37 +00:00Commented Jun 8, 2021 at 13:48
-
1
-
If that's really the answer you're looking for then you might as well post your own answer calling that one out and I'll get rid of this one. You would need to mark yours as accepted.timemage– timemage2021年06月10日 12:54:00 +00:00Commented Jun 10, 2021 at 12:54
main()
rather thansetup()
andloop()
? If so, do you callinit()
at the start ofmain()
?ADS1256
class: it works as expected. Your problem thus lies in some part of your code that you are not showing.assert_debug()
being called from?#include
directive?