Как заставить работать простую кнопку класса с обратным вызовом? - PullRequest
0 голосов
/ 10 октября 2019

Как сделать простой класс кнопок для вызова пользовательской функции при нажатии. Пример кода:

class Button
{
    public:
        Button();
        Draw();
        Press();
        SetCallback(void(*cback)());

    priate:
        void (*callback)();
}

void Button::SetCallback(void(*cback)())
{
    callback = cback;
}

class GameState
{
    ...
}

class MenuState : public GameState
{
    Button *btn;
}

class Game
{
    ...
}
bool Game::Init()
{
    std::unique_ptr<GameState> menu = std::unique_ptr<MenuState>(new MenuState);

}
void Game::PopState(){
        states.pop_back();
}

Как установить функцию обратного вызова в кнопке для функции Game :: PopState () в этом примере. Мне нужно тогда я нажимаю кнопку вызова функции Game :: PopState.

1 Ответ

1 голос
/ 11 октября 2019

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

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

Вы можете изменить приведенный ниже пример, заменив ссылку на строку ссылкой на объект Game. Если вам нужен более общий обратный вызов, загляните в шаблоны (лучше, но немного сложнее) или наберите текст ( обычно считается плохой практикой )


#include <iostream>
#include <string>
class Button
{
    public:
        void SetCallback(void(*cback)(std::string&));
        void Press(std::string str){
          //Make sure some sort of check is done in order to prevent undefined behavior from a call
          if(callback != nullptr)
            callback(str);
        }
        Button(){
          callback = nullptr;
        }
    private:
        void (*callback)(std::string&);
};

void Button::SetCallback(void(*cback)(std::string&))
{
    callback = cback;
}
//Method 2 for calling function
void DoStuff(std::string& str){
  std::cout << str;
}

int main(void){

  Button btn;
  //Method 1 for calling function, replace body with Game.PopStates();
  auto foo = [](std::string& str){ std::cout<<str.size();};
  btn.SetCallback(foo);
  btn.Press("A");
  //Method 2 for calling function
  btn.SetCallback(&DoStuff);
  btn.Press("\nPressed Button");
  return 0;
}

Ваш указатель функции для Game должен быть чем-то вродекак это:

void SetCallback(void(*cback)(Game&));

Вы можете назвать это так:

auto foo = [](Game& g){ g.DoStuffInClass(); };
  btn.SetCallback(foo);
  btn.Press(game);

Приведенные выше примеры работают нормально и, как правило, это все, что вам нужно сделать, но эта статья здесь более подробно описывает указатели на функции-члены. Основной вывод из этой статьи - это typedef - еще один способ написания указателей на функции-члены, как показано ниже:

typedef int (Fred :: * FredMemFn) (char x, float y);// Пожалуйста, сделайте это!

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

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