Absolute minimal requirements to support C++ exceptions in avr-gcc project

Go To Last Post
11 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello all! Long time, no chat.

 

To begin with, I must say that I am aware that C++ support in avr-gcc is extremely limited, particularly in terms of what historically would have been called the "standard template library".

 

My team has managed to ship a couple of XMega projects using a carefully curated limited subset of C++ -- for example, avoiding anything that would touch the heap, etc.

We have created a few portable class libraries which are shared among several different embedded targets including AVRs, and so far we've stayed clear of exceptions. But we are repeatedly running into brick walls where we end up creating really twisted code to gracefully deal with rare error conditions, which would be SO MUCH more straightforward if we could just use try/throw/catch.

 

My initial impression is that the missing link in proper exception support in avr-gcc is primarily a library problem, rather than a limitation within the compiler itself. But the linker errors are really not enough to go on, in terms of figuring out what functions need to be added and how they need to operate. I suspect it would require some insider knowledge about architectural and implementation details within GCC and its AVR backend.

 

Has anyone else previously investigated this? Any pointers about the best way forward if I wanted to try to get it working?

 

Regards,

Luke

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

lfmorrison wrote:
Any pointers about the best way forward if I wanted to try to get it working?
Quick to implement, complete, neither, or unknown?

AVR-Ada has a minimal exception handler (last chance) that's equivalent to C setjmp/longjmp.

So, restart main or even all the way back to a .init function.

 

4.2.6. Exceptions and the Last Chance Handler - ZFP and Ravenscar SFP | 4. The Predefined Profiles — GNAT User's Guide Supplement for Cross Platforms 22.0w documentation

C Programming/Error handling - Wikibooks, open books for an open world (bottom)

 

"Dare to be naïve." - Buckminster Fuller

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

gchapman wrote:

Quick to implement, complete, neither, or unknown?

I guess I should have been more clear:

My goal would be to fill in the missing platform-specific pieces to allow normal c++ try/throw/catch blocks to compile and link under avr-g++, so that my cross-platform class libraries can assume correct (and ideally, but not necessarily, relatively fast-performing) behaviour on the AVR target.

 

I specifically DO NOT care about std::exception objects; they would almost certainly pull in heap dependencies which I am not interested in.

Last Edited: Thu. Aug 26, 2021 - 04:40 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

lfmorrison wrote:
limited subset of C++ -- for example, avoiding anything that would touch the heap, etc.
That's silly.  Using the heap is as easy as falling out of bed.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

steve17 wrote:

lfmorrison wrote:
limited subset of C++ -- for example, avoiding anything that would touch the heap, etc.
That's silly.  Using the heap is as easy as falling out of bed.

It is absolutely easy to use the heap. I even have the stubs functions in place to make the C++ new and delete operators work correctly -- if I wanted to do so.

But I explicitly don't want to do it.

 

(edit: Ironically, my version of the C++ new operator explicitly violates the relevant standards because of the very issue I am raising right now: I cannot throw a std::bad_alloc because try/throw/catch is unimplemented. I am fine with that deviation because, as I keep on saying, up until now I have not actually used the heap at all, so the violation quite simply cannot come back to bite me.)

 

And, until proven otherwise, quite frankly it's irrelevant to the central thrust of my question.

