Указатель на функцию-член класса C ++ для указателя на функцию - PullRequest
4 голосов
/ 02 августа 2011

Я использую luabind в качестве оболочки для lua to C ++.Luabind предлагает метод использования моей собственной функции обратного вызова для обработки исключений, выданных lua, set_pcall_callback ().Итак, я перефразировал пример из документации, изменения были функцией logger-> log () и помещением функции в класс с именем «Engine», поэтому вместо обычной глобальной функции она теперь является функцией-членом, котораягде моя проблема кажется.

Вот соответствующие фрагменты кода:

class Engine //Whole class not shown for brevity
{
public:
    Engine();
    ~Engine();
    void Run();
    int pcall_log(lua_State*);
private:
    ILogger *logger;
};

Engine::Run()
{
lua_State* L = lua_open();
luaL_openlibs(L);
open(L);
luabind::set_pcall_callback(&Engine::pcall_log); //<--- Problem line
//etc...rest of the code not shown for brevity
}

int Engine::pcall_log(lua_State *L)
{
lua_Debug d;
lua_getstack( L,1,&d);
lua_getinfo( L, "Sln", &d);
lua_pop(L, 1);
stringstream ss;
ss.clear();
ss.str("");
ss << d.short_src;
ss << ": ";
ss << d.currentline;
ss << ": ";
if ( d.name != 0)
{
    ss << d.namewhat;
    ss << " ";
    ss << d.name;
    ss << ") ";
}
ss << lua_tostring(L, -1);
logger->log(ss.str().c_str(),ELL_ERROR);
return 1;
}

Вот что говорит компилятор во время компиляции:

C:\pb\engine.cpp|31|error: cannot convert 'int (Engine::*)(lua_State*)' to 'int (*)(lua_State*)' for argument '1' to 'void luabind::set_pcall_callback(int (*)(lua_State*))'|

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

Спасибо!

Ответы [ 3 ]

12 голосов
/ 02 августа 2011

Нет. Функция-член не является свободной функцией. Тип полностью отличается, и указатель на функцию-член (PTMF) является совершенно другим, несовместимым объектом и указателем на функцию.(PTMF обычно намного больше, например.) Самое главное, чтобы указатель на член всегда использовался вместе с указателем экземпляра на объект, член которого вы хотите вызвать, поэтому вы даже не можете использовать PTMF так же, как вы используете указатель на функцию.

Самое простое решение для взаимодействия с кодом C - это написать глобальную функцию-обертку, которая отправляет ваш вызов, или сделать функцию-член статической (в этом случае она становится по существу свободной функцией):

// global!

Engine * myEngine;
int theCallback(lua_State * L)
{
  return myEngine->pcall_log(L);
}

Engine::Run()
{
  /* ... */
  myEngine = this;
  luabind::set_pcall_callback(&theCallback);
  /* ... */
}

Концептуальная проблема здесь заключается в том, что у вас есть движок класса , хотя у вас практически будет только один его экземпляр,Для подлинного класса со многими объектами PTMF не имеет смысла, потому что вам придется указывать, какой объект использовать для вызова, тогда как ваш класс движка, возможно, по сути является одноэлементным классом, который может быть полностью статическим (то есть прославленным пространством имен).

0 голосов
/ 20 сентября 2018

Не подходит для вашей проблемы с LUA, но может быть в других библиотеках: Если функция запрашивает указатель функции, такой как func (void * param, ...), и вы можете убедиться, что время жизни вашего объекта больше, чем указатель на хранимую функцию, то технически вы также можете использовать указатель метода (выглядит так же стек), но C ++ предотвращает непосредственное приведение указателей методов к указателям на функции.

Но с небольшой хитростью вы также можете привести указатели методов к указателям на функции:

template<typename M> inline void* GetMethodPointer(M ptr)
{
    return *reinterpret_cast<void**>(&ptr);
}

Используя это, вы можете использовать указатели методов, например, с libmicrohttpd:

this->m_pDaemon = MHD_start_daemon(MHD_USE_THREAD_PER_CONNECTION, this->m_wPort, NULL, NULL, reinterpret_cast<MHD_AccessHandlerCallback>(GetMethodPointer(&CMyWebServer::AccessHandlerCallback)), this, MHD_OPTION_END);

Но знайте об этом. Вы должны заботиться о жизни этого объекта. Кроме того, соглашения о вызовах должны совпадать.

0 голосов
/ 02 августа 2011

В качестве обратного вызова обычно используется static function s:

class Engine //Whole class not shown for brevity
{
    ....
    static int pcall_log(lua_State*);
    ...
}

Это решит вашу проблему.

...