Implementing Objective-C Delegation in C++

Sometimes I wan't to implement things like I would do it in Objective-C when working in C++. But in C++ there is no such a thing like delegation. (At least not with that name)
These are just a few notes on how to implement a delegate or delegation in C++. Is not complicated at all the only thing you need is to find the equivalents for Protocols, Delegates, in C++.

Note1: There are no protocols in C++ but you can use interfaces and multi-inheritance to accomplish this.
For example in Objective-C, let's suppose you write something like this:


@protocol UITableViewDataSource;
@interface UITableView : UIScrollView{
@private
    id<UITableViewDataSource  _dataSource;
}
@end

and


@protocol UITableViewDataSource;
@required
- (NSInteger)tableView:(UITableView *)table numberOfRowsInSection:(NSInteger)section;
@end

Then (assuming you have the same class names) in C++ you will write something like this:

class UITableViewDataSource;
class UITableView : public UIScrollView {
    UITableViewDataSource *_dataSource;
};

class UITableViewDataSource{
    virtual NSInteger tableViewNumberOfRowsInSection(UITableView* tableView, NSInteger section);
}

So, how you implement your protocol?
In Objective-C:


@class MyDataSource <UITableViewDataSource> {
    NSArray *array;
}
- (NSInteger)tableView:(UITableView *)table numberOfRowsInSection:(NSInteger)section;
@end

@implementation MyDataSource
- (NSInteger)tableView:(UITableView *)table numberOfRowsInSection:(NSInteger)section{
    return [array count];
}
@end


In C++ (Assuming there is a NSArray class in c++)


class MyDataSource : public UITableViewDataSource {
    NSArray *array;
    virtual NSInteger tableViewNumberOfRowsInSection(UITableView* tableView, NSInteger section);
};


NSInteger MyDataSource::tableViewNumberOfRowsInSection(UITableView* tableView, NSInteger section){
    return array->count();
}


In C++ multi-inheritance is allowed so could have a controller class that is a controller and also implements UITableViewDataSource interface. (Or in Objective-C words: conforms to UITableViewDataSource protocol)

If in Objective-C you have:


