Mat
Mat

Reputation: 111

iPhone: starting an NSThread from a C++ object

I'm writing a game for the iPhone. Almost all the code is written in C++. Now I'd like to create a new thread using NSThread (I want to use the runLoop).

It's possible to mix objective-C and C++ if the code is written in a .mm file, which I did.

The problem is, that for creating a NSThread

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

I need to pass "self" which (as far as I understand it) an Objective-C "id" - and a c++ object is not an objective-c object.

So - is there a way to use NSThread in a c++ aplication or am I forced to use pthreads?

Thanks!

Upvotes: 3

Views: 2568

Answers (4)

Chris Becke
Chris Becke

Reputation: 36141

"can I somehow create an objective-C object which - in it's constructor - takes a c++ function pointer as an argument which it calls later?"

Yes, but its rather ugly.

First, define a callback baseclass, and a templated version. This can go into a generic .h file that can be #included from .cpp and .mm files. Also define a basic CThread class:

// 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);
};

Next, put the implementation of CThread, and its obj-c buddy object, in a .mm file:

// 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];
  }
};

And then, in your application's cpp file, you can:

// 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));
  }
};

Upvotes: 1

Seva Alekseyev
Seva Alekseyev

Reputation: 61398

For a C++-style rephrasing of my previous answer:

//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];  

Much cleaner this way, IMHO. If you're insist that the thread start routine is an arbitrary function in an atrbitrary class not of your creation, here's your answer:

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

You see, if you want to parametrize by member function, you have to parametrize by both function and class. And class cannot be a run-time variable in C++.

Upvotes: 0

Seva Alekseyev
Seva Alekseyev

Reputation: 61398

can I somehow create an objective-C object which - in it's constructor - takes a c++ function pointer as an argument which it calls later?

Sure. Like this:

//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];  

It's parametrized by a creator function because you wanted it so. But you can parametrize by class as well.

Upvotes: 2

Seva Alekseyev
Seva Alekseyev

Reputation: 61398

You cannot derive C++ classes from Objective C classes and vice versa. Design a wrapper ObjC object that would instantiate and call (but not inherit from) the C++ one. This is generally called "containment and delegation".

Or you can use POSIX threads instead of NSThread.

Upvotes: 0

Related Questions