Passing reference of C++ class to an Objective C interface - ios

I have written a Objective C++ wrapper so that I will be able to access the C++ methods in Swift.
The wrapper has a method which returns a C++ class reference.
IECMServices.h (C++ class)
namespace ECMServices
{
class IECMServices
{
public:
virtual DataServices::IParameters& getDeviceInformation(ECMServicesTypes::UINT_16 moduleID) = 0;
};
}
IECMServicesWrapper.mm (Objective C++ class)
I am getting the above error while writing the Wrapper code.
IParameters is a C++ class
OParameters is Objective C++ equivalent class for IParameters
I want to return reference of OParameters to Swift.
For that I want to pass reference of C++ class to my oParametersRef
oParametersRef = oCECMServicesProviderObj.getECMServices().getDeviceInformation(0);
"oCECMServicesProviderObj.getECMServices().getDeviceInformation(0);"
this returns reference of IParameters which is a C++ class.
So that i can access methods of IParameters(C++) in swift via OParameters(Objective C++)
I want to pass reference of C++ to Objective C++ to Swift.

Search this community site, there are many answers that address this problem. See, for example, this: How to use Superpowered lib in Swift project
The specific error message in the image is due to the fact that getDeviceInformation() returns a reference to IParameters, which is assigned to an incompatible type OParameters*, i.e. a pointer to OParameters. The code of OParameters is not given, but I would guess that converting an IParameters reference to an OParameters pointer would be tricky and unnecessary. Anyway, your OParameters class evidently does not provide such a conversion.
As you can see in the aforementioned answer, it is impossible to expose C++ code to Swift directly, and IParameters is a C++ class. Objective-C++ allows you to mix Objective-C and C++, but only the Objective-C portion of the code can be exposed to Swift through headers. So, you will need to create an Objective-C class that is populated based on the contents of an IParameters instance and returned to Swift. In addition, if you want changes made in Swift to be visible in C++, you would need to keep a pointer or a reference to IParameters in the wrapper .mm file, but NOT in any headers visible to Swift, and have setters, visible to Swift, that can be used to modify the IParameters state.
Here is a quick and dirty example. The focus here is only on sharing IParams between C++ and Swift. Things like memory management are out of scope here. For simplicity, let us say our IParams and EMC C++ classes look as follows:
class IParams {
public:
int32_t param1;
int32_t param2;
};
class ECM
{
public:
static ECM * getInstance();
// It's very important that a reference is returned here. This
// allows us to store the pointer to the referenced IParams in a
// member of OParams, and the OParams' getters and setters can then
// directly access the IParams object returned here by reference.
IParams& getDeviceInfo();
...
};
Here are their wrapper implementations in Objective-C++ (.mm) code, which refer to C++ code directly, but it's OK as this is hidden from Swift:
#implementation OParams
{
IParams * pIParams;
}
-(id)init:(IParams*)pip
{
pIParams = pip;
return self;
}
// These getters and setters directly access the memory of the IParams
// object pointed to by pIParams.
-(int32_t)getParam1 { return pIParams->param1; }
-(int32_t)getParam2 { return pIParams->param2; }
-(void)setParam1:(int32_t)p { pIParams->param1 = p; }
-(void)setParam2:(int32_t)p { pIParams->param2 = p; }
#end
#implementation ECMWrapper
-(OParams*)getDeviceInfo
{
// We're returning a pointer to an OParams instance whose pIParams
// member points to the IParams object the reference to which is
// returned from ECM::getDeviceInfo()
return [[OParams alloc] init:(&ECM::getInstance()->getDeviceInfo())];
}
#end
Finally, here are the declarations that are visible to Swift, directly or indirectly, via the bridging header:
#interface OParams : NSObject
-(int32_t)getParam1;
-(int32_t)getParam2;
-(void)setParam1:(int32_t)p;
-(void)setParam2:(int32_t)p;
#end
#interface ECMWrapper : NSObject
-(OParams*)getDeviceInfo;
#end
Note that no C++ types or methods are mentioned here, so this code can be used in Swift.

Related

Why is instancetype used?

