3

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!

Mat
  • 111
  • 1
  • 5
  • You don’t need to pass self, you can pass any objective-c object. – Jeff Argast Jan 21 '11 at 20:13
  • so it means i can't have the threadMain function be defined as a memberfunction of my C++ object? – Mat Jan 21 '11 at 20:15
  • no, but you can make a dummy objective-c object that you initialize with your c++ object, that then calls you back – Chris Becke Jan 21 '11 at 20:19
  • 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? – Mat Jan 21 '11 at 20:24

4 Answers4

2

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.

Seva Alekseyev
  • 59,826
  • 25
  • 160
  • 281
  • i actually meant a functionpointer to a memberfunction which i want to use as my threadfunction (not a functionpointer to a function returning a "ThreadBase" object). Anyway - your previous version looked quite good but i have the problem with this: `- (void)execute{obj->func(arguments);}` because obj is of type void* – Mat Jan 21 '11 at 20:58
  • 1
    That's the problem with member function pointers - they only make sense if you know which class did they come from (or use a very ugly typecast). Please don't mix up function pointers and member function pointers - they're quite different. This is not ObjC, where you can invoke a method on an object without knowing its class. I still recommend a base class with an abstract method for parametrization - that's the C++ way. – Seva Alekseyev Jan 21 '11 at 21:21
1

"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));
  }
};
Mad Physicist
  • 107,652
  • 25
  • 181
  • 264
Chris Becke
  • 34,244
  • 12
  • 79
  • 148
0

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.

Seva Alekseyev
  • 59,826
  • 25
  • 160
  • 281
  • I don't intend to inherit from NSThread - i thoght of instanciating an NSThread object with a workerThreadFunction as shown in the question – Mat Jan 21 '11 at 20:16
  • I meant - you cannot derive a C++ class from NSObject. Which is necessary for an id/selector pair to exist. – Seva Alekseyev Jan 21 '11 at 20:27
0

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++.

Seva Alekseyev
  • 59,826
  • 25
  • 160
  • 281