Почему я не могу установить неглобальную функцию в качестве обратного вызова в Джеке? - PullRequest
1 голос
/ 07 августа 2011

Я пытаюсь установить функцию обратного вызова в Джеке. Обычно это довольно просто, используя функцию «jack_set_process_callback» (в случае настройки обратного вызова «process»).

Определение в API таково:

int jack_set_process_callback(jack_client_t* client, JackProcessCallback process_callback, void* arg).

В примере кода (который содержит компоненты C) объявлена ​​глобальная функция, приведенная ниже.

int process(jack_nframes_t nframes, void *arg){

  // do something in the callback
  return 0;
}

Затем он устанавливается как обратный вызов процесса Jack по строке:

jack_set_process_callback(client, process, 0)

.. и это компилируется и работает отлично.

То, что я сейчас делаю, - это попытка создать аудиопрограмму на основе классов. Я помещаю все вещи Джека в класс с образным названием «Sound_Module».

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

int Sound_Module::process(jack_nframes_t nframes, void *arg){

  // do something in the callback
  return 0;
}

Я пытаюсь установить этот метод в качестве моей функции обратного вызова в конструкторе класса:

(83) jack_set_process_callback(client, process, 0)

но при компиляции это приводит к следующей ошибке:

sound_module.cpp:83: error: argument of type ‘int (Sound_Module::)(jack_nframes_t, void*)’ does not match ‘int (*)(jack_nframes_t, void*)’

Из этого сообщения об ошибке создается впечатление, что мне нужно привести функцию обратного вызова к чему-то другому. Я пробовал несколько вещей, таких как приведение к типу Jack, предназначенному для обратных вызовов без удачи - ниже приведена строка, вырытая из API для «JackProcessCallback».

typedef int(* JackProcessCallback)(jack_nframes_t nframes, void *arg)

Может кто-нибудь пролить свет на то, на что может указывать ошибка компилятора, или на то, что я могу делать неправильно?

Спасибо!

Ответы [ 5 ]

3 голосов
/ 07 августа 2011

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

Вам необходимо написать функцию, не являющуюся членом, которая делегируетк функции-члену.Когда вы вызываете jack_set_process_callback, вы указываете библиотеке, что передавать параметру void* вашего обратного вызова, чтобы вы могли сказать ей, чтобы он возвращал адрес объекта Sound_Module, для которого вы хотите вызвать функцию-член callback.

Обратный вызов, не являющийся членом, довольно прост:

int Sound_Module_Process_Callback(jack_nframes_t x, void* p)
{
    return static_cast<Sound_Module*>(p)->process(x);
}

Зарегистрирован как:

jack_set_process_callback(client, Sound_Module_Process_Callback, this);
1 голос
/ 07 августа 2011

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

Я думаю, вы должны сделать это:

int callback(jack_nframes_t nframes, void* arg)
{
    return static_cast<Sound_Module*>(arg)->process(nframes);
}

И определить функцию-член process как

int Sound_Module::process(jack_nframes_t nframes){

  // do something in the callback
  return 0;
}

То есть должен принимать только один параметр. Второй параметр не нужен.

Кроме того, вы должны позвонить jack_set_process_callback как:

 jack_set_process_callback(client, callback, &soundModule);
                                           //^^^^^^^^^^^^

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

void Sound_Module::SomeNonStaticMemberFunction()
{
     jack_set_process_callback(client, callback, this);
}
1 голос
/ 07 августа 2011

Вы не можете сделать это.

tl; dr: Функции-члены не являются свободными функциями.Ваш обратный вызов должен быть свободной функцией.

Более длинный ответ: для функции обратного вызова set указатель на функцию в качестве аргумента обратного вызова.Только свободные функции могут быть превращены в указатель на функцию.Нестатические функции-члены не являются свободными функциями, и их нельзя превратить в указатели на функции!Выражение &Foo::f представляет собой указатель на функцию-член (PTFM), который является совершенно другим, несовместимым, обычно гораздо большего типа.

Если ваша клиентская функция исправлена ​​с помощьюC API, тогда у вас есть два возможных решения:

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

  2. Написать глобальную функцию-обертку.

Sound_Module g_sm1; // global!
Sound_Module g_sm2;

int process1(jack_nframes_t nframes, void *arg)
{
  return g_sm1.process_firsttype(nframes, arg);
}
int process2(jack_nframes_t nframes, void *arg)
{
  return g_sm1.process_secondtype(nframes, arg);
}
int process3(jack_nframes_t nframes, void *arg)
{
  return g_sm2.process_firsttype(nframes, arg);
}
int process4(jack_nframes_t nframes, void *arg)
{
  static Sound_Module s_sm;
  return s_sm.process_firsttype(nframes, arg);
}
1 голос
/ 07 августа 2011

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

error: argument of type ‘int (Sound_Module::)(jack_nframes_t, void*)’  
                            // ^^^^^^^^^^^^^

В этой части сообщения об ошибке говорится, что это функция-член, намекающая на скрытый аргумент this .

does not match ‘int (*)(jack_nframes_t, void*)’

И эта часть говорит, что это обычное объявление функции обратного вызова типа C.

Вместо этого попробуйте это -

 class Sound_Module {
    public:
    static int process(jack_nframes_t nframes, void *arg) ; 
   // This is just declaration. Provide the definition in the source file.
 };
0 голосов
/ 26 декабря 2012

Более ориентированный на C ++ подход, ключ использует предопределенный тип Jack's JackProcessCallback:

#include <jack/jack.h>

BaseSoundModule
{
    public:

    jack_client_t * client;

    //Use a constructor to assign a client to the class.
    BaseSoundModule( jack_client_t * external_client ){ client = external_client; }

    //Use Jack's JackProcessCallback predefined type.
    void setProcessCallback( JackProcessCallback, void * pointerArgument )
    {
        jack_set_process_callback( client, JackProcessCallback, pointerArgument );
    }

};

Metronome : public BaseSoundModule
{
    static int process_audio( jack_nframes_t nframes, void * arg )
    {
        //Note #1 at the end of this example
        Metronome * MetroPointer = ( Metronome * ) arg;

        //Call other functions inside your metronome class;
        Metronome->someotherfunc();
    }

    int someotherfunc(){ //return something. }
};

Тогда вы бы назвали это так:

jack_client_t * client;

int main()
{
    Metronome Metro;

    Metronome * MetroPointer = &Metro;

    BaseSoundModule * PolyMetro = &Metro;

    PolyMetro->setProcessCallback( MetroPointer->process_audio, MetroPointer );

}
  • Примечание 1: обратите внимание, что внутри process_audio вы также передали MetroPointer в качестве аргумента. Это означает, что вы можете использовать другие методы дочернего класса внутри обратного вызова.
...