Can someone please explain to me (in simple terms) why an instancetype is used in Objective-C?
- (instancetype) init {
self = [super init];
if (self) {
// Custom initialization
}
return self;
}
It's to increase type safety.
Back in the old days, initialisers just returned an object of type id (any object).
With normal initialisers (those that begin with "init", "alloc" or "new"), this wasn't usually a problem. The compiler would automatically infer the type that it returned and therefore restrict any method calls on the object to the instance methods of that class.
However, this was a problem with static convenience initialisers or "factory methods" that didn't necessarily follow the same naming convention - therefore it was unable to apply the same type safety.
This means that with a class like this:
#interface Foo : NSObject
+(id) aConvenienceInit;
#end
The compiler would accept code like this:
NSArray* subviews = [Foo aConvenienceInit].subviews;
Why? Because the returned object could be any object, so if you try and access a UIView property - there's no type safety to stop you.
However, now with instancetype, the result you get back is of type of your given instance. Now with this code:
#interface Foo : NSObject
+(instancetype) aConvenienceInit;
#end
...
NSArray* subviews = [Foo aConvenienceInit].subviews;
You'll get a compiler warning saying that the property subviews is not a member of Foo*:
Although it's worth noting that the compiler will automatically convert the return type from id to instancetype if your method begins with "alloc", "init" or "new" - but nonetheless using instancetype wherever you can is a good habit to get into.
See the Apple docs on instancetype for more info.
Imagine two classes:
#interface A : NSObject
- (instancetype)init;
#end
#interface B : A
#end
The init method from A is inherited to B. However, in both classes the method has a different return type. In A the return type is A and in B the return type is B.
There is no other way to declare the return type for initializers correctly. Note that most programming languages with classes don't even have return types for constructors, therefore they completely avoid the issue.
This is the reason why Obj-C needs instancetype but of course it can be used outside initializers, too.
It is important to use instancetype instead of id in Objective-C if you are also using this code in Swift. Consider the following class declaration:
#interface MyObject : NSObject
+ (id)createMyObject;
- (void)f;
#end
If you want to create a MyObject instance in Swift 5.3 with createMyObject and then call f for this object, you will have to do the following:
let a = MyObject.createMyObject()
(a as? MyObject)?.f()
Now replace id with instancetype in MyObject to have the following Swift code:
let a = MyObject.create()
a?.f()
As you can see now, you can use MyObject.create() instead of MyObject.createMyObject(). And you don't need to use (a as? MyObject) since a is defined as MyObject? and not as Any.

swift: Equivalent objective-c runtime class

