Указатели и классы функций C ++ - PullRequest
12 голосов
/ 13 сентября 2008

Скажи, что у меня есть:

void Render(void(*Call)())
{
    D3dDevice->BeginScene();
    Call();
    D3dDevice->EndScene();
    D3dDevice->Present(0,0,0,0);
}

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

Render(MainMenuRender);
Render(MainMenu::Render);

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

Render(MainMenu->Render);

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

Ответы [ 6 ]

14 голосов
/ 13 сентября 2008

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

Это будет выглядеть так:

#include <boost/bind.hpp>
#include <boost/function.hpp>

void Render(boost::function0<void> Call)
{
    // as before...
}

Render(boost::bind(&MainMenu::Render, myMainMenuInstance));
3 голосов
/ 13 сентября 2008

Вы можете сделать функцию-оболочку void Wrap(T *t), которая просто вызывает t->Call() и Render принимает такую ​​функцию вместе с объектом. То есть:

void Wrap(T *t)
{
  t->Call();
}

void Render(void (*f)(T *), T *t)
{
  ...
  f(t);
  ...
}
2 голосов
/ 13 сентября 2008

А как насчет C ++ FAQ: указатели на членов говорит?

1 голос
/ 13 сентября 2008

Вы не можете вызывать функцию-член из указателя, если у вас также нет ссылки на объект. Например:

<code>((object).*(ptrToMember))

Так что вы не сможете добиться этого без изменения сигнатуры вашего метода рендеринга. Эта статья объясняет, почему это вообще плохая идея.

Лучшим способом может быть определение интерфейса "Renderer", который ваши классы, имеющие методы визуализации, могут реализовать и которые являются типом параметра вашего основного метода Render. Затем вы можете написать реализацию StaticCaller для поддержки вызова ваших статических методов по ссылке.

Например (Мой C ++ действительно ржавый, я тоже не скомпилировал).



void Render(IRenderer *Renderer)
{
    D3dDevice->BeginScene();
    Renderer->Render();
    D3dDevice->EndScene();
    D3dDevice->Present(0,0,0,0);
}

// The "interface"
public class IRenderer 
{
public:
    virtual void Render();
};

public class StaticCaller: public IRenderer
{
    void (*Call)();
public:

    StaticCaller((*Call)())
    {
        this->Call = Call;
    }

    void Render()
    {
        Call();
    }
};

Все это довольно шаблонно, но это должно улучшить читаемость.

1 голос
/ 13 сентября 2008

Я сделал это один раз, определив глобальную функцию «Вызов», которая принимает указатель на ваш intance в качестве члена

void CallRender(myclass *Instance)
{
  Instance->Render();
}

Таким образом, рендер становится:

void Render(void (*Call)(myclass*), myclass* Instance)
{
  ...
  Call(Instance);
  ...
}

И ваш вызов для рендеринга:

Render(CallRender, &MainMenu);

Я знаю, что это некрасиво, но у меня получилось (я использовал pthreads)

0 голосов
/ 10 декабря 2008

Вы можете объявить указатель на функцию-член класса T, используя:

typedef void (T::*FUNCTIONPOINTERTYPE)(args..)
FUNCTIONPOINTERTYPE function;

И вызвать его как:

T* t;
FUNCTIONPOINTERTYPE function;
(t->*function)(args..);

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

...