1

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
7
  • Is your source named “Assert.c” or “Assert.cpp”? Are you really writing main() rather than setup() and loop()? If so, do you call init() at the start of main()? Jun 6, 2021 at 9:24
  • 1
    I tried the code you posted, with a minimal stub for the ADS1256 class: it works as expected. Your problem thus lies in some part of your code that you are not showing. Jun 6, 2021 at 9:35
  • 1
    Where is assert_debug() being called from?
    – Majenko
    Jun 6, 2021 at 11:49
  • @EdgarBonet It is named assert.cpp (my mistake when writing the post, sorry). I am indeed calling main() rather than setup+loop.
    – Jurc192
    Jun 8, 2021 at 12:01
  • In that process did you change your #include directive?
    – timemage
    Jun 8, 2021 at 12:35

1 Answer 1

0

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.

5
  • 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
    Jun 8, 2021 at 13:14
  • Uhh, I will try to make an update to address that.
    – timemage
    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
    Jun 8, 2021 at 13:48
  • 1
    I asked the same question in the PlatformIO community and clashing with avr-libc's assert macro is indeed the problem.
    – Jurc192
    Jun 10, 2021 at 12:15
  • 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
    Jun 10, 2021 at 12:54

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.