What is equivalent swift code for below Objective-C code. I couldn't find swift topic with runtime concept.
#import <objc/runtime.h>
Class class = [self class];
Trying to get class object of self?
Update:
Tried with below code, got error as 'UIViewController.type' doesn't conform to protocol 'AnyObject'
var klass: AnyClass = object_getClass(self)
Note: Found this post, but wouldn't helped.
First, it's hard to translate that code to Swift without knowing what you used that class object for in Objective-C.
In Objective-C, class objects are objects, and the type Class can hold a pointer to any class object. However, when Objective-C APIs are bridged to Swift, the type Class is converted to AnyClass! in Swift, where AnyClass is defined as AnyObject.Type. Types in Swift are not objects, and thus are not directly equivalent to class objects in Objective-C. However, if you intend to use an Objective-C API from Swift, it will have been bridged to expect AnyClass anyway, so you have to pass a type. You can get the type of any expression using .dynamicType; for example:
self.dynamicType
(If you really want to get the class object as an Swift object the same way as in Objective-C, and not as a Swift type, there are some convoluted ways to do that too.)
However, your description of your problem reveals another issue. If you just want to get the type of an object, and self is an object, then var klass: AnyClass = object_getClass(self) should have worked, since object_getClass() takes an AnyObject and returns an AnyClass. The only explanation for it not working is if self is not an object. Your error message reveals that, indeed, self is a type, not an object.
self is a type if this code is running in a class method. You should have really given context for your code (obviously, you didn't put Class class = [self class]; at the top level of a file), because taken out of context it's easy to misunderstand. In Objective-C Cocoa, there are two very different methods named class: an instance method, -class, which returns the class of the object, and a class method, +class, which simply returns the (class) object it's called on. Since your code is in a class method, in Objective-C, self points to a class object, and [self class] runs the class method +class, which just returns the object it's called on. In other words, [self class] is exactly identical to self. You should have just written self all along, but didn't realize it.
So the answer is that the Objective-C should have been
Class class = self;
and similarly the Swift should be
var klass: AnyClass = self
In Swift 3, self.dynamicType (and dynamicType in general) has been removed.
You now use:
type(of: self)
var klass: AnyClass = object_getClass(self)
NSStringFromClass(klass)

I there a way, to view the exposed Objective-C header to swift

I have problems to understand, why some member functions from an imported (and complicated) set of Objective-C interface are not available in Swift.
I have a Bridging-Header File:
#import "EvernoteSDK.h"
and I can't use some member functions in my ViewController
let userStore = EvernoteUserStore()
userStore.initWithSession(session)
initWithSession is not available for the swift code, but why?
The objective-C header shows:
#interface EvernoteUserStore : ENAPI
+ (instancetype)userStore;
- (id)initWithSession:(EvernoteSession *)session;
If I could view the exposed Objective-C header, I may understand, how the mangling works
In Swift the initializer call is combined with the constructor. In other words, Objective-C's
EvernoteUserStore *userStore = [[EvernoteUserStore alloc] initWithSession:session];
becomes
let userStore = EvernoteUserStore(session:session);
The tool recognizes the initWithSomething: name of Objective-C, and converts it to
init(something something : SomeType)
In case of EvernoteUserStore the corresponding init method looks like this:
init(session session: EvernoteSession!)

Declare public static variable in objective c [duplicate]

This question already has answers here:
Objective-C: how to declare a static member that is visible to subclasses?
(3 answers)
Closed 8 years ago.
I want a static varibale in .h of a class and want it to be inherited to its child class.
#interface :UIViewController
static bool isSearchWindowOpen ; //something like this.
#end
If i write like :
static bool isSearchWindowOpen ;
#interface :UIViewController
#end
it works fine but cannot be inherited by child classes.
pls suggest.
This sounds a bit like you are confusing this with some other programming language, like C++. In Objective-C, just like C, a static variable is a variable with file scope. If you declare a static variable in a header file, then any source file including that header file has its own copy of the static variable.
You'd probably want a class method
+ (BOOL)isSearchWindowOpen
with implementation
static BOOL sSearchWindowOpen;
+ (void)setSearchWindowOpen:(BOOL)open { sSearchWindowOpen = open; }
+ (BOOL)isSearchWindowOpen { return sSearchWindowOpen; }
Probably even better to write code that checks whether the search window is open, instead of relying on a static variable that you have to track correctly all the time.
The variable as declared has nothing to do with the class. It’s a global static in the “C sense”: only code in the same file can access it. (See Wikipedia for details.) You can write class accessors:
static BOOL foo;
+ (void) setFoo: (BOOL) newFoo
{
foo = newFoo;
}
That way the class descendants can access the variable, too. But it’s not a good idea anyway. What problem are you trying to solve?

how do I make an objective-c delegate for a cpp class?

I'm stuck trying to combine openGL-es (xcode openGL game template with the ogles2tools library from powervr 3.0 sdk. My problem is the line of code where I load the effect file:
/*
Load the effect.
We pass 'this' as an argument as we wish to receive callbacks as the PFX is loaded.
This is optional and supplying NULL implies that the developer will take care
of all texture loading and binding to to the Effect instead.
*/
if(m_pEffect->Load(*m_pEffectParser, "Effect", c_szPfxFile, NULL, uiUnknownUniforms, &error) != PVR_SUCCESS)
{
NSLog(#"%s",error.c_str());
return;
}
I'm supposed to pass a "this" pointer so I can receive the callbacks. The delegate method I need to implement is:
EPVRTError OGLES2IntroducingPFX::PVRTPFXOnLoadTexture(const CPVRTStringHash& TextureName, GLuint& uiHandle, unsigned int& uiFlags)
{
/*
This is an optional callback function for PVRTPFXEffect and can be used to automate
the texture loading process.
If multiple effects are to be loaded and they share textures it would be
prudent to have a caching system in place so texture memory is not wasted.
Please see OGLES2MagicLantern for an example of this.
*/
if(PVRTTextureLoadFromPVR(TextureName.String().c_str(), &uiHandle) != PVR_SUCCESS)
return PVR_FAIL;
return PVR_SUCCESS;
}
I guess the big issue for me is how do I go about providing a cpp delegate method in objective-c? I did some reading on this issue, but it seemed what I was reading was going the other way. That is, an objective-c delegate in cpp. It's pretty confusing, but here's my thought...
I create a cpp class the implements the method I need. I add that to my viewController class and pass the pointer to this cpp class in the m_pEffect->Load call. Does this seem correct?
Thanks.
P.S. Sorry if my code formatting is bad. I'm still learning.
Edit: Here's the example I found regarding mixing the objective-c and cpp. It seems really similar to what I want to do.
Update: Here's some additional info (requested by user1118321)
The CPP class which needs a delegate is CPVRTPFXEffect (PVRTPFXParserAPI.h - from powerVR SDK 3.0). I would add a link, but I'm not sure if this is allowed. Here's a link to the class header, but this version (and others on the web) did not include the pDelegate attribute for the load method. I'm assuming they are examples of a previous version. Let me know if it's okay to post this class file and I will do so.
I found a good example of what I think I'm supposed to do from reading this thread. So here's what I have so far:
My CPP delegate class...
class myCppDelegate : public PVRTPFXEffectDelegate {
public:
myCppDelegate() {};
EPVRTError PVRTPFXOnLoadTexture(const CPVRTStringHash& TextureName, GLuint& uiHandle, unsigned int& uiFlags) {
return PVR_FAIL;
};
};
My Obj-C wrapper class (just modified from the example link above)...
struct RNWrapOpaque;
#interface RNWrap : NSObject {
struct RNWrapOpaque *_cpp;
}
- (id)init;
#end
implementation...
#import "RNWrap.h"
#import "Wrap.h"
#interface RNWrap ()
#property (nonatomic, readwrite, assign) RNWrapOpaque *cpp;
#end
#implementation RNWrap
#synthesize cpp = _cpp;
struct RNWrapOpaque
{
public:
RNWrapOpaque() : wrap() {};
myCppDelegate wrap;
};
- (id)init
{
self = [super init];
if (self != nil)
{
self.cpp = new RNWrapOpaque();
}
return self;
}
- (void)dealloc
{
delete _cpp;
_cpp = NULL;
// [super dealloc];
}
#end
Basically I am able to compile the code and debug, but when the the CPVRTPFEffect class makes this call:
if(pDelegate->PVRTPFXOnLoadTexture(pTexDesc->FileName, uiHandle, uiFlags) != PVR_SUCCESS)
I get EXC_BAD_ACCESS. I'm assuming it's not finding my callback method, because I set a breakpoint and the line never gets called.
Here's my updated code which calls CPVRTPFXEffect::Load using a bridge command for the delegate parameter.
if(m_pEffect->Load(*m_pEffectParser, "Effect", c_szPfxFile,(__bridge myCppDelegate*)opaqueCppWrap, uiUnknownUniforms, &error) != PVR_SUCCESS)
Thanks for your help!
Update 2: The project uses ARC. Here's what my viewController interface looks like:
#interface ViewController : GLKViewController {
...
RNWrap* opaqueCppWrap;
...
}
#property (strong) RNWrap *opaqueCppWrap;
Adding the #property didn't help with the EXC_BAD_ACCESS. I'm not sure how to "see" the value of pDelegate when I'm tracing the CPP code. Xcode doesn't reveal anything when I hover over the variable.
I added the following line of code to the CPVRTPFXEffect::Load method (just prior to the line where it crashes):
*pReturnError += PVRTStringFromFormattedStr("Here is your class typeid: %s.\n", typeid(pDelegate).name());
return PVR_FAIL;
This is what displayed in the debug output window:
Here is your class typeid: P21PVRTPFXEffectDelegate.
I'm not sure what the "P21" means (if anything), but it looks like I'm close to getting this working. I dunno, maybe this is as close as it gets. Still crashing and not finding my method.
First, you may want to look at the last article in the series on wrapping C++. Most of it has gotten much simpler in the latest versions of clang. You probably don't need half this code anymore. ObjC++ objects can now have private C++ properties without any tricks, while maintaining a pure-ObjC interface.
Here is how you want to think about this problem:
Build a C++ object that is the delegate. Write all the code involved in setting up the delegation, etc, in C++. So when it says "pass a this pointer" you should really be passing a this pointer (because you should be doing this in the C++ code). The fact that you're doing a _bridge cast in a C++ call is a real hint something is going wrong.
Let an ObjC own the C++ object as a property.
Write the delegate callbacks in C++ inside the C++ object. If useful, you can let the C++ object then make calls into the ObjC object as needed, but it may be easier if the C++ object does all the delegate work.
I finally got this working, but had to remove the obj-c wrapper class from my viewController in order to do so. Here's what the code looks like:
ViewController.h
struct Opaque;
#interface ViewController : GLKViewController {
...
//RNWrap* opaqueCppWrap; // this didn't work
struct Opaque *opaqueCpp; // try this
...
}
ViewController.mm
// declare the Opaque structure
struct Opaque {
public:
Opaque() : cppobject() {};
myCppDelegate cppobject;
};
viewDidLoad
// ... create opaque member on initialization
opaqueCpp = new Opaque();
//opaqueCppWrap = [[RNWrap alloc] init]; // old way of doing things using wrapper
pass the delegate to the Load method
// old way using bridge cast and wrapper
//if(m_pEffect->Load(*m_pEffectParser, "Effect", c_szPfxFile,(__bridge myCppDelegate*)opaqueCppWrap, uiUnknownUniforms, &error) != PVR_SUCCESS)
// this works...
if(m_pEffect->Load(*m_pEffectParser, "Effect", c_szPfxFile, (myCppDelegate*)opaqueCpp, uiUnknownUniforms, &error) != PVR_SUCCESS)
Not sure why the wrapper class doesn't work, but I'm happy that my callback is working (appy no crashy!)
Phew, that was rough. Any thoughts/comments?

Resources