0

Right now my OS X 10.9 project is set up with a GUI in Objective-C and all the processing in a C++ class. This doesn't seem like the best way, but this was given to me and I have to work with these constraints. I currently have a NSSlider in the Cocoa GUI. I want to be able to have this NSSlider control a variable x in the C++ class.

Currently the tolerance bar is working when I run the program (working meaning it's continuously updating its value as per my sliding).

Header for NSSlider (Slider.h):

@interface Slider : NSObject {
@private
    __weak IBOutlet NSTextField *sliderValue;
    __weak IBOutlet NSSlider *slider;
}

- (IBAction)updateSliderValue:(id)sender;

@end

Implementation for the NSSlider (Slider.mm):

#import "Slider.h"      // The NSSlider object
#import "CMain.h"       // The C++ class that does all the processing (not sure if this is the way to include a C++ interface in an Objective-C++ class or if this will allow me to call its methods)

@implementation Slider

-(void) awakeFromNib {
    [slider setIntValue:40];
    [sliderValue setIntValue:[slider intValue]];
}

- (IBAction)updateSliderValue:(id)sender {
    [sliderValue setIntValue:[slider intValue]];
    // Here is where I'd think that if I have a setValueofX(int number) method in the C++ class
    // I could call this somehow using objective-C++. I don't know if I can or how to call this method here
}

Here is the relevant snippet from Main.h:

class CMain(){
    public:
        CMain();
        void setValueofX(int number);
        int getValueofX();

    private:
        int x;
};

How do I include CMain.h in the Objective-C++ (which, the .h file or .mm file? and how?) such that it would allow me to call the methods in CMain.h? How would I phrase the method call in Objective-C to set x's value using the setValueofX(sliderValue)?

Please let me know if more information is needed! Any help or thoughts are appreciated!

Thanks!

Ishaan Taylor
  • 1,817
  • 5
  • 17
  • 19

3 Answers3

3

The code for calling the setter is the same as it would be in C++. However, you'd need a pointer to the CMain object to be able to call the method on it. I don't know where that object currently resides. If there is no object yet in another object, you probably want to create one, which you can by just declaring an instance variable CMain myCMain; and then calling myCMain.setValueOfX( [slider intValue] ); in updateSliderValue:.

As to where to include the C++ class, it's really your choice. However, if you use any C++ in your header, it will take a bunch of careful extra work to include it from plain .m files. So in general I try to stick to plain C and ObjC in the header, and only use C++ in the .mm file. You can use a class extension (sometimes called "class continuation" as well) to declare additional ivars in your .mm file to keep them out of the header.

If you want more info about ObjC++, I answered in detail on another post: Can I separate C++ main function and classes from Objective-C and/or C routines at compile and link? and that also links to a Podcast I was a guest on where I talk about a lot of the details and tricks for integrating ObjC and C++.

Aside: I hope these are not actual names and comments you're using. Never have an ObjC class without a prefix (3 letters, Apple stated they reserve all 2-letter prefixes for their own use, and they've used un-prefixed internal class names in the past which broke some peoples' programs). Also, "Slider" is a bad name, as NSSlider is the actual slider, and this sounds like it should be a subclass. You really want to call this an ISTSliderController or whatever.

Community
  • 1
  • 1
uliwitness
  • 8,532
  • 36
  • 58
  • /@uliwitness Thank you for the suggestions! I changed the variable names as they were specialized to what I was doing- I was hoping it would make more sense to introduce it like this. So, the `CMain` object is instantiated in a different class - a wrapper class, ironically called `CWrapper.mm`. I do not want to create another instance of this variable, how can I point to this one? Another route I'm considering is making the set method static method (instance agnostic (I think?)) and keep the value the same across different objects. Having it the same would work for my application. – Ishaan Taylor Oct 28 '14 at 20:21
  • 1
    Don't make your design static just because it seems easier. It will only bite you in the back if you later find you need two objects of this type after all. Really, C++ is no different from Objective-C in this regard. You just need to find an object that knows about both of these objects and have it hand your `ISTSliderController` a pointer to the instance of `CMain` that the other guy has. You usually have a hierarchy of objects, where objects closer to the root create objects "below" them. At that point you usually hook up connections like that as well. – uliwitness Oct 29 '14 at 07:38
1

I found a solution by noting that there was an existing wrapper class CWrapper.h and it was an objective-C++ implementation class CWrapper.mm. There was a CMain variable instantiated as cmain. I made a getter method for x and I simply created a static method in the wrapper class + (void) passX:(int) number; that I called in the slider class. I chose a static method because this for this application, this value will never have to be different between objects. I hope I made the right choice here!

See code changes below:

I added this to the Slider.h file.

- (int)X;

I added the getter and [CWrapper passX:[self getX]]; to the updateSliderValue method in the Slider.mm file.

- (int)X {
    return [slider intValue];
}

- (IBAction)updateSliderValue:(id)sender {
    [sliderValue setIntValue:[slider intValue]];
    [CWrapper passX:[self getX]];
}

This is the code I added to CWrapper.h.

+ (void) passX:(int) number;

This is the code I added to CWrapper.mm.

+ (void) passX:(int)num
{
    cmain.setValueofX(num);
}
Ishaan Taylor
  • 1,817
  • 5
  • 17
  • 19
  • Just one note: Don't start getters' names in Objective-C with "get". That's reserved for methods that give side effects in parameters. Just call it -tolerance. That way you can use KVO or other mechanisms that rely on you adhering to such naming standards. – uliwitness Oct 31 '14 at 15:50
-2

Here's an all objective-c and objective-c++ answer:

CMain.h:

#ifndef Testing_CMain_h
#define Testing_CMain_h

@interface CCMain : NSObject
-(CCMain*) init;
-(void) dealloc;

-(void)setValueofX:(int)number;
-(int)getValueofX;
@end

#endif

CMain.mm:

#import <Foundation/Foundation.h>
#import "CMain.h"

class CMain {
private:
    int x;

public:
    CMain() : x(0) {}

    void setValueofX(int number) {x = number;}
    int getValueofX() const {return x;}
};


@interface CCMain ()
@property (nonatomic, assign) CMain* inst;
@end

@implementation CCMain

-(CCMain*) init
{
    if (!_inst)
    {
        _inst = new CMain();
    }
    return self;
}

-(void) dealloc
{
    delete _inst;
    _inst = nil;
}

-(void)setValueofX:(int)number
{
    _inst->setValueofX(number);
}

-(int)getValueofX
{
    return _inst->getValueofX();
}

@end

and if you want to use C-style functions then:

setValueofX(cmain_instance, value);
int val = getValueofX(cmain_instance);

And this works because a C++ class function in C is the same as:

void CMain::MyFunc(int X);
//vs..
void MyFunc(CMain* inst, int X);

Both are the exact same thing.

Brandon
  • 22,723
  • 11
  • 93
  • 186
  • Objective-C++ is a better choice than this approach. ObjC++ means you can just use C++ statements in your ObjC code. The code above makes the code much less readable and relies on details of the ABI, I think. – uliwitness Oct 28 '14 at 19:37
  • You actually think you can use Objective-C++ code in Objective-C? That's a joke. Let's see you do that with `std::cout`.. He has to write wrappers no matter what. His header has to be entirely C or Objective-C. NOTHING from Objective-C++. Objective-C++ does NOT mean you can use C++ in Objective-C at all. You're still wrapping the C++ code in C-style code. It's just easier to use Objective-C++ depending on the situation. Using Objective-C++ code in Objective-C is synonymous to using `extern "C"`. – Brandon Oct 28 '14 at 19:40
  • 2
    I've been working on large mixed C++/ObjC projects for about 10 years now. And the OP didn't ask about using C++ in ObjC. The OP asked about using C++ in an ObjC++ (.mm) file. (See my answer for lots more info) – uliwitness Oct 28 '14 at 19:44
  • Your experience and opinion still doesn't invalidate what I said. You're not the only one with experience. It said exactly what I just said. "Keep objective-c++ code in the .mm file. "Only use Objective-C or C code in the header". `However, you'd need a pointer to the CMain object to be able to call the method on it` is exactly what my example shows.. I don't mind you advertising yourself, but I hate commercials.. so.. – Brandon Oct 28 '14 at 19:51
  • @Brandon what does `NSLog(@"%d", getValueofX(cmain_instance));` do? Does it simply log the action somewhere? Do I need to instantiate CMain in the `.m` file? (`cmain_instance`)? – Ishaan Taylor Oct 28 '14 at 20:22
  • @Brandon What if I were to make the variable in question a static variable? Then I would avoid having to instantiate another object.. Do you see any problem with this? – Ishaan Taylor Oct 29 '14 at 05:22
  • If you made it static, you won't have any separate instances. If you don't mind that, then it's fine. Also, NSLog is the same as printf in objective-c but for the xcode console. – Brandon Oct 29 '14 at 05:51
  • 1
    @Brandon Look at my answer and the post and podcast that links to. I explain it all there in detail. But you can easily pass a pointer to a C++ object into C code by forward-declaring it as a struct and passing a pointer to it. No need to put actual C++ in the header, and you're free to put C++ into the implementation, so there's no need to typecast function pointers and make them static. – uliwitness Oct 29 '14 at 07:42
  • 1
    Ah, I see you changed your code to also mention a better approach now. Thanks! If the OP can afford to just change the class from C++ to ObjC, that approach works, too. But if this is e.g. cross-platform code that has to be shared with a Windows or Linux version, there are better approaches like I mention elsewhere (no need to re-post all that info in less detail to fit here). – uliwitness Oct 29 '14 at 07:45