berry
berry copied to clipboard
Don't use setjmp/longjmp for exceptions
In C++, setjmp/longjmp are not compatible with C++ exceptions. Therefore, I want to change the berry exception to error code mechanism. I think it should be possible to use the int
return value of the native-function to be compatible with the existing code as much as possible.
Simply put, we may need to provide some macros:
#define be_call(vm, argc) for (int errcode = be_call_(vm, argc); errcode;) return errcode;
#define be_raise(vm, except, msg) do { be_raise_(vm, except, msg); return BE_EXCEPT_RAISED; } while (0)
In short, when an exception occurs, the function will always return along the call stack. Due to the use of return
semantics, this will be compatible with C++ exceptions.
Will this work without C++ exception support? Tasmota uses C++ but disables C++ exceptions completely (for code size reasons)
Will this work without C++ exception support? Tasmota uses C++ but disables C++ exceptions completely (for code size reasons)
Yes, we generally disable C++ exceptions in the MCU. Unfortunately, it is impossible to disable C++ exceptions anywhere ☹️.
setjmp/longjmp will destroy the C++ stack unwinding mechanism. Let me give an example:
struct Test {
char *data;
Test() { data = new char[10](); }
~Test() { delete[] data; }
};
int berry_func(bvm *vm) {
Test obj;
// do somthing ...
// if be_raise is called, longjmp will be called to pop the stack
// directly instead of Temp::~Temp(). So that obj cannot be destructed.
}
I understand but I'm still nervous on this change. When running C code called by Berry, it is very handy to call be_raise() deep in the C code when something goes wrong and longjmp
does its magic. With the new scheme, I need to change the entire call chain to get back the error code from call to call.
Would there be any way to still enable longjmp when Berry calls C code for which I'm certain that there are no C++ exceptions nor cleaning code needed?
This is the main problem I'm worrying about now.
Would there be any way to still enable longjmp when Berry calls C code for which I'm certain that there are no C++ exceptions nor cleaning code needed?
There's nothing that prevents you calling setjmp/longjmp from a C only chain. The only requirement is making sure there's no C++ with exceptions involved in the chain, or if they are, that the C++ code does not leak exceptions up. So the easiest solution is to add 2 macros:
#define EnterCPPCode try {
#define LeaveCPPCode } catch (const std::exception & e) { be_raise(vm, CPP_CODE_EXCEPT, e.what()); } catch (...) { be_raise(vm, CPP_CODE_EXCEPT, "unknown C++ exception"); }
As long as you don't mix the C++ and C code in the same call chain, it'll work.
Thanks, I love this approach. It should be obviously added in the documentation.
As mentioned, C++ exceptions are often disabled in embedded systems, so we're safe here. The danger would be in non-embedded systems.
Well, avoiding C++ exceptions in Berry should be a very viable approach. And I found that people don't use C++ exceptions very much.