Как использовать указатель функции C ++ в качестве параметра для указателя / обратного вызова функции C - PullRequest
0 голосов
/ 10 июля 2019

Я знаю о лямбдах C ++ или указателях функций 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? */
   );

}
};

Класс "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);
}
};

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

Ответы [ 2 ]

0 голосов
/ 10 июля 2019

FormQStudio::theCallback не является функцией. Это нестатическая функция-член, которая означает, что ее можно вызывать только через объект типа FormQStudio, указатель на который передается функции как this.

C ничего не знает о классах C ++ или их членах. Поэтому он не способен вызывать такую ​​функцию.

Более того, оператор &, примененный к FormQStudio::theCallback, создает «указатель на член-функцию» (PMF), который совсем не то же самое, что указатель на функцию , (Вероятно, именно поэтому вы видите ошибки компилятора, хотя трудно точно знать, что вы пытались из примера кода в своем вопросе.) Поэтому даже программы на C ++ не могли использовать это в качестве автономного обратного вызова, хотя они могли использовать .* / ->* синтаксис для вызова его с указанным указателем / указателем на объект intance класса (Примечание: в наши дни лучше использовать std::invoke, а не довольно неясные операторы.)

Если окажется, что FormQStudio::theCallback на самом деле вообще не использует this, тогда сделайте его static. Затем вы можете взять его адрес и использовать его как указатель на функцию. (Хотя я думаю, что не гарантируется, что функции C и C ++ имеют одинаковые соглашения о вызовах, поэтому вполне возможно, что у вас все еще могут быть проблемы с передачей его функции C, ожидающей обратного вызова.)

0 голосов
/ 10 июля 2019

Обратите внимание на тот факт, что последний параметр jack_set_process_callback () является void *, и он передается в функцию обратного вызова.

Это общий шаблон проектирования с библиотеками C длясделать их пригодными для использования с C ++.

То, что вы собираетесь сделать, это передать this объекта, метод которого вы хотите вызвать, в качестве обратного вызова, как void * с указателем обратного вызова на extern "C" функция-обертка связывания, которая принимает 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 все еще существует и является допустимым объектом, когда вызывается обратный вызов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...