2011-01-21 1 views
2

J'écris un jeu pour l'iPhone. Presque tout le code est écrit en C++. Maintenant, je voudrais créer un nouveau thread en utilisant NSThread (je veux utiliser le runLoop).iPhone: démarrage d'un NSThread à partir d'un objet C++

Il est possible de mélanger l'objectif C et C++ si le code est écrit dans un fichier .mm, ce que j'ai fait.

Le problème est que pour la création d'un NSThread

NSThread* myThread = [[NSThread alloc] initWithTarget:self selector:@selector(workerThreadFunction:) object:nil]; 
[myThread start]; 

J'ai besoin de passer « soi » qui (pour autant que je le comprends) un objectif-C « id » - et un objet C++ n'est pas un objet objectif-c. Donc, y a-t-il un moyen d'utiliser NSThread dans une application C++ ou suis-je obligé d'utiliser pthreads?

Merci!

+0

Vous n'avez pas besoin de passer soi-même, vous pouvez passer un objet-c objectif. –

+0

cela signifie que je ne peux pas avoir la fonction threadMain être définie comme une fonction de membre de mon objet C++? – Mat

+0

non, mais vous pouvez créer un objet objectif-c factice que vous initialisez avec votre objet C++, qui vous rappelle –

Répondre

0

Vous ne pouvez pas dériver des classes C++ à partir de classes Objective C et vice versa. Concevez un objet ObjC de wrapper qui instancie et appelle (mais n'en hérite pas) le C++. Ceci est généralement appelé "confinement et délégation". Ou vous pouvez utiliser des threads POSIX au lieu de NSThread.

+0

Je n'ai pas l'intention d'hériter de NSThread-i d'instancier un objet NSThread avec un workerThreadFunction comme indiqué dans la question – Mat

+0

Je voulais dire - vous ne pouvez pas dériver une classe C++ de NSObject. Ce qui est nécessaire pour qu'une paire id/selector existe. –

2

puis-je en quelque sorte créer un objet Objective-C qui - dans son constructeur - prend un C++ pointeur de fonction comme un argument qu'il appelle plus tard?

Sure. Comme ceci:

//The C++ base 
class ThreadBase 
{ 
    virtual void Run() = 0; 
}; 

typedef ThreadBase * (*ThreadCreator)(); 

//The ObjC wrapper 

@interface ThreadStarter:NSObject 
{ 
    ThreadCreator TheCreator; 
} 

-(void)Run; 
-(id)init:(ThreadCreator)tc; 
@end 

@implementation ThreadStarter 
-(id)init:(ThreadCreator)tc 
{ 
    TheCreator = tc; 
    return [super init]; 
} 

-(void)Run 
{ 
    (*TheCreator)()->Run(); 
} 
@end 

// 
class MyThread: public ThreadBase 
{ 
//... 
}; 

ThreadBase *MyThreadCreator() 
{ 
    return new MyThread(); 
} 

//And finally usage 

ThreadStarter *tc = [[ThreadStarter alloc]init:MyThreadCreator]; 
NSThread* myThread = [[NSThread alloc] initWithTarget:tc selector:@selector(Run) object:nil]; [myThread start]; 

Il est paramétré par une fonction de créateur parce que vous le vouliez. Mais vous pouvez aussi paramétrer par classe.

+0

En fait, je voulais dire un pointeur de fonction à une fonction membre que je veux utiliser comme fonction de thread (pas un pointeur de fonction à une fonction retournant un objet "ThreadBase"). Quoi qu'il en soit, votre version précédente semblait assez bonne mais j'ai le problème avec ceci: '- (void) execute {obj-> func (arguments);}' parce que obj est de type void * – Mat

+1

Voilà le problème avec les pointeurs de fonction membres - ils Cela n'a de sens que si vous savez de quelle classe viennent-ils (ou utilisez un typage très moche). Veuillez ne pas mélanger les pointeurs de fonction et les pointeurs de fonctions membres - ils sont très différents. Ce n'est pas ObjC, où vous pouvez appeler une méthode sur un objet sans connaître sa classe. Je recommande toujours une classe de base avec une méthode abstraite pour la paramétrisation - c'est la méthode C++. –

0

Pour un C++ - le style de reformulations ma réponse précédente:

//The C++ base 
class ThreadBase 
{ 
    virtual void Run() = 0; 
}; 

//The ObjC wrapper 

@interface ThreadStarter:NSObject 
{ 
    ThreadBase *TheThread; 
} 

-(void)Run; 
-(id)init:(ThreadBase)tb; 
@end 

@implementation ThreadStarter 
-(id)init:(ThreadBase)tb 
{ 
    TheThread = tb; 
    return [super init]; 
} 

-(void)Run 
{ 
    TheThread->Run(); 
} 
@end 

// 
class MyThread: public ThreadBase 
{ 
    //...   
}; 

//And finally usage 

MyThread *Thread = new MyThread(); 
ThreadStarter *tc = [[ThreadStarter alloc]init:Thread]; 
NSThread* myThread = [[NSThread alloc] initWithTarget:tc selector:@selector(Run) object:nil]; 
[myThread start]; 

beaucoup plus propre de cette façon, à mon humble avis. Si vous insistez pour que la routine de démarrage de fil est une fonction arbitraire dans une classe atrbitrary pas de votre création, voici votre réponse:

class MyThread: public ThreadBase, public MyOtherObject 
{ 
    void Run() 
    { 
     DoWork(); //Function defined in MyOtherObject 
    } 
} 

Vous voyez, si vous voulez paramètrer par fonction membre, vous devez paramètrer par la fonction et la classe. Et la classe ne peut pas être une variable d'exécution en C++.

1

"est-ce que je peux en quelque sorte créer un objet objectif-C qui - dans son constructeur - prend un pointeur de fonction C++ comme argument qu'il appellera plus tard?"

Oui, mais c'est plutôt moche.

D'abord, définissez un baseclass de rappel et une version basée sur un modèle. Cela peut aller dans un fichier générique .h qui peut être #includ éd à partir de fichiers .cpp et .mm. définir également une classe CThread de base:

// Thread.h 
#pragma once 
#include <objc/objc.h> // for a cpp compatible definition of id 
class Callback{ 
public: 
    virtual void operator()(void)=0; 
}; 

template<class T> 
class ClassCallback : public Callback { 
    T* _classPtr; 
    typedef void(T::*fncb)(void); 
    fncb _cbProc; 
public: 
    ClassCallback(T* classPtr,fncb cbProc):_classPtr(classPtr),_cbProc(cbProc){} 
    virtual void operator()(void){ 
    (_classPtr->*_cbProc)(); 
    } 
}; 

class CThread { 
    id _thread; 
public: 
    void Start(Callback* cb); 
}; 

Ensuite, mettre la mise en œuvre de CThread, et son objet copain obj-c, dans un.mm fichier:

// Thread.mm 
#import <Cocoa/Cocoa.h> 
#import "Thread.h" 

@interface ThreadStarter:NSObject 
{ 
    Callback *theCallback; 
} 

-(void)Run; 
-(id)init:(Callback*)cb; 
@end 

@implementation ThreadStarter 
-(id)init:(Callback*)cb 
{ 
    theCallback = cb; 
    return [super init]; 
} 

-(void)Run 
{ 
    theCallback(); 
} 
@end 

void CThread::Start(Callback* cb){ 
    _thread = [[ThreadStarter alloc] init:cb]; 
    [NSThread detachNewThreadSelector:@selector(Run) 
         toTarget:_thread 
        withObject:nil]; 
    } 
}; 

Et puis, dans le fichier cpp de votre application, vous pouvez:

// MyClass.cpp (doesn't even have to be .mm) 
#include "Thread.h" 

class MyClass { 
    CThread _myThread; 
    void SomeArbMethod(){ 
    } 
public: 
    MyClass(){ 
    _myThread.Start(new ClassCallback<MyClass>(this,&MyClass::SomeArbMethod)); 
    } 
}; 
Questions connexes