Open taliesin opened 6 years ago
If your ShoudEstimateAndSend::action_function
is static void
, this should work.
Using a template event class 'breaks' inheritance and would need a template react function as far as I can see?!
Not sure what you mean here. Do you have an example to clarify?
Just thinking too complicated .. a plain old C function pointer works pretty well.
struct ShouldEstimateAndSend : public tinyfsm::Event { typedef void (*action_function_t)(void); action_function_t _action_function; explicit ShouldEstimateAndSend(action_function_t af) : _action_function(af) {} };
... and you can even pass lambdas and bind arguments there, which extends the usability dramatically.
FSM::dispatch(ShouldEstimateAndSend(ts, []() { printf("lambda called\n"); } ));
... and default it to a do nothing:
explicit ShouldEstimateAndSend(action_function_t af = [](){}) : _action_function(af) {}
In the end the problem was just how to store the function in the event class and I did not want std::function for the given reasons.
Thanks for pulling me back to earth.
Ah damn, binding does not work, see https://stackoverflow.com/questions/28746744/passing-lambda-as-function-pointer
Yes, I think that's one reason why std::function
exists (I never really used it though, remember playing around with it when testing these TinyFSM action functions).
For the record: example usage of lamda function: elevator.cpp
Yes I did see that example, but still had the trouble to pass it via the event. There are some nifty workarounds for std::function, not as complete, but for a void() function it would be easy enough, probably I'll go that way if I really need to.
Finally, to close that topic, my solution:
// ActionFunctions may be passed via Events to be used for transit<state>(action_function)
// It allows to pass capturing lambdas as it stores context locally (other than a normal
// C style function pointer).
// This is mainly to avoid std::function which pulls in exception handling.
class ActionFunction
{
public:
using action_function_t = void (*)(void *);
private:
action_function_t _action_function;
void *_context;
public:
ActionFunction(action_function_t f, void *ctx) :
_action_function(f), _context(ctx) {}
void operator()() const { _action_function(_context); }
// helper to instantiate a calling instance for each lambda passed
template<typename T> static void caller(T* v) { return (*v)(); }
};
template <typename Func>
ActionFunction make_action_function(Func f)
{
return ActionFunction(reinterpret_cast<ActionFunction::action_function_t>(ActionFunction::caller<Func>), &f);
}
And the event:
struct ShouldEstimateAndSend : public tinyfsm::Event
{
ActionFunction _action;
explicit ShouldEstimateAndSend(ActionFunction af = make_action_function([](){})) : _action(af) {}
const ActionFunction & action() const { return _action; }
};
Usage (binding the stream for ChibiOS as example):
auto af = make_action_function([chp] () { chprintf(chp, "capturing lambda called\n"); });
AnchorFSM::dispatch(ShouldEstimateAndSend(af));
in transit
transit<SomeState>(passed_event.action());
This has some potential to be more type-safe and probably shorter ...
Probably this is more a general C++ question than it is a tinyfsm one, but maybe somebody did this already.
I have an event called ShouldEstimateAndSend which transits to a new state on a few conditions. I'd like to have it call transit\<NewState>(ShoudEstimateAndSend::action_function). Using std::function would work, but this introduces some runtime overhead and pulls in exception handling which is not what I like on my embedded target. Using a template event class 'breaks' inheritance and would need a template react function as far as I can see?!
Can't wiggle my head around this.