I want to change the properties of some objects (Labels, Buttons..), I created using the Storyboard out of c++-code. So I need a way of running ViewController-class-internal methods.
Is there any proper way to do this? Is there another possibility?
I've tried using callbacks, but there is always this barrier between global and internal in the ViewController-class. Thanks in advance!
EDIT:
Since I don't know how to access a swift class out of c++ code, i cannot give any proper examples, but I thought of something like this (pseudo code):
In c++:
int main(){
say_hello();
}
and in Swift:
class ViewController: NSViewController {
#IBOutlet weak var label: NSTextField!
func say_hello(){
label.stringValue = "Hello"
}
}
Here is an oversimplified example of how this could be done using an Objective-C++ wrapper, as suggested by Richard. Memory management and thread safety aspects, and many other things, are not addressed here. In this example there is a 1-to-1 relationship between Swift and C++ class instances. Also, Swift object pointers are used as identifiers to decide which Swift object should receive a notification. This is kind of dangerous, see comments in the code below. Using more sophisticated data structures in the Objective-C++ wrapper to maintain a connection between Swift and C++ objects, one could easily work around this danger and support relationships other than 1-to-1.
First of all, here is a C++ class that triggers changes in Swift code:
typedef void (*cb_t)(const char *, void *);
class MyClassCPP {
public:
MyClassCPP(cb_t callBack, void * p) : myCallBack(callBack), clientPtr(p) {}
void doWork(); // perform some work and invoke the callback
private:
cb_t myCallBack;
void * clientPtr;
};
void MyClassCPP::doWork() {
myCallBack("C++ code at work...", clientPtr);
}
Here is an Objective-C++ wrapper interface that should be made visible to Swift code via the bridging header, directly or indirectly. Please note that it does not reference any C++ types.
#class SwiftClass; // forward declaration
// can't include *-Swift.h in a header
#interface OCWrapper : NSObject
-(instancetype)init:(SwiftClass * )sc;
-(void)requestWorkFromCPP;
#end
And here is the wrapper implementation. It does reference C++ types. We cannot provide a Swift global function as a callback to C++ code, but we can provide an Objective-C++ global function for this purpose.
// Extension that deals with C++ specifics that can't be visible to Swift
#interface OCWrapper ()
{
MyClassCPP * myClassCPP;
}
#end
void callBack(const char * msg, void * swiftClient)
{
// Danger: what if swiftClient does not point to a SwiftClass instance?
[(__bridge SwiftClass*)swiftClient sayHello:
[[NSString alloc] initWithBytes: msg length:strlen(msg)
encoding:NSASCIIStringEncoding]];
}
#implementation OCWrapper
-(instancetype)init:(SwiftClass * )sc
{
myClassCPP = new MyClassCPP(callBack, (__bridge void*)sc);
return self;
}
-(void)requestWorkFromCPP{
myClassCPP->doWork();
}
#end
The above should be in an Objective-C++ file. Create an Objective-C file and then rename it to have the .mm extension. You will also need to include the *-Swift.h header, so Objective-C++ can use Swift types.
Finally, here is some Swift code that uses the C++ code via the Objective-C++ wrapper:
// This is like your Swift view controller
class SwiftClass : NSObject
{
var label = "[Empty]"
var name : String;
init(name : String) {
self.name = name
}
func sayHello(greeting : String) {
label = "SwiftClass named " + name + " received greeting: " + greeting
}
}
...
let sc = SwiftClass( name : "Zero")
let ocWrapper = OCWrapper(sc)
let sc1 = SwiftClass( name : "One" )
let ocWrapper1 = OCWrapper(sc1)
ocWrapper1.requestWorkFromCPP()
print("The label value from C++: \(sc1.label)")
ocWrapper.requestWorkFromCPP()
print("The label value from C++: \(sc.label)")
...
Related
I'm just about to get into the basics of Swift, Objective-C & C++. I'm trying to build a bridge between Objective-C & Swift & set up a suitable delegate (MyDelegate).
The code below is working quite fine but I got some problems calling the Swift function callbackInteger() from a static function like:
MyFile.mm:
static void test() {
// how to call callbackInteger?
}
MyFile.mm:
- (void)callbackToSwift:(int)testInteger {
if (self.delegate != nil) {
[self.delegate callbackInteger: testInteger];
}
}
MyDelegate.h:
#protocol MyDelegate <NSObject>
- (void) callbackInteger: (int) testInteger;
#end
ViewController.swift:
class ViewController: UIViewController, MyDelegate {
func callbackInteger(_ testInteger: Int) {
print("testInteger: \(testInteger)");
}
}
Note: I really have no idea how to achieve a call to the callbackInteger function using the delegate call.
A protocol is nothing more that a set of requirements (methods) that a class has to implement. We say that a class conforms to a protocol.
So in your static function test(), you can't call the method of the protocol if you don't have an instance/object around (here a ViewController). A working way (but not necessary a beautiful one) would be to store somewhere (as a global variable for example) an instance of ViewController in order to reuse it in the function.
Something like this :
// Top of your file
#import <Foundation/Foundation.h>
// other headers...
id<MyDelegate> globalDelegate;
static void test() {
[globalDelegate callbackInteger:42];
}
// rest of your file
There are plenty of resources about protocols and the delegation pattern like this guide from Apple. Read carefully how they use it in Cocoa & Cocoa Touch.
I'm quite new to Swift and recently noticed that you cannot inherit from a generic in Swift, e.g.
class MyClass<T> : T {}
is not valid in Swift 3 (see question this question).
Here is the problem I was hoping to solve with the above construct:
protocol Backend {
func operationA(operand: Int)
}
class ConcreteBackend : Backend {
func operationA(operand: Int) {
// ...
}
// Some other functions and/or custom initializers
// ...
}
class EnhancedBackend<T : Backend> : T {
override func operationA(operand: Int) {
// do something smart here
super.operationA(operand: modifiedOperand)
}
}
Basically EnhancedBackend does something smart with the input of operationA and then passes it to the actual implementation of Backend.
I'm using inheritance here instead of composition, because ConcreteBackend might have some public properties, functions and initializers that are not specified in the protocol (because they are only related to the concrete implementation) that I want to also expose with EnhancedBackend.
Without inheritance this would not be possible.
A C++ implementation might look like
// Using concepts here instead of protocols
class ConrecteBackend {
public:
void operationA(int operand) { .... }
}
template<class T>
class EnhancedBackend : public T {
using Base = T;
public:
// Ensure T is a model of the Backend concept
static_assert(isModelOfConceptBackend<T>::value,
"Template parameter is not a model of concept Backend");
// Ensure all constructors of Base can be used
template<class ...Args, typename = std::enable_if_t<
std::is_constructible<Base, Args...>::value>>
inline EnhancedBackend(Args &&...args) : Base(std::forward<Args>(args)...) {}
void operationA(int operand) {
// ...
Base::operationA(operand);
}
};
So with C++ it's quite simple to solve the problem. But at the moment I have no clue how to implement in with (pure) Swift 3.
Swift's generics are not the same as C++ templates which are closer to pre processing macros than Swift's type semantics.
There are however a number of ways to achieve similar results. One way if to use variables to reference functions that need customized dispatching rules:
for example:
protocol Backend:class
{
var operationA:(Int) -> () { get set }
func performOperationA(_ : Int) -> ()
}
class ConcreteBackend : Backend
{
lazy var operationA:(Int) -> () = self.performOperationA
func performOperationA(_ : Int) -> ()
{
// ...
}
// Some other functions and/or custom initializers
// ...
}
extension Backend
{
var enhancedForTesting:Self
{
operationA = testing_OperationA
return self
}
func testing_OperationA(_ operand:Int) -> ()
{
let modifiedOperand = operand + 1
performOperationA(modifiedOperand)
}
}
let enhancedBackend = ConcreteBackend().enhancedForTesting
By using a variable to reference the function's implementation, it becomes possible to dynamically change the behaviour of operationA at runtime and for specific instances.
In this example, the enhancements are added to the Backend protocol but they could also have been set by an independent function or even another class that has its own particular kind of altered behaviour.
When using this approach, the instance has all the attributes and functions of the concrete class while implementing the enhanced behaviour for the altered functions of the protocol.
Creating the enhanced instance uses a syntax that is as simple as (if not simpler than) a generic class constructor:
// for example:
let instance = ConcreteBackend(...).enhanced
// rather than:
let instance = EnhancedBackend<ConcreteBackEnd>(...)
I created void type method in Objective-C to pass it to C++ method shown below:
C++ method to which Objectiv-C method will be passed:
glfwSetWindowSizeCallback(m_Window, windowResize);
Here is Objective-C method:
- (void) windowResize:(GLFWwindow *)window :(int)width :(int)height {glViewport(0, 0, width, height);}
I know it can be solved by adding C++ file to the project and linking it to .m file with C++ method, but i would like to implement it in Cocoa syntax
ObjC methods can be passed arround as selectors like
SEL method = #selector(windowResize:width:height:);
glfwSetWindowSizeCallback(m_Window, method);
Then applied inside glfwSetWindowSizeCallback. Alternately you could wrap the ObjC call in a c function and pass the function pointer.
A more complete example might be:
void func(NSApplication * target, SEL method) {
if ([target respondsToSelector: method] ) {
[target performSelector:method];
}
}
elsewhere:
- (void)applicationWillTerminate:(NSNotification *)aNotification {
SEL method = #selector(unhideAllApplications:);
func([NSApplication sharedApplication] , method);
}
USING BDS2006:
I'm trying to convert the Graphics32 Resampler_ex example in C++, but i can't even understand what happens in some codes, or how to rewrite that code in C++.
In that sample there's a combobox to choose what resampler to use:
This is the Deplhi code in his OnChange event:
procedure TfmResamplersExample.KernelClassNamesListClick(Sender: TObject);
var
Index: Integer;
begin
Index := KernelClassNamesList.ItemIndex;
if Src.Resampler is TKernelResampler then
with TKernelResampler(Src.Resampler) do
begin
Kernel := TCustomKernelClass(KernelList[Index]).Create;
LbParameter.Visible := (Kernel is TAlbrechtKernel) or
{$IFDEF Ex}
(Kernel is TGaussianKernel) or
(Kernel is TKaiserBesselKernel) or
(Kernel is TNutallKernel) or
(Kernel is TBurgessKernel) or
(Kernel is TBlackmanHarrisKernel) or
(Kernel is TLawreyKernel) or
{$ENDIF}
(Kernel is TSinshKernel);
gbParameter.Visible := LbParameter.Visible;
SetKernelParameter(Kernel);
CurveImage.Repaint;
end;
end;
where:
{ TClassList }
{ This is a class that maintains a list of classes. }
TClassList = class(TList)
protected
function GetItems(Index: Integer): TClass;
procedure SetItems(Index: Integer; AClass: TClass);
public
function Add(AClass: TClass): Integer;
function Extract(Item: TClass): TClass;
function Remove(AClass: TClass): Integer;
function IndexOf(AClass: TClass): Integer;
function First: TClass;
function Last: TClass;
function Find(AClassName: string): TClass;
procedure GetClassNames(Strings: TStrings);
procedure Insert(Index: Integer; AClass: TClass);
property Items[Index: Integer]: TClass read GetItems write SetItems; default;
end;
ResamplerList: TClassList;
My problems are on this line
Kernel := TCustomKernelClass(KernelList[Index]).Create;
How can i convert this line in C++?
EDIT AFTER THE COMMENTS AND THE ANWERS:
Ok, seems beyond my undertanding. For my purposes, it will suffice to be able to replicate what this code do without too much hassle.
Could it be possible to instantiate the right class just using a switch based on the itemindex?
These are the 4 classes i should instantiate:
class DELPHICLASS TNearestResampler;
class PASCALIMPLEMENTATION TNearestResampler : public Gr32::TCustomResampler
{
typedef Gr32::TCustomResampler inherited;
[...]
}
class DELPHICLASS TLinearResampler;
class PASCALIMPLEMENTATION TLinearResampler : public Gr32::TCustomResampler
{
typedef Gr32::TCustomResampler inherited;
[...]
};
class DELPHICLASS TDraftResampler;
class PASCALIMPLEMENTATION TDraftResampler : public TLinearResampler
{
typedef TLinearResampler inherited;
[...]
};
class DELPHICLASS TKernelResampler;
class PASCALIMPLEMENTATION TKernelResampler : public Gr32::TCustomResampler
{
typedef Gr32::TCustomResampler inherited;
[...]
};
I don't ever get how could i assign one of them to "Kernel"....
The Delphi code relies on Delphi virtual constructors. This functionality does not exist in C++.
If you wanted to translate the code literally then you'd need to find a way to instantiate the class by calling the virtual Delphi constructor. You cannot do that in C++ so you'd need some Delphi code to glue it all together. Remy's answer here shows how to use __classid() to obtain a Delphi metaclass. You'd then need to pass that metaclass to a Delphi function to perform the instantiation.
Frankly I would view that as being a rather convoluted solution to the problem. Instead I think I'd replace the class list with a function list. The functions in the list would have the task of instantiating a new instance of a kernel. So instead of adding a class to the list, you add a function that creates a new instance of the class. In fact you might want a map between name and function, it all depends on your needs.
From what I remember from Delphi programming, this will actually instantiate the same type of class, which currently is kept in KernelList[index] and then cast it back to TCustomKernelClass. AFAIK there is no such mechanism in C++, but you can solve it by introducing virtual CreateInstance method:
class TCustomKernelClass
{
public:
virtual TCustomKernelClass * CreateInstance() = 0;
}
class TDerivedKernelClass
{
public:
TCustomKernelClass * CreateInstance()
{
return new TDerivedKernelClass();
}
}
You'll have to introduce some changes in the classes though. I doubt it can be solved directly in C++.
Edit: In response to comments
Ok, I see now, there are class definitions kept in that list. Since RTTI in C++ is not as extensive as in Delphi, I'd change the code to (written from memory, may not compile):
std::vector<std::function<TBaseKernelClass(void)>> KernelList;
KernelList.push_back([]() { return new TDerivedKernelClass(); });
// (...)
Kernel = KernelList[index]();
Suppose I have two objective c++ objects that each wrap a native c++ object given:
A, B = objective c++ object types
Acpp, Bcpp = c++ object types
In B.mm
#import "Bcpp.h"
#import "B.h"
#interface B ()
{
Bcpp myBcpp; // declare instance c++ variable of type Bcpp
}
#end
In A.mm
#import "Acpp.h"
#import "A.h"
#interface A ()
{
Acpp myAcpp; // declare instance c++ variable of type Acpp
}
#end
#implementation A
// method to return an instance of B from an instance of A (self)
- (B)GetBfromA
{
Bcpp *bfroma = myAcpp.GetBfromA(); // return c++ object
// How do i find the objective C++ object B from its wrapped c++ instance bfroma?
}
#end
The reason for doing this is we have a mature c++ data structure and we wish to wrap it with objective c++ objects. Is the the best way? And if it is, how do we solve the reverse mapping problem?
EDIT: Thank you to the early responders but I have a more tricky situation that I implied above. Suppose the function GetBFromA() returns an instance of Bcpp that had already been declared (as an instance variable of an instance of B). So I am holding a pointer to a Bcpp object that is itself an instance variable of an objective C++ object of type B. How do I find the instance of B from the instance of Bcpp?
What you probably need to do is to be able to create a B from a Bcpp. So B will need to be amended to have an -initWithBcpp: method:
- (id)initWithBcpp:(Bcpp*)bcpp
{
self = [super init];
if (self != nil)
{
myBcpp = *bcpp;
}
return self;
}
Then, in GetBFromA, you'll need to create a B from the Bcpp*:
- (B*)GetBfromA
{
Bcpp *bfroma = myAcpp.GetBfromA(); // return c++ object
B* result = [[B alloc] initWithBcpp:bfroma];
return result;
}