@class TableViewController : UIViewController < UITableViewDataSource > { ...


in C++ you will have:


class TableViewController : public UIViewController, public UITableViewDataSource { ...


Multi-inheritance + interfaces plays quite the same role as protocols.

Note2: There are no selectors in C++ but you can pass a method as a pointer. This is useful when you want  to set a method to be called by other object.
TODO: add samples: for [object performSelector:@selector(aMethod:) withObject:argument afterDelay:0.0];
TODO: add samples for method pointers in C++


Note3: C++ does not bring any kind of concurrency APIs so you will have get your own. Boost thread, OpenCL, CUDA, OpenMP. I think OpenMP is the simplest option here. Most compilers implement it and just with a few lines of #pragma you are done. But if you get really serious and need not only multithreading but also GPU/CPU power then OpenCL is the best option,  OpenCL is a young but powerful bunch of APIs for multi GPU/CPU programming, version 1.1 was just approved by Khronos group last month and with this new update we are able to run c++ code in it. The bad thing is we have to wait until Apple or your hardware vendor implements since OpenCL is just a specification.

TODO:add some links to OpenMP and Boost examples
TODO: add some links to OpenCL

Using PointGrey Research Cameras with OpenCV 2.1.1 in Macs

Point Grey Research makes really good industrial cameras that are suitable for many kinds of image processing researches. The big con for me is that they don't or plan to offer support for Mac OS X. Luckily with the new Quick Time libraries (QTKit.framework) we can use those cameras.

There are two alternatives, you can become a native citizen and use Objective-C, Cocoa and QTKit directly or you can use OpenCV (a C/C++ image processing library) that uses QTKit internally.

OpenCV is an open source project for image processing, computer vision, machine learning and many other areas, is very useful and developed by a very active group!. Since version 2.1 OpenCV uses QTKit.framework to get frames into images (IplImages).

This is just a couple of notes you should take in count when using this unsupported cameras, you have two options:

Option 1: Modify your program

When using PGR cams in macs you MUST set a width and height to the Video Capturer like this:

using namespace cv;
int main(int, char**)
{
 VideoCapture cap = VideoCapture(1);
 cap.set(CV_CAP_PROP_FRAME_WIDTH, 320); //LINE ADDED
 cap.set(CV_CAP_PROP_FRAME_HEIGHT, 240); //LINE ADDED
 if(!cap.isOpened()) return -1;

 namedWindow("edges",1);
 for(;;)
 {
  Mat frame;
  cap >> frame;
  imshow("edges", frame);
  if(waitKey(30) >= 0) break;
 }
 return 0;
}

If you don't set a width and height then the window will never appear. No errors, nothing happens. Probably you will be debugging for hours or end up by using another camera.
If don't want to modify every single program you have, you simply modify OpenCV, is less than 10 lines including building and installing. These is the best part of open software stuff!

Option 2: Modify OpenCV

Find the file to modify

Locate file "cap_qtkit.mm" and open it. It should be in "opencv/modules/highgui/src/" or just do a search.
Find startCaptureDevice() method implementation, it should look something like this:

int CvCaptureCAM::startCaptureDevice(int cameraNum){
  ...
}

Modify the file

Inside startCaptureDevice() method you will find these lines:

NSDictionary *pixelBufferOptions ;
if (width > 0 && height > 0) {

We are gonna modify the code just before those line as in the picture:



Here is the code:

//start of modification
if (width == 0 && height == 0 && [[device description] isEqualToString:@"Camera"]) {
 width = 640; height = 480;
}
//end of modification

Now just built and compile OpenCV and your are done!
(You can refer these instructions if you don't know how, also make sure you have your cmake updated)

Explanation

PGR cams are not made to work with macs, but happily they conform to some protocols (I believe one of them is ieee1394) so they actually do!. They problem seems to be in some of them default parameters
So when we are just about to initialize a PGR cam, we simply set good defaults.

How to recognize a PGR cam? I have realized that when printing all devices descriptions PGR cameras shows a string Camera.
So printing all devices in a MacBook with a PGR cam will show something like this:

"Built-in Input",
"Built-in Microphone"
"Built-in iSight"
"Camera"

That is why we check for a device whose description is Camera and its width and height hasn't been set.

More on PGR cams and their pixel formats:

I have tested with QTKit.framework and Objective-C and only a reduced group of pixel format seems to  work with my PGR cam (Grasshopper) . I said seem to work because I didn't check the pixels, I just used a QTCaptureView object to see the results.

kCVPixelFormatType_32ARGB
kCVPixelFormatType_32BGRA
kCVPixelFormatType_32ABGR
kCVPixelFormatType_32RGBA
kCVPixelFormatType_64ARGB

Only those worked for me, but I didn't test all of them (they are too many!) Even though this is more than necessary to work with raw pixels.

References

Installing and Using OpenCV 2.1.1 in Snow Leopard


Update 2011/07/26:

It is easier to install homebrew and then install opencv using it:
$ /usr/bin/ruby -e "$(curl -fsSL https://raw.github.com/gist/323731)"
$ brew install opencv
And you are done!
Now add the headers to your project as shown in step 3 and 4 :)


I use OpenCV very often for my projects. I like it because it has some interesting and very useful implementations. What I don't like is kind of messy and docs update are slow. Specially documentation for Macs is slow!

So I just followed some instructions from here, read some other info from here and realized opencv's new structure by looking at the installed files.

This post shows how to run the sample code I got from here

Step 1: Get and Configure OpenCV 2.1.1:


In the Terminal,
$ svn co https://code.ros.org/svn/opencv/trunk/opencv
$ cd opencv
$ mkdir build
$ cd build
$ cmake ..
Optionally you can configure some flags using cmake configure tool

Step 2: Build and Install OpenCV 2.1.1:

$ make -j8
$ sudo make install

And we are done!

Step 3: Create a Xcode Project:

(I am using Xcode 3.2.2)

In Xcode, create a Standard C++ project by doing: File>New Project...
Select "Application" from MacOS X left menu and "Command line tool" type: "C++ stdc".

Step 4: Use OpenCV 2.1.1


Add OpenCV 2.1.1 Headers Path

  • Select Project>Edit Project Settings...
  • Set Configuration to "All Configurations"
  • In the "Architectures" section, double-click "Valid Architectures" and remove all PPC architectures
  • In "Search Paths" section set "Header Search Paths" to /usr/local/include/
    (What we are doing is add the path where opencv's headers folder is)
  • Close the Project Info window


Add OpenCV 2.1.1 Libraries (Dylibs)

  • Select Project -> New Group and create a group called OpenCV Frameworks
  • With the new group selected, select Project -> Add to Project…
  • Press the "/" key to get the Go to the folder prompt
  • Enter /usr/local/lib
  • Select the libraries needed or all of them:
    libopencv_calib3d.2.1.1.dylib
    libopencv_contrib.2.1.1.dylib
    libopencv_core.2.1.1.dylib
    libopencv_features2d.2.1.1.dylib
    libopencv_highgui.2.1.1.dylib
    libopencv_imgproc.2.1.1.dylib
    libopencv_legacy.2.1.1.dylib
    libopencv_ml.2.1.1.dylib
    libopencv_objdetect.2.1.1.dylib
    libopencv_video.2.1.1.dylib
    


Uncheck "Copy resources" when  asked, importing a reference is enough.

Include OpenCV 2.1.1 headers in your code:


In prior versions of OpenCV we would have written:
#include "cv.h"; or depending on your header search path #include "opencv/cv.h"; but in OpenCV2.1.1 file structure is changed, so including headers like that will cause at least some warnings. (Because those headers are deprecated) So let's do it the new way:

#include "opencv2/imgproc/imgproc.hpp" //for image processing
#include "opencv2/highgui/highgui.hpp" //for GUI

You will need to include more headers if you are doing other stuff like machine learning, etc. If you are not sure what headers you should include just check directory:
/usr/local/include/opencv2/
You will realize that OpenCV 2.1.1 is now divided in modules!



Now build and Run and you should have your program running ;)
You can download my sample project from here.

I hope it helps ;)

This work is licensed under BSD Zero Clause License | nacho4d ®