<re_irc>
<@fuse117:matrix.org> can someone suggest a good design for a table of callback functions in embedded rust? im still relatively new to embedded rust, so im not sure the best way to go about it. i have maybe a few dozen callbacks i need to handle, and their function signatures are not all the same.
<re_irc>
1. Wrap them in an enum, which will make the table at least large enough to hold all of the largest signature types
<re_irc>
<@ryan-summers:matrix.org> If their signatures are not all the same, you can either:
<re_irc>
2. Treat them as closures and use dynamic memory allocation
<re_irc>
<@ryan-summers:matrix.org> I.e. if you had them all as closures, you could do something like "pub type FnTable: HashMap<KEY, impl FnOnce>" or similar
<re_irc>
<@fuse117:matrix.org> i have thus far not used dynamic memory allocation in this project. i would prefer to avoid it, but it its not off the table
<re_irc>
<@dirbaio:matrix.org> my advice is don't use callbacks
<re_irc>
<@dirbaio:matrix.org> at all
<re_irc>
<@ryan-summers:matrix.org> Whereas if you wrapped all the possible signature types in an enum, you could do "pub type FnTable = heapless::Map<KEY, FnSignEnum>" or the likes
<re_irc>
<@dirbaio:matrix.org> they work really badly with Rust
<re_irc>
<@dirbaio:matrix.org> the borrow checker hates them
<re_irc>
<@ryan-summers:matrix.org> But yeah, I'd generally avoid callbacks etc
<re_irc>
<@dirbaio:matrix.org> 🤣
<re_irc>
<@ryan-summers:matrix.org> You'll probably have a ton of ownership problems with this if you choose to go this route
<re_irc>
<@ryan-summers:matrix.org> I fought this for minireq a while back
<re_irc>
<@ryan-summers:matrix.org> Actually fuse117 take a look at https://docs.rs/minireq/0.2.0/minireq/, some of the design patterns might be really useful for you
<re_irc>
<@fuse117:matrix.org> maybe callbacks is the wrong word. i have an embedded device that needs to respond, take action, to various commands. historically, i have called these callbacks
<re_irc>
<@ryan-summers:matrix.org> because the use case there is almost identical to what you're looking for, it's a bunch of functions with some data the functions need to act on
<re_irc>
<@ryan-summers:matrix.org> Minireq is literally that, but over MQTT
<re_irc>
<@ryan-summers:matrix.org> I think you solve the ownership issues by moving all of the data used in your handler functions into a context owned by the hash table router
<re_irc>
<@dirbaio:matrix.org> you can make a trait "Handler" with methods for "handle_foo()", "handle_bar()" ... and have the user pass a struct that implements it to the driver
<re_irc>
<@ryan-summers:matrix.org> Eh, that's dynamic memory allocation though
<re_irc>
<@dirbaio:matrix.org> or you can take "&dyn Handler", which wouldn't need it either but compilcates lifetimes for the user
<re_irc>
<@dirbaio:matrix.org> or you can take "Box<dyn Handler>", yes
<re_irc>
<@dirbaio:matrix.org> if it's a single trait with multiple methods, it should be rarer to need multiple of them though?
<re_irc>
<@thejpster:matrix.org> You have plain function pointers in Rust too. That might work if the callback function is able to retrieve its own context from somewhere (a global Mutex<State> perhaps).
<re_irc>
<@explodingwaffle101:matrix.org> : if you only need one, wouldn't it be better to use a generic instead of dynamic dispatch?
<re_irc>
<@dirbaio:matrix.org> yea that's what I said
<re_irc>
<@bugadani:matrix.org> embedded-hal is more a question for components, not for your firmware. You use the hal that's available for your MCU and hope that is compatible with whatever libraries you want to use on top of that. If your MCU's HAL implements embedded-hal, then you have a better chance of having a better time.
<re_irc>
<@mkstn:matrix.org> danielb: Well rp-hal implements both, and I'll have to implement drivers for my peripherals myself anyway..
<re_irc>
<@bugadani:matrix.org> if you want reuse your peripheral drivers, you can implement either. if you want to use async in your reusable driver, then that's pretty much ties you to 1.0. if you don't plan to reuse (why not?), then do whatever is easier/faster to write
<re_irc>
<@almindor:matrix.org> anyone has any idea how to figure out why a (RISCV 32bit) "sw zero 156(sp)" that causes a trap, gets generated in place of some SPI init code when a particular code change that's like 50 lines later gets changed a bit? I have got a 1 liner that if I comment out things work and the same line of code produced a "sw zero 88(sp)" which works ok. The line of code that I uncomment is quite later in the logic and the only thing it...
<re_irc>
... has to do with this SPI init function call is that it uses the SPI bus to draw something (again like 50 lines later :D)
<re_irc>
<@dirbaio:matrix.org> : not afaik. you probably can port stuff from stm32-eth
dc740 has joined #rust-embedded
<re_irc>
<@tschundler:matrix.org> Does anyone have any preferences/opinions/experience regarding compact extensible data structures with decent embedded rust implementations (can be no_std+alloc), eg protocol buffers, flatbuffers, thrift etc. - are there ones that have worked out well, or poorly.
<re_irc>
I'd like something that can be expanded upon with new fields without needing existing devices to be upgraded. I could use JSON, though would prefer binary.
<re_irc>
<@diondokter:matrix.org> : So you can use pretty much any protocol if you add a version number and a migration mechanism.
<re_irc>
Newer fields also don't really make a lot of sense in Rust unless they're options that will be set to None of they're missing in the binary. But that won't give a great API
<re_irc>
<@diondokter:matrix.org> You could of course create your own protocol if you control all endpoints.
dc740 has quit [Remote host closed the connection]