I have an object which needs to interface with an existing C api to register an in interrupt (void function taking no arguments). I can attach the interrupt to the function function()
. However, I want to be able to pass in arguments to the function, but that would change the function signature. I thought a way around that would be to create an object to store the parameters (and modify them as necessary), and then pass in a method (or similar). However, I haven't been able to figure out how to do that.
I've tried passing in a lambda as [=](){ std::cout << "a: " << a << "\\n"; }
[=](){ std::cout << "a: " << a << "\\n"; }
, but it turns out lambdas with a capture can't be converted to function pointers. I've also tried a templated method (since it would get instantiated at compile time), but couldn't get it to work. I've seen some posts on SO talking about std::bind
and std::function
, but they often warn about virtual function overhead, which I'd like to avoid on an embedded platform for an ISR.
What is the best way to convert a paramterized function to a void(*)()
?
#include <iostream>
void function() {
std::cout << "Hello World!\n";
}
void attach_interrupt(void(*fn)()) {
fn();
}
class A {
int a;
public:
A(int a) : a(a) {
attach_interrupt(function); // This works as expected
// attach_interrupt(method); // How do I make this work?
// attach_interrupt(method2<a>);
}
void method() {
// something requiring a and b
std::cout << "a: " << a << "\n";
}
template<int a>
void method2() {
std::cout << "a: " << a << "\n";
}
};
int main()
{
const int PIN_1 = 0;
const int PIN_2 = 1;
const int PIN_3 = 2;
A foo(PIN_1);
A bar(PIN_2);
A baz(PIN_3);
return 0;
}
EDIT: My solution, inspired by the selected answer:
#include <iostream>
void attach_interrupt(int pin, void(*fn)()) {
fn();
}
// Free function, which works as an ISR
template <unsigned int IRQ, unsigned int IRQ2>
static void irqHandler()
{
std::cout << "IRQ: " << IRQ << "\n";
std::cout << "IRQ2: " << IRQ2 << "\n";
};
template <unsigned int PIN_1, unsigned int PIN_2>
class Handler {
private:
public:
Handler() {
void(*irq)() = &irqHandler<PIN_1, PIN_2>;
attach_interrupt(0, irq);
attach_interrupt(0, &handler_2);
}
// static member function can have its address taken, also works as ISR
static void handler_2() {
std::cout << "PIN_1: " << PIN_1 << "\n";
std::cout << "PIN_2: " << PIN_2 << "\n";
}
};
Handler<1, 2> a;
Handler<2, 3> b;
int main()
{
return 0;
}
So you want to register one and the same interrupt handler for different interrupts, each having equal, but individual data...
What about a free-standing template function with static data?
template <unsigned int IRQ>
void irqHandler()
{
static A a(IRQ);
a.doSomething();
};
void(*interruptVectorTable[12])() =
{
// ...
&irqHandler<7>,
// ...
&irqHandler<10>,
};
Well here is a convoluted way to do this. It requires some boilerplate code so I wrapped that up in a couple of MACROS (yuck). For C++11
the locking is somewhat limited (read less efficient) but that can be improved upon if you have access to C++14
or above:
// ## Header Library Code
namespace static_dispatch {
inline std::mutex& mutex()
{ static std::mutex mtx; return mtx; }
inline std::lock_guard<std::mutex> lock_for_reading()
{ return std::lock_guard<std::mutex>(mutex()); }
inline std::lock_guard<std::mutex> lock_for_updates()
{ return std::lock_guard<std::mutex>(mutex()); }
inline std::vector<void*>& cbdb()
{
static std::vector<void*> vps;
return vps;
}
inline void register_cb(void(*cb)(), void* user_data)
{
auto lock = lock_for_updates();
cbdb().push_back(user_data);
cb(); // assign id under lock
}
inline void* retreive_cb(std::size_t id)
{
auto lock = lock_for_reading();
return cbdb()[id];
}
} // namespace static_dispatch
#define CALLBACK_BOILERPLATE(id) \
static auto id = std::size_t(-1); \
if(id == std::size_t(-1)) { id = static_dispatch::cbdb().size() - 1; return; }
#define CALLBACK_RETREIVE_DATA(id, T) \
reinterpret_cast<T*>(static_dispatch::retreive_cb(id))
// ## Application Code
class A
{
public:
void member_callback_1() const
{
std::cout << s << '\n';
}
private:
std::string s = "hello";
};
void callback_1()
{
CALLBACK_BOILERPLATE(id);
auto a = CALLBACK_RETREIVE_DATA(id, A);
a->member_callback_1();
}
// The framework that you need to register your
// callbacks with
void framework_register(void(*cb)()) { cb(); }
int main()
{
A a;
// register callback with data structure
static_dispatch::register_cb(&callback_1, &a);
// Now register callback with framework because subsequent calls
// will invoke the real callback.
framework_register(&callback_1);
// etc...
}
As noted about if you have C++14
you can replace the mutex and locking code with the more efficient functions here:
inline std::shared_timed_mutex& mutex()
{ static std::shared_timed_mutex mtx; return mtx; }
inline std::shared_lock<std::shared_timed_mutex> lock_for_reading()
{ return std::shared_lock<std::shared_timed_mutex>(mutex()); }
inline std::unique_lock<std::shared_timed_mutex> lock_for_updates()
{ return std::unique_lock<std::shared_timed_mutex>(mutex()); }
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.