gpt4 book ai didi

c++ - 动态创建函数并获取指针

转载 作者:搜寻专家 更新时间:2023-10-31 01:32:05 31 4
gpt4 key购买 nike

我正在使用 Arduino 和电机编码器来跟踪电机的旋转。为此,我在 Arduino 上使用中断。我可以创建一个函数,一个 ISR,只要引脚上的信号发生变化,处理器就会执行该函数。中断/ISR 组合的工作方式如下:

void setup() {
attachInterrupt(1,ISR_function,FALLING);
}

void ISR_function() {
// do something
}

鉴于我有多个带编码器的电机,我决定创建一个类来处理这个问题。但是,attachInterrupt 方法需要一个函数指针,而且我知道在 C++ 中,您不能拥有指向对象实例的方法函数的指针。所以这样的事情是行不通的:

class Encoder {
public:
Encoder(void);
void ISR_function(void);
private:
// Various private members
}

Encoder::Encoder() {
attachInterrupt(1,ISR_function,FALLING);
}

Encoder::ISR_function() {
// Do some interrupt things with private members
}

因为 ISR_function 不是静态的。然而,ISR_function 执行的代码依赖于每个特定实例的私有(private)数据成员。

是否可以动态创建函数?然后检索指向该函数的指针?几乎就像在 javascript 中一样:

class Encoder {
public:
Encoder(void);
void* ISR_function(void);
private:
// Various private members
}

Encoder::Encoder() {
attachInterrupt(1,ISR_function(),FALLING);
}

Encoder::ISR_function() {
return dynamicFunctionPointer;
}

这可能吗?如果没有,如何在不手动创建单独的静态 ISR_functions 的情况下完成我正在尝试做的事情。

最佳答案

// type of an interrupt service routine pointer
using ISR = void(*)();

// a fake version of the environment we are working with
// for testing purposes
namespace fake_environment {
enum bob{FALLING};

ISR isrs[100] = {0};

void attachInterrupt(int i, void(*f)(), bob) {
isrs[i] = f;
}

void runInterrupt(int i) {
isrs[i]();
}
}

// type storing a pointer to member function
// as a compile-time constant
template<class T, void(T::*m)()>
struct pmf {};

// stores a pointer to a class instance
// and a member function. Invokes it
// when called with operator(). Type erases
// stuff down to void pointers.
struct funcoid {
using pfunc = void(*)(void*);
pfunc pf = 0;
void* pv = 0;
void operator()()const { pf(pv); }
template<class T, void(T::*m)()>
funcoid(T* t, pmf<T,m>):
pv(t)
{
// create a lambda, then decay it into a function pointer
// this stateless lambda takes a void* which it casts to a T*
// then invokes the member function m on it.
pf = +[](void* pt) {
(static_cast<T*>(pt)->*m)();
};
}
funcoid()=default;
};

// a global array of interrupts, which have a this pointer
// and a member function pointer type erased:
namespace client {
enum {interrupt_count = 20};
std::array<funcoid, interrupt_count> interrupt_table = {{}};
// with a bit of work, could replace this with a std::vector
}

// some metaprogramming utility code
// this lets me iterate over a set of size_t at compile time
// without writing extra helper functions at point of use.
namespace utility {
template<std::size_t...Is>
auto index_over( std::index_sequence<Is...> ) {
return [](auto&& f)->decltype(auto) {
return f(std::integral_constant<std::size_t, Is>{}...);
};
}
template<std::size_t N>
auto index_upto( std::integral_constant<std::size_t, N> ={} ) {
return index_over( std::make_index_sequence<N>{} );
}
}

// builds an array of interrupt service routines
// that invoke the same-index interrupt_table above.
namespace client {
// in g++, you'd write a helper function taking an `index_sequence`
// and take the code out of that lambda and build the array there:
std::array<ISR, interrupt_count> make_isrs() {
// creates an array of ISRs that invoke the corresponding element in interrupt_table.
// have to do it at compile time, because we are generating 20 different functions
// each one "knows" its index, then storing pointers to them.
// Could be done with a lot of copy-pasta or a macro
return ::utility::index_upto< interrupt_count >()(
[](auto...Is)->std::array<ISR, interrupt_count>{
return {{ []{ interrupt_table[decltype(Is)::value](); }... }};
}
);
}
// isr is a table of `void(*)()`, suitable for use
// by your interrupt API. Each function pointer "knows" its
// index, which it uses to invoke the appropraite `interrupt_table`
// above.
auto isr = make_isrs();
// with a bit of work, could replace this with a std::vector
}

// interrupt is the interrupt number
// index is the index in our private table (0 to 19 inclusive)
// t is the object we want to use
// mf is the member function we call
// kind is FALLING or RISING or the like
// index must be unique, that is your job.
template<class T, void(T::*m)()>
void add_interrupt( int interrupt, int index, T* t, pmf<T, m> mf, fake_environment::bob kind ) {
client::interrupt_table[index] = {t, mf};
fake_environment::attachInterrupt(interrupt,client::isr[index],kind);
}


class Encoder {
public:
Encoder():Encoder(1, 7) {};
Encoder(int interrupt, int index);
void ISR_function(void);
// my choice for some state:
std::string my_name;
};

Encoder::Encoder(int interrupt, int index) {
add_interrupt( interrupt, index, this, pmf<Encoder, &Encoder::ISR_function>{}, fake_environment::FALLING );
}

void Encoder::ISR_function() {
// display state:
std::cout << my_name << "\n";
}

int main() {
Encoder e0;
e0.my_name = "Hello World";
fake_environment::runInterrupt(1);
Encoder e1(0, 10);
e1.my_name = "Goodbye World";
fake_environment::runInterrupt(0);
}

不在 g++ 中编译并使用 C++14。

确实解决了您的问题。 g++ 问题在 make_isrs 中,可以用冗长的复制粘贴初始化来代替。 C++14 来自 index_uptoindex_over,同样可以针对 C++11 进行修改。

Live example .

但是,ISR 应该是最小的;我怀疑您应该只记录消息并在别处处理它,而不是与对象状态交互。

关于c++ - 动态创建函数并获取指针,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44051517/

31 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com