0

У меня есть класс кнопки Button, в котором я создал указатель на функцию, которая должна исполняться по нажатию на кнопку. Эдакий коллбэк, задаваемый извне.
В определенном месте кода, мне нужно использовать в этом коллбэке текст, введенный пользователем в текстовое поле, что происходит в определенном классе. Но я получаю ошибку login.cpp|80|undefined reference to Login::tf

Как мне добиться того, чтобы объявленная вне класса функция, могла обратиться к его данным, и использовать/изменять их?

Вот код класса Button ниже; То, что я посчитал маловажным убрано, но если что, полный код лежит на github:
(если это возможно, я бы хотел оставить интерфейс этого класса без изменений, так как он много где используется)

///Класс кнопки. Обрабатывает клик, выполняет скормленную ему функцию реакции на него.
class Button : public AbstractUIElement
{
/*стандартный цвет кнопки, цвет при вхождении курсора в область кнопки*/

public: void (onClick)(StateBasedGame, SDL_Event*); // функция-обработчик клика

/// можно создать кнопку, не устанавливая функцию, которую она будет выполнять
Button(char* myText) {/*...*/}

/// инициализация кнопки функцией, которую она выполняет
Button( void (*callback)(StateBasedGame* e, SDL_Event* g), char* myText )
{
   onClick = callback;
   /*...*/
}

void update(StateBasedGame* g, SDL_Event* e)
{
    if( e->type == SDL_MOUSEMOTION || e->type == SDL_MOUSEBUTTONDOWN || e->type == SDL_MOUSEBUTTONUP )
    {
        int x, y;
        SDL_GetMouseState( &x, &y );

        bool inside = true;

        if( x < bounds.x )
        {
            inside = false;
        }
        else if( x > bounds.x + bounds.w )
        {
            inside = false;
        }
        else if( y < bounds.y )
        {
            inside = false;
        }
        else if( y > bounds.y + bounds.h )
        {
            inside = false;
        }

        if( !inside )
        {
            color = backup_color;
        }
        else
        {
            switch( e->type )
            {
                case SDL_MOUSEMOTION:
                    color = ACTIVE_COLOR;
                break;

                case SDL_MOUSEBUTTONDOWN:
                     color = CLICK_COLOR;
                     onClick(g, e);
                break;
                case SDL_MOUSEBUTTONUP:
                     color = CLICK_COLOR;
                     onClick(g, e);
                break;
            }
        }
    }
}

void setCallback (void (*callback)(StateBasedGame* e, SDL_Event* g))
{
    this->onClick = callback;
}

};

А вот класс, данные которого должна использовать кнопка. В TextField tf вводится имя, которое по нажатию на кнопку должно записаться в глобальную структуру, отвечающую за пользователей в текущей сессии. Код выложен практически полностью, но вот на всякий случай ссылка на него в том же проекте.

#ifndef LOGIN
#define LOGIN

using namespace std;

static void loginOnClick(StateBasedGame* g , SDL_Event* e);

class Login : public GameState { SDL_Event event; SDL_Surface* screen; Button* back_button; Label* text; public: static TextField tf;

void init( SDL_Surface* display, StateBasedGame* g )
{
    screen = display;
    back_button = new Button(loginOnClick, "Продолжить");
    back_button->bounds.x = (display->w - back_button->bounds.w)/2;
    back_button->bounds.y = display->h - back_button->bounds.h - 50;

    text = new Label("Введите имя:");
    text->bounds.w = 300;
    text->bounds.x = (screen->w - text->bounds.w)/2;
    text->bounds.y = screen->h/2-50;

    tf.bounds.w = 300;
    tf.bounds.x = (screen->w - tf.bounds.w)/2;
    tf.bounds.y = screen->h/2;
}

void update( StateBasedGame* g, int delta )
{
    if(SDL_PollEvent(&event)) {
        if(event.type == SDL_QUIT) g->exit();
        tf.update(event);
    }
    back_button->update(g, &event);
}

void render( SDL_Surface* display )
{
    back_button->render(display);
    text->render(display);
    tf.render(display);
}

SDL_Event* pollEvent( void )
{
    return &event;
}

SDL_Surface* getScreen()
{
    return screen;
}

~Login()
{
    delete back_button;
    delete text;
}

};

#ifndef LOGIN_ON_CLICK #define LOGIN_ON_CLICK static void loginOnClick(StateBasedGame* g , SDL_Event* e) { Player* player = new Player((Login::tf).text); current_session.players.push_back(player); current_session.current_player = player; g->switchState(states::main_menu); } #endif // LOGIN_ON_CLICK

#endif // LOGIN

Я пробовал следующее:

  • создавать функцию статической до объявления класса, но тогда она не знает о его существовании, и как следствие не может обратиться к его членам
  • создавать её и статической и не статической внутри класса. Позже нашёл объяснение в интернете, что это так работать не будет. Там есть пример рабочего синтаксиса, но он, на сколько я понял, будет привязан к одному конкретному классу, а мне нужен подход, который позволит использовать подобные конструкции в будущем и для других классов - универсальная кнопка, какой она до сих пор и была, пока не потребовался доступ к членам класса-пользователя кнопки.
  • пробовал пользоваться лямбда-выражением вместо создания функции, и передачи её указателя позже, но запутался в синтаксисе, и словил столько ошибок, что оставил идею, и вернулся к ранее работавшему варианту с отдельной функцией. Сама идея реализовать функцию прямо на месте присвоения, как анонимный объект в java, мне нравится.

Буду рад, если поможете починить мои кнопки, и объясните, что я делаю не так. Заранее спасибо.

OldTeaOwl
  • 366
  • А лишний параметр передавать в функцию? Указатель на нужный объект? – Harry Nov 09 '20 at 04:04
  • 90% приведенного кода - маловажный и никак не относится к проблеме, прежде чем задавать вопрос потрудитесь составить [mcve] – user7860670 Nov 09 '20 at 07:26
  • Маленькое послание ребятам закрывшим вопрос: он НЕ является дубликатом - то, что вы прикрепили не решает моей проблемы и не объясняет как и что работает - это просто некая общая выдержка. Это не помогает. Не можете ответить на вопрос - подождите того, кто сможет - если это не поможет мне, может помочь человеку с такой же задачей да хоть через 10 лет. Для того ресурс и существует. Наша русская тяга к бюрократии и "формам", которым должны соответствовать реальные проблемы утопит ресурс - вылизать все до блеска НЕ ПОЛУЧИТСЯ, ибо ПРОГРАММИРОВАНИЕ ТАК НЕ РАБОТАЕТ. Простите за капс. – OldTeaOwl Nov 12 '20 at 05:03
  • @Harry да, в итоге так и решил задачу - сделал параметр с указателем на void, чтобы можно было передавать объекты любого типа. Не самое изящное, но довольно действенное получилось решение. – OldTeaOwl Nov 12 '20 at 05:05

0 Answers0