如何使用C++函数指针作为指向 C 函数指针/回调的参数

How to use C++ Function Pointer as Parameter to a C Funcion Pointer/Callback

本文关键字:指针 函数 回调 参数 何使用 C++      更新时间:2023-10-16

我知道C++lambdas或C函数指针,但我不知道如何将它们混合在一起。我已经找到了 将C++实例方法与 C 回调函数混合在一起,但它不依赖于我的需求。

我有一个包含 C 标头的C++类,如下所示:

extern "C" {
#include <jack/jack.h>
#include <jack/types.h`enter code here`>
#include <jack/ringbuffer.h>
#include <jack/midiport.h>
}
#include <QDialog>
#include <QFile>
#include <QString>
#include "Cliente.h"
class FormQStudio : public QDialog
{
Q_OBJECT
public:
explicit FormQStudio(QWidget *parent = nullptr);
~FormQStudio();
void playMIDIFile(QString file) noexcept(false);
int theCallback(jack_nframes_t nframes, void *arg);
};

C 标头定义了一些类型:

typedef uint32_t         jack_nframes_t;
typedef int  (*JackProcessCallback)(jack_nframes_t nframes, void *arg);
int jack_set_process_callback (jack_client_t *client,
JackProcessCallback process_callback,
void *arg) JACK_OPTIONAL_WEAK_EXPORT;

我的C++类的实现需要将theCallback(jack_nframes_t nframes, void *arg)作为参数传递给C函数jack_set_process_callback。但我不知道该怎么做。

#include "FormQStudio.h"
FormQStudio::FormQStudio(QWidget *parent) :
QDialog(parent),
ui(new Ui::FormQStudio)
{
ui->setupUi(this);
}
void FormQStudio::playMIDIFile(QString file) noexcept(false){
smf_t *smf = smf_load(file.toUtf8());
smf_event_t *event;
Cliente *c = new Cliente("QStudio");
c->inicializarCliente( 
/* HOW TO REFERENCE this->processJackCallback as a parameter? */
);
}
};

类"客户"是这样的:

extern "C" {
#include <jack/jack.h>
#include <jack/types.h>
#include <jack/ringbuffer.h>
#include <jack/midiport.h>
}
#include <QString>
#include <QMap>
#include "ClientePorta.h"
#include "QStageException.h"
class Cliente
{
public:
Cliente(QString clientName);
struct MidiMessage {
jack_nframes_t  time;
int     len;    /* Length of MIDI message, in bytes. */
unsigned char   data[3];
};

jack_client_t *getClient();
void conectar(QString portaOrigem, QString clienteDestino, QString portaDestino) noexcept(false);

void inicializarCliente(JackProcessCallback callback) noexcept(false);
void addClientePorta(ClientePorta * cp);
ClientePorta* getClientePorta(QString nomePorta);
void sendMIDI(QString outputPortName, jack_nframes_t nframes ) noexcept(false);
private:
QString clientName;
QString inputPortName;
QString outputPortName;
QMap<QString,ClientePorta*> portas;
jack_client_t   *jackClient = NULL;
};

客户.cpp是:

#include "Cliente.h"
#include <QDebug>
Cliente::Cliente(QString clientName)
{
this->clientName = clientName;
}
void Cliente::inicializarCliente(JackProcessCallback callback) noexcept(false){
jackClient = jack_client_open(clientName.toUtf8().data(), JackNoStartServer, NULL);
int err = jack_set_process_callback(jackClient, callback , this);
}
};

我省略了实现的其余部分,因为它不相关。我也不是在问自由杰克。重点是如何将C++函数作为参数传递给 C 回调,如上所示。

FormQStudio::theCallback

不是一个函数。它是一个非静态成员函数,这意味着它只能通过FormQStudio类型的对象调用,该对象指向该对象的指针作为this传递给函数。

C 对C++类或类成员一无所知。所以它不能调用这样的函数。

此外,应用于FormQStudio::theCallback&运算符会产生一个"指向成员函数的指针"(PMF),它与指向函数的指针完全不同。(这可能就是您看到编译器错误的原因,尽管很难确切地知道您从问题中的代码示例中尝试了什么。因此,即使C++程序也不能将其用作独立的回调,尽管它们可以使用.*/->*语法通过提供的类intance对象引用/指针来调用它。(注意:现在最好使用std::invoke而不是相当晦涩的运算符。

如果事实证明FormQStudio::theCallback实际上根本没有使用this,那就让它static。然后,您可以获取其地址并将其用作函数指针。(尽管我认为不能保证 C 和 C++ 函数具有相同的调用约定,因此可以想象,将其传递给期望回调的 C 函数时仍然会遇到问题。

注意jack_set_process_callback()的最后一个参数是一个void *,它被传递给回调函数。

这是 C 库的常见设计模式,用于使其可用于C++。

您要做的是将要调用其方法的对象的this作为回调传递给带有回调指针的extern "C"void *,该函数采用void *,然后将其reinterpret_cast回类实例指针,然后调用其方法, 像这样:

extern "C" {
int fwd_callback(jack_nframes_t nframes, void *arg)
{
FormQStudio *p=reintrerpret_cast<FormQStudio *>(arg);
return p->theCallback(nframes);
}
};
// ...
FormQStudio *some_object;
// ...
jack_set_process_callback(client, fwd_callback, reinterpret_cast<void *>(some_object));

当然,当调用回调时,您需要确保some_object仍然存在并且是有效的对象。