How can I call an Objective C instance method from a c++ class? In TestApp.cpp I would like to call updateUI in TestDelegate.mm
TestDelegate.h
#include "cinder/app/CinderView.h"
#include "TestApp.h"
#import <Cocoa/Cocoa.h>
#interface TestDelegate : NSObject <NSApplicationDelegate>
{
IBOutlet CinderView *cinderView;
IBOutlet NSWindow *window;
TestApp *mApp;
}
#property (assign) IBOutlet NSWindow *window;
- (IBAction)subdivisionSliderChanged:(id)sender;
- (void)updateUI;
#end
TestDelegate.mm
#include "cinder/Cinder.h"
#import "TestDelegate.h"
#implementation TestDelegate
#synthesize window;
- (void)dealloc
{
[super dealloc];
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
mApp = new TestApp;
mApp->prepareLaunch();
mApp->setupCinderView( cinderView, cinder::app::RendererGl::create() );
mApp->launch();
}
- (void)updateUI
{
//Set new values...
}
#end
TestApp.h
#pragma once
#include "cinder/app/AppCocoaView.h"
class TestApp : public cinder::app::AppCocoaView {
public:
void setup();
void draw();
};
TestApp.cpp
#include "TestApp.h"
#include "cinder/gl/gl.h"
using namespace ci;
using namespace ci::app;
void TestApp::setup()
{
//Set values
//Call updateUI method in TestDelegate.mm
}
void TestApp::draw()
{
}
Something like the following ought to work:
TestDelegate.mm
#include "cinder/Cinder.h"
#import "TestDelegate.h"
#implementation TestDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// mApp = new TestApp;
// mApp->prepareLaunch();
// mApp->setupCinderView( cinderView, cinder::app::RendererGl::create() );
// add the following line
mApp->m_TestDelegate = self;
// mApp->launch();
}
#end
TestApp.h
#pragma once
#include "cinder/app/AppCocoaView.h"
#class TestDelegate;
class TestApp : public cinder::app::AppCocoaView {
public:
void setup();
void draw();
TestDelegate *m_TestDelegate;
};
TestApp.cpp -> renamed to TestApp.mm
#include "TestApp.h"
#include "cinder/gl/gl.h"
#import "TestDelegate.h"
using namespace ci;
using namespace ci::app;
void TestApp::setup()
{
//Set values
//Call updateUI method in TestDelegate.mm
[this->m_TestDelegate updateUI];
}
Note: this code was written in a browser, and the Objective-C++ stuff I've done doesn't use ARC, so if it gives any warnings/errors, let me know and I'll update the code accordingly.
To call an instance method, you need an instance. Once your C++ code has a pointer to an instance of the class, you can just change the file to Objective-C++ and send a message like normal.
Related
I am looking for help creating a sprite in a different class and file cocos2d-x. I have used the code from the cocos2d-x website for subclassing a sprite and other posts and It would not work for me. I have followed the sonar systems tutorial for making a separate class for a "bird" sprite ("Player" in my case) and am having trouble with an error when trying to pass the helloworld layer through the Player class constructor that takes a parameter of a layer. the error says: "'Player::Player(const Player &)': cannot convert argument 1 from 'HelloWorld *const ' to 'const Player &' ApocalypseWorld c:\cocosprojects\apocalypseworld\classes\helloworldscene.cpp 39"
here is the code:
Player.h:
#pragma once
#include "cocos2d.h"
class Player
{
public:
Player( cocos2d::Layer* layer );
private:
cocos2d::Sprite *player1;
};
Player.cpp:
#include "Player.h"
#include "cocostudio/CocoStudio.h"
#include "ui/CocosGUI.h"
USING_NS_CC;
Player::Player( cocos2d::Layer* layer )
{
player1 = Sprite::create("PlayerHead.png");
player1->setPosition(Point(200, 200));
layer->addChild(player1, 100);
}
HelloWorldScene.h:
#ifndef __HELLOWORLD_SCENE_H__
#define __HELLOWORLD_SCENE_H__
#include "cocos2d.h"
#include "Player.h"
class HelloWorld : public cocos2d::Layer
{
public:
// there's no 'id' in cpp, so we recommend returning the class instance pointer
static cocos2d::Scene* createScene();
// Here's a difference. Method 'init' in cocos2d-x returns bool, instead of returning 'id' in cocos2d-iphone
virtual bool init();
Player* player;
// implement the "static create()" method manually
CREATE_FUNC(HelloWorld);
};
#endif // __HELLOWORLD_SCENE_H__
HelloWorldScene.cpp:
#include "HelloWorldScene.h"
#include "Player.h"
#include "cocostudio/CocoStudio.h"
#include "ui/CocosGUI.h"
USING_NS_CC;
using namespace cocostudio::timeline;
Scene* HelloWorld::createScene()
{
// 'scene' is an autorelease object
auto scene = Scene::create();
// 'layer' is an autorelease object
auto layer = HelloWorld::create();
// add layer as a child to scene
scene->addChild(layer);
// return the scene
return scene;
}
// on "init" you need to initialize your instance
bool HelloWorld::init()
{
//////////////////////////////
// 1. super init first
if ( !Layer::init() )
{
return false;
}
auto rootNode = CSLoader::createNode("MainScene.csb");
addChild(rootNode);
player = new Player(this);
return true;
}
i get a little red squigly line under the t in:
player = new Player(this);
at the end of the HelloWorldScene.cpp file
Edited code:
Player.h:
#ifndef __Sample__Player__
#define __Sample__Player__
#include "cocos2d.h"
USING_NS_CC;
class Player : public Node
{
public:
static Player* createPlayer(Layer* layer);
Sprite* head;
};
#endif
Player.cpp:
#include "Player.h"
Player* Player::createPlayer(Layer* layer) {
auto ret = new (std::nothrow) Player;
if(ret && ret->init()) {
ret->autorelease();
ret->head = Sprite::create("PlayerHead.png");
ret->addChild(ret->head);
layer->addChild(ret, 100);
return ret;
}
CC_SAFE_RELEASE(ret);
return nullptr;
}
Then in your scene.h:
#include "Player.h"
And in your scene.cpp:
auto player = Player::createPlayer(this);
addChild(player);
Running into issues trying to mix c++ and objective-c when building cocoa app using Xcode4.
The issue is when I use NSTimer to call handleFrame function, which calls a virtual function of a class.
Here is what i am trying to do:
1. Create a monitor;
2. Create a handler;
3. Assign handler to monitor (init function)
4. Call monitor->update() that is expected to call handler's virtual method.
5. Code works as expected in applicationDidFinishLaunching function, however NSTimer is causing EXC_BAD_ACCESS exception in handleFrame.
//
// AppDelegate.h
// Concept5
//
#import <Cocoa/Cocoa.h>
#include "monitor.h"
#include "Derived.h"
#interface AppDelegate : NSObject <NSApplicationDelegate>
{
Monitor *monitor;`enter code here`
NSTimer *gameTimer;
}
#property (assign) IBOutlet NSWindow *window;
- (void)handleFrame:(NSTimer *)timer;
#end
AppDelegate implementation (.mm)
//
// AppDelegate.mm
// Concept5
//
#import "AppDelegate.h"
#implementation AppDelegate
- (void)dealloc
{
[super dealloc];
}
- (id) init {
self = [super init];
if(self) {
monitor = new Monitor();
}
return self;
}
- (void)handleFrame:(NSTimer *)timer {
monitor->update();
}
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
// Insert code here to initialize your application
Derived derived;
monitor->init(derived);
monitor->update();
gameTimer = [[NSTimer scheduledTimerWithTimeInterval:1
target:self
selector:#selector(handleFrame:)
userInfo:nil
repeats:YES] retain];
monitor->update();
}
#end
//
// Monitor.cpp
// Concept5
//
#include "Monitor.h"
void Monitor::init (Base& handler)
{
_handler = &handler;
}
void Monitor::update()
{
if (_handler != NULL)
{
_handler->speak(); // <-- EXC_BAD_ACCESS exception.
}
}
//
// Monitor.h
// Concept5
#ifndef __Concept5__Monitor__
#define __Concept5__Monitor__
#include <iostream>
#include "Base.h"
class Monitor
{
private:
Base* _handler;
public:
void init (Base& handler);
void update();
};
#endif /* defined(__Concept5__Monitor__) */
//
// Base.cpp
// Concept5
#include "Base.h"
void Base::speak()
{
std::cout << "Base speaks" << std::endl;
}
//
// Base.h
// Concept5
#ifndef __Concept5__Base__
#define __Concept5__Base__
#include <iostream>
class Base
{
public:
virtual void speak();
};
#endif /* defined(__Concept5__Base__) */
//
// Derived.cpp
// Concept5
#include "Derived.h"
void Derived::speak()
{
std::cout << "Derived speaks" << std::endl;
}
//
// Derived.h
// Concept5
//
#ifndef __Concept5__Derived__
#define __Concept5__Derived__
#include <iostream>
#include "Base.h"
class Derived : public Base
{
public:
void speak();
};
#endif /* defined(__Concept5__Derived__) */
I have never used Objective-C, but the following looks like an issue:
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
Derived derived;
monitor->init(derived);
//....
}
Since Derived is a local variable, the scope will not go beyond the applicationDidFinishLaunching function. Thus calling init() (which takes a pointer), will be holding onto an invalid object when the above function returns.
If this were C++, the solution is to make sure the lifetime of the object is sufficient. The usual solutions are:
1) Make the object global, or
2) Create the object dynamically using new, or
3) Create a smart pointer (probably std::shared_ptr) and use that instead of raw pointers.
I'm not an Objective-C expert, but EXC_BAD_ACCESS mean you are trying to access something with a bad pointer, likely a nil pointer.
Since your timer is calling an INSTANCE method in stead of a CLASS method on a Monitor instance, you better have a monitor instance when your timer fires. My guess is that you don't. If I was more of an Objective-C person, I could probably look at your code & see this, but as it is I would have to run your code to tell. But I'd bet that is what is wrong.
That all being said, it is a risky idea to call timers with instance methods, unless you are absolutely sure the instance will still be around & you know what you're doing. It is safer to only call class methods on a timer.
I know this has been asked a lot and I did implemented all I have created by looking around here and there.
I have all objective C files as .mm.
Also I can see in output console that "this = (a *) NULL" so the object used to call b(int x) function is null.
Here is my situation I am initialising c++ class from objective c++(.mm) class.
but I am getting BAD_ACCESS.
"a.cpp"
#include "a.h"
void a::b(int x) {
cout <<"hi";
c.push_back(x); // EXC_BAD_ACCESS error here.
cout<<c.size();
}
other files are as follow.
"a.h"
#ifndef __Cperiments__File__
#define __Cperiments__File__
#include <iostream>
#include <vector>
using namespace std;
class a {
public :
vector<int> c;
void b(int x);
};
#endif /* defined(__Cperiments__File__) */
AppDelegate.h
#import <UIKit/UIKit.h>
#import "ViewController.h"
#interface AppDelegate : UIResponder <UIApplicationDelegate>
#property (strong, nonatomic) UIWindow *window;
#property (strong, nonatomic) ViewController *viewController;
#end
AppDelegate.mm
#import "AppDelegate.h"
#implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:
[[UIScreen mainScreen] bounds]];
self.viewController = [[ViewController alloc]
initWithNibName:nil bundle:nil];
self.window.rootViewController = self.viewController;
[self.window makeKeyAndVisible];
return YES;
}
#end
ViewController.h
#import <UIKit/UIKit.h>
#import "a.h"
#interface ViewController : UIViewController
{
a* objA;
}
-(id) init;
#property(readonly,assign) a* objA;
#end
ViewController.mm
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
-(id)init
{
if ((self = [super init]))
{
objA = new a();
}
return self;
}
- (void)viewDidLoad
{
[super viewDidLoad];
objA->b(5);
}
#end
What else I am missing so that I can push some ints in vector.
Thanks
As pointed out by #borrrden in comments I never called init in appdelegate.mm.
replaced this line in appdelegate.mm
self.viewController = [[ViewController alloc]initWithNibName:nil bundle:nil];
with
self.viewController = [[ViewController alloc] init];
Thanks for quick help.
Schleife.h
#ifndef __CPlusPlusTest__Schleife__
#define __CPlusPlusTest__Schleife__
#include <iostream>
class Schleife;
#endif /* defined(__CPlusPlusTest__Schleife__) */
Schleife.cpp
#include "Schleife.h"
class Schleife
{
public:
int addition(int,int);
private:
int ergebnis;
};
int Schleife::addition(int a,int b)
{
ergebnis = a +b;
return ergebnis;
}
ViewController.h
#import <UIKit/UIKit.h>
#import "Schleife.h"
#interface ViewController : UIViewController
#end
ViewController.mm
#import "ViewController.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
Schleife *schleife = new Schleife();
// Do any additional setup after loading the view, typically from a nib.
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
#end
Whats Wrong?
I got the Error Message:
"ViewController.mm:22:31: Allocation of incomplete type 'Schleife'"
I don't understand what I did wrong. I want just implement C++ Code in my App. So I started with a easy example but it doesn't work...
Can you help me?
I know its maybe a easy Questions but I can't find the mistake...
Schleife.h should have this code instead of the cpp file
class Schleife
{
public:
int addition(int,int);
private:
int ergebnis;
};
you will also need to include #include "Schleife.h" in your ViewController.mm
I am getting an EXC_BAD_ACCESS error in my contact listener code. Below is the code:
Main object class (GameObjects) through which all objects are subclassed:
GameObjects.h:
#import "cocos2d.h"
#import "CCNode.h"
#import "CCPhysicsSprite.h"
#import "Box2D.h"
#include <math.h>
#interface GameObjects : CCNode {
//b2Body* objectBody_;
}
-(b2Body*)getObjectBody;
-(void)objectsTouched:(GameObjects*)otherObject;
#end
GameObjects.mm (for now I just want to CCLOG to tell if it's working):
#import "GameObjects.h"
#implementation GameObjects
-(b2Body*)getObjectBody {
}
-(void)objectsTouched:(GameObjects*)otherObject {
CCLOG(#"it's working");
}
#end
ContactListenerTest.h:
#import <Foundation/Foundation.h>
#import "cocos2d.h"
#import "Box2D.h"
#import "Enemy.h"
#import "Sprite.h"
#import "GameObjects.h"
class ContactListenerTest : public b2ContactListener {
public:
b2World* world;
void BeginContact(b2Contact* contact);
};
ContactListenerTest.mm:
#import "ContactListenerTest.h"
void ContactListenerTest:: BeginContact(b2Contact *contact)
{
b2Fixture *fixtureA = contact->GetFixtureA();
b2Fixture *fixtureB = contact->GetFixtureB();
b2Body *fixtureABody = fixtureA->GetBody();
b2Body *fixtureBBody = fixtureB->GetBody();
GameObjects* spriteObject = (GameObjects*)fixtureABody->GetUserData();
GameObjects* spriteObject2 = (GameObjects*)fixtureBBody->GetUserData();
[spriteObject objectsTouched:spriteObject2];
[spriteObject2 objectsTouched:spriteObject];
}
When I get the EXC_BAD_ACCESS error the following is printed in the console:
-[Enemy objectsTouched:]: unrecognized selector sent to instance 0x8558840
Enemy is one of the subclasses of GameObjects.
Verify that the userdata object actually is of GameObjects class:
NSAssert1([spriteObject isKindOfClass:[GameObjects class]],
#"userdata %# not a game object", spriteObject);
NSAssert1([spriteObject2 isKindOfClass:[GameObjects class]],
#"userdata 2 %# not a game object", spriteObject2);
When you initialize the GameObject you should make sure that the UserData of the created body points to your GameObject. Something like this:
objectBody_->SetUserData(self);