将类方法作为无效函数指针(C ++ 11)

时间:2018-11-15 00:25:49

标签: c++ c++11

我有一个对象,该对象需要与现有的C api进行接口才能注册in中断(void函数不带参数)。我可以将中断附加到函数function()上。但是,我希望能够将参数传递给函数,但这将更改函数签名。我想一种解决方法是创建一个对象来存储参数(并根据需要修改它们),然后传入一个方法(或类似方法)。但是,我还无法弄清楚该怎么做。

我尝试将lambda作为[=](){ std::cout << "a: " << a << "\n"; }传递,但事实证明带有捕获的lambda无法转换为函数指针。我也尝试了模板化方法(因为它会在编译时实例化),但无法使其正常工作。我曾经在SO上看到过一些关于std::bindstd::function的帖子,但是他们经常警告虚函数开销,我想避免在用于ISR的嵌入式平台上使用它们。

将参数化的函数转换为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;
}

编辑:我的解决方案受到所选答案的启发:

#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;
}

2 个答案:

答案 0 :(得分:2)

因此,您想为一个不同的中断注册一个相同的中断处理程序,每个中断具有相等但独立的数据...

具有静态数据的独立模板功能如何?

template <unsigned int IRQ>
void irqHandler()
{
    static A a(IRQ);
    a.doSomething();
};

void(*interruptVectorTable[12])() =
{
   // ...
   &irqHandler<7>,
   // ...
   &irqHandler<10>,
};

答案 1 :(得分:0)

这是一种复杂的方法。它需要一些样板代码,所以我将其包装在几个MACROS(糟糕的)中。对于C++11,锁定受到一定程度的限制(读取效率较低),但是如果您可以访问C++14或更高版本,则可以进行改进:

// ## 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...
}

关于是否拥有C++14的说明,您可以在此处用更有效的功能替换互斥体和锁定代码:

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()); }