2013-05-12 6 views
0

У меня есть класс, как следует и основной метод следующим образом:Передача функции-члены класса в качестве аргумента

class Mesh 
{ 
public: 
    void draw() 

    void draw(){} 
} 

int main(int argc,char* argv[]) 
{ 
    Mesh mesh; 
    glutDisplayFunc(mesh.draw); 
} 

Так что я хочу передать функцию-член объекта в качестве аргумента, но компилятор выводит следующее сообщение об ошибке:

error: argument of type ‘void (Mesh::)()’ does not match ‘void (*)()’ 

Что я делаю неправильно?

+3

Посмотрите на указатели функций-членов. – Pubby

+0

'glutDisplayFunc' ожидает указатель на свободную функцию без параметров и ничего не возвращает. Вы не можете передать ему указатель на функцию-член. – jrok

+0

Можете ли вы написать декларацию glutDisplayFunc()? – shubendrak

ответ

2

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

В чистом C++ вы должны использовать функциональный объект (функтор «a.k.a.», см. std::function или его предшественник boost::function). Это, к сожалению, не вариант в C или C API.

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

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

// at some global-ish file scope... 

// we define our class 
class Mesh { ... }; 

// and here's the one global instance of it 
Mesh myOneMesh; 

// and here is our global/static function that "knows" 
// which instance to draw 
void drawMyOneMesh() { myOneMesh.draw(); } 

// then, in the main flow of your program: 
glutDisplayFunc(&drawMyOneMesh); 

Если у вас есть несколько мешей, похоже, лучшее, что вы можете сделать, это ввести его в текущем окне. Не зная многое о вашем приложении или о GLUT API, я бы, вероятно, сделать что-то вроде этого, чтобы позволить меш-в-окна обратного вызова:

#include <map> 

// this could also be a class with all-static members, if one 
// were so inclined. might be worth it, if only to protect 'map' 
// from external meddling. 

namespace WindowToMesh 
{ 

    typedef std::map< int, Mesh * > Map; 
    Map map; 

    void addMapping(int win, Mesh * mesh) 
    { 
     map.insert({ win, mesh }); 
    } 

    void removeMapping(int win) 
    { 
     map.erase(win); 
    } 

    void draw() 
    { 
     int win = glutGetWindow(); 
     Map::iterator it = map.find(win); 
     if (it != map.end()) 
      it.second->draw(); 
    } 

} // end namespace WindowToMesh 

Теперь, в основной программе, вы можете связать новый Mesh с текущее окно:

Mesh * m = new Mesh(...); 
WindowToMesh::addMapping(glutGetWindow(), m); 

И вы можете связать (статический) эффективно WindowToMesh::draw функцию обратного вызова:

glutDisplayFunc(&WindowToMesh::draw); 

Когда вы будете готовы уничтожить Mesh, убедитесь, что вы находитесь в том же окне, а затем:

WindowToMesh::removeMapping(glutGetWindow()); 

В зависимости от других факторов, это может иметь смысл сделать двунаправленное отображение (так что вы можете найти вещи по Mesh *, а не только по окну int, или для сканирования грубой силы при редкой регистрации и т. д.

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

+0

Я думаю, что ваша функция 'register' должна быть переименована, так как это ключевое слово. (Я бы не видел этого, если бы не было синтаксического выделения на SO;)) – leemes

+0

@leemes - Gah. Благодарю. Получил коричневый бумажный пакет, который я мог бы взять? – AnthonyFoiani

+0

Теперь это хорошо. Мне нравится, как ваше решение может обрабатывать несколько окон. Моему решению не хватает этой функции ... – leemes

1

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

Есть возможность превратить указатель функции-члена в «нормальный» указатель на функцию. Но вы должны иметь в виду, что эта информация (экземпляр от Mesh) должна быть доступна после вызова этой функции. Это означает, что типичное преобразование из указателя функции-члена в «нормальный» указатель функции добавляет параметр (экземпляр).

Но glutDisplayFunc() принимает void(*)(void) указатель на функцию а, то есть список параметров пустой, поэтому он не будет говорить вызываемая функция ничего. Вы должны убедиться, что как только переданная функция будет вызвана, вы можете как угодно восстановить «контекст», то есть экземпляр Mesh.

Это можно сделать, указав экземпляр Mesh из глобальной точки вашего кода. Он всегда будет использоваться, когда вы вызываете вспомогательную функцию, которую мы собираемся написать. Поэтому, когда вы передаете указатель на glutDisplayFunc(), наша вспомогательная функция знает, для чего использовать Mesh, как только мы сказали, что он должен использовать.

class Mesh 
{ 
public: 
    void draw() { ... } 
} 

Mesh *instance; 

// Helper function called by glut: 
void display() { 
    // Now we can access a particular instance: 
    instance->draw(); 
} 

int main(int argc,char* argv[]) 
{ 
    Mesh mesh; 

    // First point to this instance globally: 
    instance = &mesh; 

    // Then, pass our helper function which now has an empty parameter list: 
    glutDisplayFunc(&display); 
} 

У этого решения есть ограничение. Вы не можете повторно использовать один и тот же указатель функции с разными экземплярами. Таким образом, вы не можете включить любой экземпляр в указатель функции, но только один для каждого указателя экземпляра, определенного глобально. Но поскольку вы просто хотите передать свою функцию розыгрыша до glutDisplayFunc, вам нужно всего лишь сделать это раз, так что все должно быть хорошо. Я просто не хочу держать эту «опасность» от вас. ;)

Смежные вопросы