Подтвердить что ты не робот

Преобразовать указатель на функцию С++ на указатель функции c

Я разрабатываю С++-приложение, используя библиотеку C. Мне нужно отправить указатель на функцию в библиотеку C.

Это мой класс:

 class MainWindow : public QMainWindow {  
     Q_OBJECT  
     public:  
     explicit MainWindow(QWidget *parent = 0);  
     private:  
     Ui::MainWindow *ui;
     void f(int*);

 private slots:
     void on_btn_clicked(); 
};

Это моя функция on_btn_clicked:

void MainWindow::on_btn_clicked()
{
    void (MainWindow::* ptfptr) (int*) = &MainWindow::f;

    c_library_function(static_cast<void()(int*)>(ptfptr), NULL);

}

Функция C должна получить указатель на такую ​​функцию: void f (int *). Но код выше не работает, мне не удастся преобразовать мою функцию f member в нужный указатель.

Может ли кто-нибудь помочь?

4b9b3361

Ответ 1

Если я его правильно напомню, доступ к статическим методам класса можно получить с помощью "обычного" указателя C на синтаксис функции. Поэтому постарайтесь сделать его статичным. Указатель на метод класса требует дополнительной информации, такой как "объект" (это), который не имеет смысла для чистого метода C.

Часто задаваемые вопросы здесь имеют хорошее объяснение и возможное (уродливое) решение для вашей проблемы.

Ответ 2

Вы не можете передать нестатический указатель функции-члена как обычный указатель функции. Они не одно и то же, и, вероятно, даже не того же размера.

Однако вы можете (обычно) передать указатель на функцию-член статический через C. Обычно при регистрации обратного вызова в C API вы также можете передать указатель "пользовательские данные", который получает вернулся к вашей зарегистрированной функции. Поэтому вы можете сделать что-то вроде:

class MyClass
{

    void non_static_func(/* args */);

public:
    static void static_func(MyClass *ptr, /* other args */) {
        ptr->non_static_func(/* other args */);
    }
};

Затем зарегистрируйте свой обратный вызов как

c_library_function(MyClass::static_func, this);

то есть. передать указатель экземпляра статическому методу и использовать его как функцию переадресации.

Строго говоря, для полной переносимости вам нужно использовать свободную функцию, объявленную extern "C", а не статический член, чтобы выполнить вашу переадресацию (если это необходимо, как friend), но практически говоря, у меня никогда не было проблем с использованием этот метод связывает код С++ с кодом GObject, который является C обратным вызовом.

Ответ 3

Вы не можете передать указатель на функцию нестатического члена. Вы можете создать статическую или глобальную функцию, которая делает вызов с параметром экземпляра.

Вот пример, который я нахожу полезным, который использует вспомогательный класс с двумя членами: оболочку функции и функцию обратного вызова, вызывающую оболочку.

template <typename T>
struct Callback;

template <typename Ret, typename... Params>
struct Callback<Ret(Params...)> {
    template <typename... Args>
    static Ret callback(Args... args) { return func(args...); }
    static std::function<Ret(Params...)> func;
};

// Initialize the static member.
template <typename Ret, typename... Params>
std::function<Ret(Params...)> Callback<Ret(Params...)>::func;

Используя это, вы можете хранить любые вызываемые, даже нестатические функции-члены (используя std::bind) и конвертировать в c-указатель с помощью функции Callback::callback. Например:

struct Foo {
    void print(int* x) { // Some member function.
        std::cout << *x << std::endl;
    }
};

int main() {
    Foo foo; // Create instance of Foo.

    // Store member function and the instance using std::bind.
    Callback<void(int*)>::func = std::bind(&Foo::print, foo, std::placeholders::_1);

    // Convert callback-function to c-pointer.
    void (*c_func)(int*) = static_cast<decltype(c_func)>(Callback<void(int*)>::callback);

    // Use in any way you wish.
    std::unique_ptr<int> iptr{new int(5)};
    c_func(iptr.get());
}

Ответ 4

@Snps ответ велик. Я расширил его с помощью функции-производителя, которая создает обратный вызов, поскольку я всегда использую обратные вызовы void без параметров:

typedef void (*voidCCallback)();
template<typename T>
voidCCallback makeCCallback(void (T::*method)(),T* r){
  Callback<void()>::func = std::bind(method, r);
  void (*c_function_pointer)() = static_cast<decltype(c_function_pointer)>(Callback<void()>::callback);
  return c_function_pointer;
}

С этого момента вы можете создать свой обычный обратный вызов C из класса или где-либо еще и вызвать член:

voidCCallback callback = makeCCallback(&Foo::print, this);
plainOldCFunction(callback);

Ответ 5

У меня есть идея (не полностью стандартная, поскольку extern "C" отсутствует):

class MainWindow;

static MainWindow* instance;

class MainWindow
{
public:
  MainWindow()
  {
    instance = this;

    registerCallback([](int* arg){instance->...});
  }
};

У вас возникнут проблемы при создании экземпляров MainWindow.

Ответ 6

Короткий ответ: вы можете преобразовать указатель функции-члена в обычный указатель функции C, используя std:: mem_fn.

Это ответ на заданный вопрос, но этот вопрос, похоже, имеет путаную предпосылку, так как спрашивающий ожидает, что C-код сможет вызвать метод экземпляра MainWindow без MainWindow *, что просто невозможно.

Если вы используете mem_fn для приведения MainWindow::on_btn_clicked к указателю функции C, то вы по-прежнему являетесь функцией, которая принимает в качестве первого аргумента MainWindow*.

void (*window_callback)(MainWindow*,int*) = std::mem_fn(&MainWindow::on_btn_clicked);

Это ответ на заданный вопрос, но он не соответствует интерфейсу. Вам нужно будет написать функцию C для переноса вызова на конкретный экземпляр (в конце концов, ваш код API C ничего не знает о MainWindow или каком-либо конкретном экземпляре):

void window_button_click_wrapper(int* arg)
{
    MainWindow::inst()->on_btn_clicked(arg);
}

Это считается анти-шаблоном OO, но поскольку API C ничего не знает о вашем объекте, это единственный способ.

Ответ 7

@Snps ответ прекрасен! Но, как отметил @DXM, он может содержать только один обратный вызов. Я немного улучшил его, теперь он может содержать много обратных вызовов одного типа. Это немного странно, но отлично работает:

 #include <type_traits>

template<typename T>
struct ActualType {
    typedef T type;
};
template<typename T>
struct ActualType<T*> {
    typedef typename ActualType<T>::type type;
};

template<typename T, unsigned int n,typename CallerType>
struct Callback;

template<typename Ret, typename ... Params, unsigned int n,typename CallerType>
struct Callback<Ret(Params...), n,CallerType> {
    typedef Ret (*ret_cb)(Params...);
    template<typename ... Args>
    static Ret callback(Args ... args) {
        func(args...);
    }

    static ret_cb getCallback(std::function<Ret(Params...)> fn) {
        func = fn;
        return static_cast<ret_cb>(Callback<Ret(Params...), n,CallerType>::callback);
    }

    static std::function<Ret(Params...)> func;

};

template<typename Ret, typename ... Params, unsigned int n,typename CallerType>
std::function<Ret(Params...)> Callback<Ret(Params...), n,CallerType>::func;

#define GETCB(ptrtype,callertype) Callback<ActualType<ptrtype>::type,__COUNTER__,callertype>::getCallback

Теперь вы можете просто сделать что-то вроде этого:

typedef void (cb_type)(uint8_t, uint8_t);
class testfunc {
public:
    void test(int x) {
        std::cout << "in testfunc.test " <<x<< std::endl;
    }

    void test1(int x) {
        std::cout << "in testfunc.test1 " <<x<< std::endl;
    }

};

cb_type* f = GETCB(cb_type, testfunc)(std::bind(&testfunc::test, tf, std::placeholders::_2));

cb_type* f1 = GETCB(cb_type, testfunc)(
                std::bind(&testfunc::test1, tf, std::placeholders::_2));


f(5, 4);
f1(5, 7);