Agreed, none the less the library function terminate() which shouldn't know anything about Teensies prints to USB Serial. I assume this works since _write() is now defined to forward write requests on files 0..2 (stdout/stderr??) to the USB serial port. If the terminate function does this, who knows what other library functions are able now to write to the serial port. While this can be useful, it also might be dangerous. One doesn't know what hardware is connected to a microcontroller.As well, I don't think the C library uses `printf`
#include <exception>
void setup() {
while(!Serial){}
Serial.println("start");
std::terminate();
}
void loop() {
}
start
terminate called without an active exception
std::set_terminate([]() {
Serial.println("Here!");
abort();
});
std::terminate();
int _write(...) {
if (file >= 0 && file <= 1) file = (int)&Serial;
else if (file == 2) return len; // Ignore stderr output
return ((class Print *)file)->write((uint8_t *)ptr, len);
}
2. Just after the includes and before the namespace declaration in inplace_function.h, I add this:
Is this the inplace_function.h header you're using?
https://github.com/WG21-SG14/SG14/blob/master/SG14/inplace_function.h
#define SG14_INPLACE_FUNCTION_THROW(x) std::__throw_bad_function_call()
#include "Arduino.h"
#include "assert.h"
void someInnocentLibraryFunction(int i){
assert(i < 5000);
// do something here which requires i < 5000
}
void setup(){
}
void loop(){
unsigned i = millis();
someInnocentLibraryFunction(i);
}
assertion "i < 5000" failed: file "src/main.cpp", line 8, function: void someInnocentLibraryFunction(int)
#define SG14_INPLACE_FUNCTION_THROW(x) /* allow uninitialized no-op without error */
Ok, looks like inplace_function is the way to go.
I've been testing with this:
Code:#define SG14_INPLACE_FUNCTION_THROW(x) /* allow uninitialized no-op without error */
The idea is libraries can create uninitialized callbacks and not need to store extra info about whether it's actually initialized. The situation is like we have now, where the function pointers are initialized to an empty function, so calling through the pointer is always safe.
[size]
stdex::function<int(int)>: 24
std::function<int(int)>: 64
cxx_function::function<int(int)>: 40
multifunction<int(int)>: 32
boost::function<int(int)>: 40
func::function<int(int)>: 32
generic::delegate<int(int)>: 48
fu2::function<int(int)>: 32
fixed_size_function<int(int)>: 128
embxx_util_StaticFunction: 64
Function_: 56
[function_pointer]
Perf< no_abstraction >: 0.1330217596 [s] {checksum: 0}
Perf< stdex::function<int(int)> >: 0.2122339664 [s] {checksum: 0}
Perf< std::function<int(int)> >: 0.2399393114 [s] {checksum: 0}
Perf< cxx_function::function<int(int)> >: 0.2386651034 [s] {checksum: 0}
Perf< multifunction<int(int)> >: 0.2141137326 [s] {checksum: 0}
Perf< boost::function<int(int)> >: 0.2151225938 [s] {checksum: 0}
Perf< func::function<int(int)> >: 0.2121811386 [s] {checksum: 0}
Perf< generic::delegate<int(int)> >: 0.2123649795 [s] {checksum: 0}
Perf< fu2::function<int(int)> >: 0.2420536333 [s] {checksum: 0}
Perf< fixed_size_function<int(int)> >: 0.2389506757 [s] {checksum: 0}
Perf< embxx_util_StaticFunction >: 0.2130780047 [s] {checksum: 0}
Perf< Function_ >: 0.2130749860 [s] {checksum: 0}
[compile_time_function_pointer]
Perf< no_abstraction >: 0.3972803010 [s] {checksum: 0}
Perf< stdex::function<int(int)> >: 1.5937800175 [s] {checksum: 0}
Perf< std::function<int(int)> >: 1.8513632305 [s] {checksum: 0}
Perf< cxx_function::function<int(int)> >: 1.6328985953 [s] {checksum: 0}
Perf< multifunction<int(int)> >: 1.5960648980 [s] {checksum: 0}
Perf< boost::function<int(int)> >: 1.5989849202 [s] {checksum: 0}
Perf< func::function<int(int)> >: 1.3389178922 [s] {checksum: 0}
Perf< generic::delegate<int(int)> >: 1.6025210662 [s] {checksum: 0}
Perf< fu2::function<int(int)> >: 1.5986084840 [s] {checksum: 0}
Perf< fixed_size_function<int(int)> >: 1.8629920010 [s] {checksum: 0}
Perf< embxx_util_StaticFunction >: 1.8321471718 [s] {checksum: 0}
Perf< Function_ >: 1.8688015562 [s] {checksum: 0}
[compile_time_delegate]
Perf< no_abstraction >: 0.0626496160 [s] {checksum: 0}
Perf< stdex::function<int(int)> >: 0.1589898226 [s] {checksum: 0}
Perf< std::function<int(int)> >: 0.2116471244 [s] {checksum: 0}
Perf< cxx_function::function<int(int)> >: 0.1846861995 [s] {checksum: 0}
Perf< multifunction<int(int)> >: 0.1856941551 [s] {checksum: 0}
Perf< boost::function<int(int)> >: 0.1645116925 [s] {checksum: 0}
Perf< func::function<int(int)> >: 0.1423058787 [s] {checksum: 0}
Perf< generic::delegate<int(int)> >: 0.1793970741 [s] {checksum: 0}
Perf< fu2::function<int(int)> >: 0.2156849841 [s] {checksum: 0}
Perf< fixed_size_function<int(int)> >: 0.2176550105 [s] {checksum: 0}
Perf< embxx_util_StaticFunction >: 0.1899209876 [s] {checksum: 0}
Perf< Function_ >: 0.1872478979 [s] {checksum: 0}
[heavy_functor]
Perf< stdex::function<int(int)> >: 1.5955586561 [s] {checksum: 0}
Perf< std::function<int(int)> >: 1.8375015018 [s] {checksum: 0}
Perf< cxx_function::function<int(int)> >: 1.6050371818 [s] {checksum: 0}
Perf< multifunction<int(int)> >: 1.6027912430 [s] {checksum: 0}
Perf< boost::function<int(int)> >: 1.6006808455 [s] {checksum: 0}
Perf< func::function<int(int)> >: 1.3345585372 [s] {checksum: 0}
Perf< generic::delegate<int(int)> >: 1.6102707624 [s] {checksum: 0}
Perf< fu2::function<int(int)> >: 1.5997166615 [s] {checksum: 0}
Perf< fixed_size_function<int(int)> >: 1.8603795878 [s] {checksum: 0}
Perf< embxx_util_StaticFunction >: 1.8277974767 [s] {checksum: 0}
Perf< Function_ >: 1.8649366700 [s] {checksum: 0}
[non_assignable]
Perf< stdex::function<int(int)> >: 0.1590927615 [s] {checksum: 0}
Perf< std::function<int(int)> >: 0.2110050395 [s] {checksum: 0}
Perf< cxx_function::function<int(int)> >: 0.1838614811 [s] {checksum: 0}
Perf< multifunction<int(int)> >: 0.1871413366 [s] {checksum: 0}
Perf< boost::function<int(int)> >: 0.1631176407 [s] {checksum: 0}
Perf< func::function<int(int)> >: 0.1413607129 [s] {checksum: 0}
Perf< generic::delegate<int(int)> >: 0.2028013256 [s] {checksum: 0}
Perf< fu2::function<int(int)> >: 0.2168885537 [s] {checksum: 0}
Perf< fixed_size_function<int(int)> >: 0.2178153053 [s] {checksum: 0}
Perf< embxx_util_StaticFunction >: 0.1893721816 [s] {checksum: 0}
Perf< Function_ >: 0.1874921136 [s] {checksum: 0}
[lambda_capture]
Perf< stdex::function<int(int)> >: 1.5930697092 [s] {checksum: 0}
Perf< std::function<int(int)> >: 1.9196068641 [s] {checksum: 0}
Perf< cxx_function::function<int(int)> >: 1.8563353887 [s] {checksum: 0}
Perf< multifunction<int(int)> >: 1.6529472210 [s] {checksum: 0}
Perf< boost::function<int(int)> >: 1.6056910397 [s] {checksum: 0}
Perf< func::function<int(int)> >: 1.5992049860 [s] {checksum: 0}
Perf< generic::delegate<int(int)> >: 1.8260656285 [s] {checksum: 0}
Perf< fu2::function<int(int)> >: 1.8579377332 [s] {checksum: 0}
Perf< fixed_size_function<int(int)> >: 2.0478922288 [s] {checksum: 0}
Perf< embxx_util_StaticFunction >: 1.8469540663 [s] {checksum: 0}
Perf< Function_ >: 1.7864459408 [s] {checksum: 0}
[stateless_lambda]
Perf< stdex::function<int(int)> >: 1.5926567463 [s] {checksum: 0}
Perf< std::function<int(int)> >: 1.8454776031 [s] {checksum: 0}
Perf< cxx_function::function<int(int)> >: 1.6209733850 [s] {checksum: 0}
Perf< multifunction<int(int)> >: 1.6152552974 [s] {checksum: 0}
Perf< boost::function<int(int)> >: 1.6308521949 [s] {checksum: 0}
Perf< func::function<int(int)> >: 1.3416845627 [s] {checksum: 0}
Perf< generic::delegate<int(int)> >: 1.6141353468 [s] {checksum: 0}
Perf< fu2::function<int(int)> >: 1.6046740280 [s] {checksum: 0}
Perf< fixed_size_function<int(int)> >: 1.8767809781 [s] {checksum: 0}
Perf< embxx_util_StaticFunction >: 1.8332145964 [s] {checksum: 0}
Perf< Function_ >: 1.8790269169 [s] {checksum: 0}
Ok, I've committed inplace_function and usage in IntervalTimer to the core library.
https://github.com/PaulStoffregen/cores/commit/a9d7ece998701d44bc498525afa659422b2d87a2
https://github.com/PaulStoffregen/cores/commit/673a4c0aac64a98bf88f6590dfbc16da81bfb0c6
More usage to come, but hopefully this gives a good starting point.
Can someone explain in two sentences:
Apart from replacing "void (*funct)()" by "callback funct" is there any other advantage, and what is different to "typedef void (*callback)() ;" that I used in the past?
and why should this be lightweight?
Would love to see example of expressions in use
#include "Arduino.h"
IntervalTimer t[4];
void callback(const char* text, int i) // Callbacks with some input parameters
{
Serial.printf("%s %d\n",text, i);
}
void setup()
{
const char text[12] = "someText";
for (int i = 1; i < 4; i++)
{
t[i].begin([i, text] { callback(text,i); }, i * 100'000); // setup all timers at once, capture the parameter values for later use (the capturing is necessary since"i" and "text" are local variables)
}
}
void loop()
{
}
Yes, I this might be a good idea. Probably the wiki would be a good idea to collect those examples?
...
So it stores known static values allowing the same shared _isr() code to be entered and present known unique behavior? Prior posts have seemed to use 'live' variables?
[&i, &text](){...}
Yes, that's true. As it is set now you can capture up to 16bytes. If you increase the size of the text variable to 13 you'll get a compilier error since together with the 4 bytes for "i" wont fit in the prealocated space. This limitation ist the main difference to the usual std::function which would allocate the required space dynamically and would have no such limit (but of course the disadvantage of dynamically allocating)Doing 5 pin interrupts to a single _isr() wouldn't have to guess/calculate what pin caused it to be called. The same single copy of the _isr() code would be executed - but would be 'passed'/provided identifying values. The added RAM use of interest is just saving the passed data and not any duplicated code?
Yes, the same happens with "i"On creation it uses "//... capture the parameter" to save those 'identifier values' - so the example above the "char text[12]" could be edited between each iteration of the for() and that unique 'string' would be captured before the local is destroyed?
It doesn't help eliminate 'volatile globals' for shared data if they are captured/static?
If there was some shared data a pointer (to struct or anything) could be passed?
BTW: Your questions are more related to lambda expressions. Using lambdas as callbacks is just one of the possibilities you now have to attach callbacks. Anyway,
...
and why should this be lightweight?
is there any other advantage