Вызов указателя функции-члена из статической функции-члена

Я пишу программу, которая использует SDL_Audio и динамически воспроизводит звук, но я обнаружил, что моя реализация не очень отзывчива с задержкой от 0,3 до 0,5 секунды.

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

  • запускает производство звука
  • сбрасывает указатель функции на метод audioCallback
  • вызывает метод audioCallback напрямую

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

Поиск правильного синтаксиса для вызова функции-члена через указатель функции.

У меня есть следующая тестовая программа, которая воспроизводит мою проблему.

/*
 * testcallback.cpp
 *
 *  Created on: Apr 20, 2023
 *      Author: phil
 */
#include <SDL2/SDL.h>
#include <fstream>
#include <iostream>

class AudioCallbackTest
{
public:
    AudioCallbackTest()
    {
    SDL_Init(SDL_INIT_AUDIO);
    SDL_AudioSpec desiredSpec = {0};
    desiredSpec.format = AUDIO_S16SYS;
    desiredSpec.channels = 1;
    desiredSpec.freq = 44100;
    desiredSpec.samples = 256;
    desiredSpec.callback = audioCallbackWrap;
    desiredSpec.userdata = this;

    m_hwDevice = SDL_OpenAudioDevice(nullptr, false, &desiredSpec, NULL, 0);
    if (m_hwDevice == 0)
    {
        std::cout << "SDL_OpenAudioDevice failed: SDL error [" << SDL_GetError() << "]" << std::endl;
        return;
        }
    }

    static void audioCallbackWrap(void *_instance, Uint8* _stream, int _length)
    {
//      ((AudioCallbackTest*)_instance)->m_audioCallback(_stream, _length);
        
        ((AudioCallbackTest*)_instance)->*m_audioCallback(_stream, _length);
    }
    void audioCallback(Uint8* stream, int len)
    {
        std::cout << "audio callback" << std::endl;
    }
    void primeAudioCallback(Uint8* stream, int len)
    {
        std::cout << "prime callback" << std::endl;
        m_audioCallback = &AudioCallbackTest::audioCallback;
    }

private:
    SDL_AudioDeviceID m_hwDevice;
    void (AudioCallbackTest::*m_audioCallback)(uint8_t *stream, int len) = &AudioCallbackTest::primeAudioCallback;
};

Обратите внимание на закомментированную строку в статической функции. В первой форме я получаю ошибку

g++ -O0 -g3 -Wall -c -fmessage-length=0 -o testcallback.o ../testcallback.cpp 
../testcallback.cpp: In static member function ‘static void AudioCallbackTest::audioCallbackWrap(void*, Uint8*, int)’:
../testcallback.cpp:35:65: error: must use ‘.*’ or ‘->*’ to call pointer-to-member function in ‘((AudioCallbackTest*)_instance)->AudioCallbackTest::m_audioCallback (...)’, e.g. ‘(... ->* ((AudioCallbackTest*)_instance)->AudioCallbackTest::m_audioCallback) (...)’
   35 |                 ((AudioCallbackTest*)_instance)->m_audioCallback(_stream, _length);
      |                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~

Со второй формой я получаю следующую ошибку

g++ -O0 -g3 -Wall -c -fmessage-length=0 -o testcallback.o ../testcallback.cpp 
../testcallback.cpp: In static member function ‘static void AudioCallbackTest::audioCallbackWrap(void*, Uint8*, int)’:
../testcallback.cpp:37:51: error: invalid use of member ‘AudioCallbackTest::m_audioCallback’ in static member function
   37 |                 ((AudioCallbackTest*)_instance)->*m_audioCallback(_stream, _length);
      |                                                   ^~~~~~~~~~~~~~~
../testcallback.cpp:51:35: note: declared here
   51 |         void (AudioCallbackTest::*m_audioCallback)(uint8_t *stream, int len) = &AudioCallbackTest::primeAudioCallback;
      |                                   ^~~~~~~~~~~~~~~

версия компилятора GNU C++17 (Ubuntu 11.3.0-1ubuntu1~22.04) версия 11.3.0 (x86_64-linux-gnu) Версия IDE: 2023-03 (4.27.0) Версия CDT: 11.1.0.202212091724

Я пробовал различные синтаксис

    This->m_audioCallback(_stream, _length);
    This->*m_audioCallback(_stream, _length);
    (This->*m_audioCallback)(_stream, _length);
    This->*m_audioCallback(_stream, _length);
    (*This).*m_audioCallback(_stream, _length);

Я погуглил сообщение об ошибке.

Я гуглил «статический член, вызывающий член через указатель функции-члена», и получил информацию о том, как вызывать функции-члены, и множество примеров указателей на функции, но ничего, что попало в цель.

Я предполагаю, что для этого есть правильный синтаксис.

Почти уверен, что вы хотите (((AudioCallbackTest*)_instance)->*m_audioCallback)(_stream, _length);.

Sam Varshavchik 20.04.2023 20:28
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
1
91
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Ответ принят как подходящий

Сообщение об ошибке во втором примере говорит вам, что не так:

../testcallback.cpp:37:51: error: invalid use of member ‘AudioCallbackTest::m_audioCallback’ in static member function
   37 |                 ((AudioCallbackTest*)_instance)->*m_audioCallback(_stream, _length);
      |                                                   ^~~~~~~~~~~~~~~

m_audioCallback — это переменная экземпляра, поэтому вы не можете просто

((AudioCallbackTest*)_instance)->*m_audioCallback(_stream, _length);

Вам нужно сообщить компилятору, чей m_audioCallback использовать:

((AudioCallbackTest*)_instance)->*((AudioCallbackTest*)_instance)->m_audioCallback(_stream, _length);

Странно, я тоже не могу заставить это работать. clang сообщает, что тип объекта 'void (AudioCallbackTest::)(uint8_t, int)' не является функцией или указателем на функцию, но void (AudioCallbackTest::*)(uint8_t *, int), черт возьми, выглядит как указатель на функцию. g++ жалуется на ошибку: необходимо использовать '.' или '->' для вызова функции указателя на член, что я почти уверен, что делаю.

Но std::invoke не совершает той ошибки, которую совершаю я.

#include <iostream>
#include <functional>
using Uint8 = uint8_t;
class AudioCallbackTest
{
public:
    AudioCallbackTest()
    {
    }

    static void audioCallbackWrap(void *_instance, Uint8* _stream, int _length)
    {
        AudioCallbackTest* instance = (AudioCallbackTest*)_instance;  // to simplify following
        std::invoke(instance->m_audioCallback, *instance, _stream, _length);
        //                    ^ Is an instance variable. Needs to be accessed through an instance
        //          ^ and here's the instance

        //dunno what I have wrong in the syntax here.
        // void (AudioCallbackTest::*)(uint8_t *, int) sure looks like a method pointer to me.
        //instance->*(instance->m_audioCallback)(_stream, _length); 


    }
    void audioCallback(Uint8* , int )
    {
        std::cout << "audio callback" << std::endl;
    }
    void primeAudioCallback(Uint8* , int )
    {
        std::cout << "prime callback" << std::endl;
        m_audioCallback = &AudioCallbackTest::audioCallback;
    }

private:
    void (AudioCallbackTest::*m_audioCallback)(uint8_t *stream, int len) = &AudioCallbackTest::primeAudioCallback;
};

int main()
{
    AudioCallbackTest test;
    Uint8 stream = 1;
    AudioCallbackTest::audioCallbackWrap (&test, &stream, 2);
    AudioCallbackTest::audioCallbackWrap (&test, &stream, 2);
}

Выводит ожидаемый

prime callback
audio callback

Поскольку работа std::invoke заключается в упрощении такого синтаксиса, используйте его.

Но std::invoke не делает той ошибки, которую делаю я. Все, что вам не хватает, это дополнительный набор скобок, похоже, но я обнаружил, что разбиение его на 3 строки значительно упрощает жизнь.

Paul Sanders 20.04.2023 22:09

Ну что ж, сукин сын, @PaulSanders. (instance->*(instance->m_audioCallback))(_stream, _length); Не могу поверить, что я этого не пробовал.

user4581301 20.04.2023 22:27

Рад, что я не единственный, кто спотыкается о такие вещи.

Paul Sanders 20.04.2023 23:18

Я попытался упростить ваш код и, согласно этому ответу, я попытался преобразовать указатель, но это не сработало.

class A
{
  void (A::* m_func_ptr)(int len) = &A::foo2;
  static void foo(void* obj, int k) {
    //want call foo1
    A* p = static_cast<A*>(obj);
    p->m_func_ptr(k);
  }
  void foo1(int len) { cout << "foo1" << endl; }
  void foo2(int len) {
    cout << "foo2" << endl;
    m_func_ptr = &A::foo1;
  }
};

Возможно, вы можете попробовать синглтон в том же ответе выше, объявив статический указатель в классе:

class A
{
public:
  static A* myInstance;
  //static A* getInstance() {
  //  if (!myInstance) {
  //    myInstance = new A;
  //  }
  //  return myInstance;
  //}
  static void foo(void* obj, int k) {
    //call foo1
    A* p = static_cast<A*>(obj);
    p->foo1(k);
  }
  void foo1(int len) { cout << "foo1" << endl; }
  void foo2(int len) {
    cout << "foo2" << endl;
  }
};

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

В качестве альтернативы ответу @ user4581301 (хотя я думаю, что на самом деле я предпочитаю его метод), вот волшебный соус для прямого вызова функции:

static void audioCallbackWrap(void *_instance, Uint8* _stream, int _length)
{
    auto *inst = (AudioCallbackTest*) _instance;
    auto call_me = inst->m_audioCallback;
    (inst->*call_me) (_stream, _length);
}

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

Никогда раньше не использовал std::invoke, поэтому я сначала попробовал это, просто чтобы понять, и он отлично работает.

Также попробовал второй вариант (instance->*(instance->m_audioCallback))(_stream, _length); и это тоже работает.

Большое спасибо за помощь.

Но, в конце концов, у меня все еще есть задержка в 200 мс.

Другие вопросы по теме