如何使用 C++ 函数指针作为 C 函数的参数 Pointer/Callback
How to use C++ Function Pointer as Parameter to a C Funcion Pointer/Callback
我知道 C++ lambda 或 C 函数指针,但我不知道如何将它们混合在一起。我已经找到 Mixing C++ instance methods with C callback functions ,但它不符合我的需要。
我有一个 C++ class 包含 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++ class 的实现需要将 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? */
);
}
};
class"Cliente"是这样的:
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;
};
Cliente.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);
}
};
我省略了实施的其余部分,因为它不相关。我也不是在问 lib jack。重点是如何将 C++ 函数作为参数传递给 C 回调,如上所示。
注意 jack_set_process_callback
() 的最后一个参数是 void *
,它会传递给回调函数。
这是 C 库的常见设计模式,使它们可用于 C++。
您要做的是将您希望调用其方法的对象的 this
作为回调传递给 void *
,回调指针指向 extern "C"
使用 void *
的链接包装函数,然后 reinterpret_cast
将其返回到 class 实例指针,然后调用其方法,例如:
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
仍然存在并且是一个有效的对象。
FormQStudio::theCallback
不是函数。它是一个 non-static 成员函数,这意味着它只能通过类型为 FormQStudio
的对象调用,指向该对象的指针作为 this
传递给函数。
C 对 C++ classes 或 class 成员一无所知。所以它不能调用这样的函数。
此外,应用于 FormQStudio::theCallback
的 &
运算符会生成 "pointer-to-member-function" (PMF),这与指向函数的指针完全不同。 (这可能就是您看到编译器错误的原因,尽管很难确切地知道您从问题中的代码示例中尝试了什么。)因此,即使 C++ 程序也不能将其用作 stand-alone 回调,尽管它们可以使用 .*
/->*
语法通过提供的 class 实例对象 reference/pointer 调用它。(注意:现在最好使用 std::invoke
而不是相当晦涩的运算符。)
如果发现 FormQStudio::theCallback
实际上根本没有使用 this
,那么将其设为 static
。然后你可以获取它的地址并将其用作函数指针。 (虽然我认为不能保证 C 和 C++ 函数具有相同的调用约定,所以可以想象,将它传递给 C 函数并期待回调时仍然会遇到问题。)
我知道 C++ lambda 或 C 函数指针,但我不知道如何将它们混合在一起。我已经找到 Mixing C++ instance methods with C callback functions ,但它不符合我的需要。
我有一个 C++ class 包含 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++ class 的实现需要将 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? */
);
}
};
class"Cliente"是这样的:
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;
};
Cliente.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);
}
};
我省略了实施的其余部分,因为它不相关。我也不是在问 lib jack。重点是如何将 C++ 函数作为参数传递给 C 回调,如上所示。
注意 jack_set_process_callback
() 的最后一个参数是 void *
,它会传递给回调函数。
这是 C 库的常见设计模式,使它们可用于 C++。
您要做的是将您希望调用其方法的对象的 this
作为回调传递给 void *
,回调指针指向 extern "C"
使用 void *
的链接包装函数,然后 reinterpret_cast
将其返回到 class 实例指针,然后调用其方法,例如:
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
仍然存在并且是一个有效的对象。
FormQStudio::theCallback
不是函数。它是一个 non-static 成员函数,这意味着它只能通过类型为 FormQStudio
的对象调用,指向该对象的指针作为 this
传递给函数。
C 对 C++ classes 或 class 成员一无所知。所以它不能调用这样的函数。
此外,应用于 FormQStudio::theCallback
的 &
运算符会生成 "pointer-to-member-function" (PMF),这与指向函数的指针完全不同。 (这可能就是您看到编译器错误的原因,尽管很难确切地知道您从问题中的代码示例中尝试了什么。)因此,即使 C++ 程序也不能将其用作 stand-alone 回调,尽管它们可以使用 .*
/->*
语法通过提供的 class 实例对象 reference/pointer 调用它。(注意:现在最好使用 std::invoke
而不是相当晦涩的运算符。)
如果发现 FormQStudio::theCallback
实际上根本没有使用 this
,那么将其设为 static
。然后你可以获取它的地址并将其用作函数指针。 (虽然我认为不能保证 C 和 C++ 函数具有相同的调用约定,所以可以想象,将它传递给 C 函数并期待回调时仍然会遇到问题。)