如何在运行时生成函数?

4

我正在尝试使用C++编写中断服务程序,以下是一些代码片段:

void handlerProxy(int intrNo) {}

typedef void(*IntrHandler)();

IntrHandler IDT[256];

我希望能像这样在运行时或编译时初始化IDT:

for (size_t i = 0; i < 256; ++i) {
    // It doesn't compile
    IDT[i] = std::bind(handlerProxy, i);
    // or
    IDT[i] = [i] () {handlerProxy(i);};
}

问题是

  • 带有捕获的lambda函数无法转换为函数指针
  • 我的代码将使用-fno-rtti编译,因此std::function::target不可用

是否有可能我能够完成这个任务? 我不想手动编写IDT[0]= ... IDT[1]=...或使用其他程序来生成它。允许使用宏和内联汇编。可以更改IDT的类型,但是IDT的元素应该是函数地址,这意味着类似于jmp IDT[0]的内容应该是有效的。


IDT[]的类型定义为一个带有函数调用运算符重载(函数对象)的结构体是否可行? - Galik
在这种情况下,jmp IDT[0] 将不起作用。BIOS 只需 push 寄存器和标志,然后 jmp 到地址来调用这些函数。 - Shangtong Zhang
2个回答

5
您可以将 `intrNo` 设为模板参数,方法如下:
template <int intrNo>
void handlerProxy() {}

typedef void(*IntrHandler)();

并使用打包扩展初始化数组:

template <typename IS>
struct helper;

template <size_t ... Is>
struct helper<std::index_sequence<Is...>> {
  static constexpr std::array<IntrHandler, sizeof...(Is)> make_handlers() {
    return {{ &handler_proxy<Is> ... }};
  }
};

constexpr std::array<IntrHandler, 256> IntrHandlers = helper<std::make_index_sequence<256>>::make_handlers();

IntrHandler * IDT = IntrHandlers.data();

(顾客自负,代码未经测试)


它可以工作,尽管"IntrHandlers.data()"不能直接赋值给"IntrHandler *",只需要进行一些小修改即可。 - Shangtong Zhang

1

Chris的回答更加简洁,但是这也可以用元组来实现,将handlerProxy定义为一个带有静态成员的模板函数对象:

template <int i>
    struct handlerProxy{
    static int func(){return i * i;}
};

然后创建指向函数的元组的功能:

template <size_t... I>
decltype(auto) functions(std::index_sequence<I...>) {
         return return std::make_tuple(&handlerProxy<I>::func...);
}
template <size_t size>
decltype(auto) generate_functions() {                                                      
   using Indices = std::make_index_sequence<size>;
   return functions(Indices{});
}

使用:
int main()
{
    //generate tuple of functions
    auto IDT = generate_functions<256>();
    //call 2th function
    std::cout << std::get<2>(IDT)();
}

注意:在使用gcc编译时,应将-ftemplate-depth选项设置为较大的数字,例如50000。

我认为这不可能实现。请注意,在我的问题中,我需要像 jmp IDT[0] 这样的东西是有效的。IDT 中的每个元素都必须是一个 32 位地址。虽然 std::get<2>(IDT)() 可以工作,但是它是 BIOS 调用这些函数。BIOS 不知道 operator (),只会跳转到地址而不传递任何参数。 - Shangtong Zhang
也许我可以使用地址&handlerProxy<0>::operator()&handlerProxy<1>::operator()等来填充IDT数组,但我不确定在汇编中简单的jmp到这个地址是否总是能正确调用函数。 - Shangtong Zhang
我的意思是成员函数总是有一个隐藏参数“this”,但BIOS不会传递这个参数,所以我不确定它是否会产生一些副作用。 - Shangtong Zhang
@Slardar 张,我已经编辑了代码。请使用静态成员函数而不是 operator()。 - rahnema1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接