(edit: Based on a follow-up answer, it is possible -- depending on GCC internals -- that the compiler's internal expectations about exceptions might inextricably make the heap a mandatory part of the throwing process. But without that extra GCC insider knowledge, I still cannot actually answer that.)

Last Edited: Thu. Aug 26, 2021 - 06:49 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

I'd recommend reading Herb Sutter's "Zero-overhead deterministic exceptions: Throwing values" paper (especially section 2.5 "Root causes: Why we can’t just use exceptions everywhere today") before you go down the route of using exceptions for any embedded project, regardless of what microcontroller is being targeted.

build-avr-gcc: avr-gcc build script

toolchain-avr-gcc: CMake toolchain for cross compiling for the AVR family of microcontrollers

avr-libcpp: C++ standard library partial implementation (C++17 only) for use with avr-gcc/avr-libc

picolibrary: C++ microcontroller driver/utility library targeted for use with resource constrained microcontrollers

picolibrary-microchip-megaavr: picolibrary HIL for megaAVR microcontrollers

picolibrary-microchip-megaavr0: picolibrary HIL for megaAVR 0-series microcontrollers

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1


lfmorrison wrote:
if we could just use try/throw/catch.

 

FF = PI > S.E.T

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

steve17 wrote:
Using the heap is as easy as falling out of bed.

I find that falling out of bed is best avoided ....

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

lfmorrison wrote:

steve17 wrote:

 

 

lfmorrison wrote:

limited subset of C++ -- for example, avoiding anything that would touch the heap, etc.

 

That's silly.  Using the heap is as easy as falling out of bed.

It is absolutely easy to use the heap. I even have the stubs functions in place to make the C++ new and delete operators work correctly

No.  Using delete in embedded software is usually avoided.   I simply new my classes at startup and don't delete them.  I can use a command from the PC to request the heap/stack situation on the microcontroller.  I always have more than enough RAM.

 

If you want to use a chip with a small amount of RAM,  it's easy to have the heap allocator check for low free RAM.  Then decide how to report it.  Maybe light a LED.  It could also return a null pointer but you would need to check it.

 

I don't think I've ever used exceptions.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

steve17 wrote:

lfmorrison wrote:

steve17 wrote:

 

 

lfmorrison wrote:

limited subset of C++ -- for example, avoiding anything that would touch the heap, etc.

 

That's silly.  Using the heap is as easy as falling out of bed.

It is absolutely easy to use the heap. I even have the stubs functions in place to make the C++ new and delete operators work correctly

No.  Using delete in embedded software is usually avoided.   I simply new my classes at startup and don't delete them.  I can use a command from the PC to request the heap/stack situation on the microcontroller.  I always have more than enough RAM.

 

If you want to use a chip with a small amount of RAM,  it's easy to have the heap allocator check for low free RAM.  Then decide how to report it.  Maybe light a LED.  It could also return a null pointer but you would need to check it.

 

I don't think I've ever used exceptions.

 

I've just been fortunate, I suppose, that my classes are just as easily constructed either statically as globals at compile time, or else dynamically on the stack as needed. And in either case there is marginally (about the size of 2 pointers) less RAM utilization versus creating them on the heap.

 

If I don't need the heap, then why bother introducing it?

(As for reporting a failure in new: According to the c++ standard, that's one place where throwing exceptions is *supposed* to come into play...)

 

No matter, I am still hitting a brick wall on the exception thing. And I have seen one response with a big red flag telling me that I ought to rethink it. I think I'll just abandon the idea.

 

(edit: But honestly, I really don't want to turn this into a flame war about the heap. That wasn't my intention from the outset and I fully respect that you have a different style than me and it appears that both styles have suited our respective needs successfully.)

Last Edited: Thu. Aug 26, 2021 - 08:25 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

lfmorrison wrote:

No matter, I am still hitting a brick wall on the exception thing. And I have seen one response with a big red flag telling me that I ought to rethink it. I think I'll just abandon the idea.

 

Unless Herb's proposal (or something similar) ends up being adopted by the standard, the only viable approach for embedded projects is to standardize the way a code base reports and propagates errors. The approach I've adopted is to have any function that may fail and need to report error information to the caller return a result type that either contains the result of a successful operation or an error code. This results in APIs that aren't as clean as exception based ones would be, but I don't have to figure out how to get exceptions working on AVRs, and, more importantly, it avoids the unbound exception handling execution time covered in Herb's paper. If you want to take a look at my approach, it can be found in https://github.com/apcountryman/picolibraryhttps://github.com/apcountryman/picolibrary/blob/878e9f3232ebcc9ca1dc95e918a1205a020b1793/include/picolibrary/i2c.h#L859 provides a good example of what using the result type looks like.

build-avr-gcc: avr-gcc build script

toolchain-avr-gcc: CMake toolchain for cross compiling for the AVR family of microcontrollers

avr-libcpp: C++ standard library partial implementation (C++17 only) for use with avr-gcc/avr-libc

picolibrary: C++ microcontroller driver/utility library targeted for use with resource constrained microcontrollers

picolibrary-microchip-megaavr: picolibrary HIL for megaAVR microcontrollers

picolibrary-microchip-megaavr0: picolibrary HIL for megaAVR 0-series microcontrollers