On old and new in the Computer Science
These last weeks I have finally been able to play around with a dialect of LISP called PLT Scheme. It got triggered by reading the so called Wizard book: "Structure and Interpretation of Computer Programs"
I must say I am very impressed with Scheme. I am still not sure if I could ever be fully comfortable with the language because of all the annoying parenthesis. But the power and flexibility of the language is mind-boggling. And there is something beautiful about that ultra simple syntax. Everything is just words separated by spaces and enclosed by parenthesis. That is all there is to it!
Compare that to the extremely complicated syntax in C++.
I have realized that thinking that new is always better than old is the dumbest belief I ever had. Sometimes new stuff is old already at the instance it is made, while old stuff is modern 20 years later.
An example of this is the code base of our program at work. A lot of the code is crap and badly thought out. At first I assumed it was the old code that was bad. Then I realized that in fact the old stuff is what was fairly good and the crap was mostly new stuff.
The stuff is a maintenance nightmare because nothing is commented. I talked to a guy who talked about a system he worked on that was from the 50s or 60s or something. The New York subway system I believe. I asked if that wasn't hard to do given that the original makers were probably not around anymore. But it wasn't because as he explained for each source code file there was a corresponding document file explaining that source code file in detail. It was all very well documented.
I wonder how could they get software design right 50 years ago and we manage to screw it up today?
Same with LISP, mainstream languages are only now starting to get some of the more important LISP features. How could they get it right that long time ago, and we mange to screw it up today with languages such as C++ and Java?
I increasingly realize that languages like C++, Java , C# etc seldom offer anything new. They are just the same shit in new wrapping. New wrapping in this case means new syntax.
But perhaps the biggest eye opener with respect to the value of old stuff, is when I read Adam Smiths the Wealth of Nations. It is something like 250 years old. I had read other new books about economic history and theories for this and that about the historical economic development of China, Africa etc. Surprisingly I found Smith treatment of the same much clearer, well reasoned and thought out. In short he offered a much better explanation than these newer books. But this isn't the first time. People like Ricardo and other economist have come after Smith and presumable proved him wrong only to have later economist discover that Smith was right all along.
Perhaps talking of old and new as if it corresponded to bad and good is about as stupid as saying that derivation and integration is bad because now we have newer mathematical methods. A good idea is a good idea no matter how old it gets.
Why Objective-C is cool
I think anybody who has played computer games to some extent know how the game Doom (1993) from id software revolutionized gaming on the PC platform. What few people know was the the game was actually not developed at the PC platform at all.
At the time Doom was developed in 1992, Windows 95 didn't exist yet and software development on PC's looked like this:
Not very impressive. Instead it was developed on computers from NextStep, the company founded by Steven Jobs after he was kicked out of Apple. As id developer John Romero says:
Why do I care so much about NeXT computers? Because we at id Software developed the groundbreaking titles DOOM and Quake on the NeXTSTEP 3.3 OS running on a variety of hardware for about 4 years. I still remember the wonderful time I had coding DoomEd and QuakeEd in Objective-C; there was nothing like it before and there still is no environment quite like it even today.
To get an impression of how far ahead of its time Objective-C and what is now known as Cocoa was ahead of its time, consider this:
In fact, with the superpower of NeXTSTEP, one of the earliest incarnations of DoomEd had Carmack in his office, me in my office, DoomEd running on both our computers and both of us editing one map together at the same time. I could see John moving entities around on my screen as I drew new walls. Shared memory spaces and distributed objects. Pure magic.
Consider how long time ago that was, CORBA didn't support C++ for remote method invokation until 1996. At the time id did this it was just in its infant stage. Not to mention this was long long before Java RMI made remote objects populare and mainstream. It isn't even common to do this kind of thing today, and yet this was something that was quite trivial to do in Objective-C and Cocoa back in 1992.
Introducing Objective-C
Every time someone introduce you to Objective-C they will tell you about what a small and simple language it is. How easy it is to learn etc. But that was not my impression when I first tried to learn it. At the time I knew C++. When I saw an example of how to declare a class:
{
int mSomeNumber;
NSString* mSomeString;
}
- (int)someNumber;
- (void)setSomeNumber:(int)aNum;
@end
I didn't think it looked easy or simple at all. Especially when I saw some code examples:
{
// Fill whole background with white
[[NSColor whiteColor] set];
[NSBezierPath fillRect:frameRect];
// Construct rows of lines
NSBezierPath *gridLines = [[NSBezierPath alloc] init];
for (unsigned row = 0; row <= mNoRows; ++row) {
[gridLines moveToPoint: rowLeft];
[gridLines lineToPoint: rowRight];
rowLeft.y += rowHeight;
rowRight.y += rowHeight;
}
// Set style to draw line in
[[NSColor blackColor] set];
[gridLines setLineCapStyle:NSSquareLineCapStyle];
float pattern[2] = {2.0f, 5.0f};
[gridLines setLineDash:pattern count:2 phase:0.0f];
// Draw specified lines in given style
[gridLines stroke];
[gridLines release];
}
The code below is just to give an idea of what Objective-C syntax is like. What the code does is not important. It has been simplified a bit to understand better. It basically draws several rows with black lines on a white background. When I first saw this kind of code I didn't think it looked easy at all. And I thought whoever said that Objective-C was a much simpler language than C++ must have been smoking something that wasn't good for them.
The problem was that I was too caught up in the syntax. The syntax is indeed very unusual, but if one looks beyond that, the structure of the syntax is actually quite simple.
Perhaps the best way to understand the syntax is to understand, why it looks so strange.
Background
Unlike C++, Objective-C wasn't really a new language at all originally. There was no Objective-C compiler. Instead Objective-C was just plain old C, with an added preprocessor. As any C/C++ developer worth his/her salt should know all statements starting with # specifies a preprocessor directive in C/C++. E.g.
#define
and #include
. The preprocessor runs before the compiler and replaces the directives with actual C/C++ code. What the makers of Objective-C did was to add another preprocessor, but instead of marking the directives with #, they marked them with @. That way their special preprocessor could find all the new directives easily.
While this does make Objective-C look like some alien entity inside C, it does make it trivial to distinguish between pure C code and the add-ons from Objective-C. Unlike C++, where what is C and what is C++ is blurred. Actually C++ isn't a strict superset of C like Objective-C although it usually compiles C code. The reason being among other things that C++ reinterprets a lot of regular C code into new C++ concepts. E.g. a struct isn't just a struct anymore but actually a class in C++.
Syntax inspiration from smalltalk
The second stumble block in order to understand Objective-C syntax is to realize it is derived from Smalltalk, while C/C++ syntax is derived from Algol. Algol based languages separate arguments with comma, while smalltalk separates with name of argument and colon. Below is a code snippet that demonstrates setting the position and dimension of a rectangle object.
rectangle setX: 10 y: 10 width: 20 height: 20
// Objective-C
[rectangle setX: 10 y: 10 width: 20 height: 20];
[rectangle setX1: 10 y1: 10 x2: 20 y2: 20];
// C++
rectangle->set(10, 10, 20, 20);
The Smalltalk/Objective-C syntax improves readability of code. When specifying a rectangle some libraries use the start and end coordinates while others use start coordinates and size. With Smalltalk/Objective-C it is made quite clear what is done. While with C++ it is not clear whether the first or the second line of Objective-C code is used. Unlike Smalltalk Objective-C methods are called by enclosing call with []. This was originally to aid the preprocessor in extracting what was regular C code and what was Objective-C specific code.
A small and simple language
What do we exactly mean by saying that Objective-C is a small an simple language. C++ looks simpler syntax wise for the novice. However as said before syntax in deceiving. C++ add a host of new features to the C language: classes, virtual methods, non-virtual methods, constness, templates, friend classes, multiple inheritance, pure virtual functions, operator overloading, function overloading, private, public and protected members, constructors etc.
Objective-C on the other hand add very little, it is just classes, methods, categories and protocols. There is only one way to create classes and to inherit form them. You can specify whether inheritance is public or private e.g. There is only one kind of methods. There is no distinction between virtual and non-virtual. They can't be const or not const. The concept of constructors and destructors don't exist. Instead these are just normal methods.
As mentioned Objective-C is so simple they didn't even need to create a new compiler. A preprocessor was enough. This can make Objective-C seem overly simple. How can you do much with a language like that? The answer is the Objective-C runtime. The runtime is in fact what makes most of the Objective-C magic happen. Most of Objective-Cs features is provided at runtime and not done at compile time.
Methods and Selectors
Objective-C differs from C++ in that one distinguish between methods and selectors. To call a method on an Objective-C method you send a message. The method is not called directly. Instead Objective-C determines based on the selector which method to call.
Conceptually one can think of a message (selector) as simply a string with a list of arguments. The code below e.g. send the message
setX:y:width:height:
to object rectangle
. This will invoke a method on rectangle if it understands the message.If we think of Objective-C as just a preprocessor as it originally was, all message sending is replaced with a call to a C function:
So when the preprocessor encounters our rectangle message it is translated into something like this:
objc_msgSend
queries rectangle for its class and then queries the class for methods it contains. Then it finds out which method corresponds to the given selector. The method is nothing but a function pointer of the form:Of course this is a simplified explanation. In reality every selector is registered. Meaning we don't pass newly created strings to
objc_msgSend
each time we invoke a method but a pointer to a string. This pointer has to be unique. So we can't pass any string pointer. So if we got � string we can find the unique pointer to this string by using the function:
Which returns the pointer if it exist or registers the selector as a unique string pointer if it doesn't exist. The benefit of this is that selectors can be looked up quickly and compared quickly by just comparing their pointer addresses rather than comparing each character of the string.
Unique dynamic features of Objective-C
I don't intend to go into every minor of the Objective-C runtime library but the example above should give an idea of how the dynamic features of Objective-C works.
Classes
In C++ classes don't exist passed compile time. Or at least in modern C++ compilers which support runtime type identification classes exist in a watered down sense in that one can query if two classes have a relationship.
In Objective-C it is almost opposite. Classes don't exist at compile time but rather are runtime entities. Classes are registered at runtime with function:
Likewise methods and selectors are registered at runtime. So classes can in fact be modified at runtime. Of course users don't call
objc_addClass
. These methods along with the ones that registers methods are generated by the preprocessor from the class definition provided by the programmer. But it is this fact that classes and methods exist as structures in memory at runtime that allows the programmer at runtime to query classes about their member variables and functions and whether they respond to a selector or not.
In fact an Objective-C developer could create an application which could let user specify classes to call and functions to call. User could just type in the name of a class. Then developer could use C function NSClassFromString() to get corresponding class. NSSelectorFromString() could be used to retrieve the selector. With this one could query class further about arguments existing for selector etc.
To learn more about Objective-C look at wikipedia
The right tool for the job: C++ vs Objective-C
I read Alexei's blog entry about C++ vs Objective-C. I agree fully with his statement that:
The argument (which you can go and read yourself) boils down to �C++ can do everything Objective-C can� versus �sure it can, but not easily, usably or understandibly.� The usual argument comes down to object models: With Objective-C, you can send any object any message, and if it understands it, it will respond. C++ is restricted by its compile-time type system, so that even if you have arbitrary objects that all implement the member function foo, there is no way to call that method on a set of them unless they all inherit from the same base class. Except that you can, as exemplified boost::any and boost::variant.
Actually I pretty much agrees with everything Alexei says and the comments put forth by different people. C++ templates is a wonderful thing while often complicated to understand and use. My major issue with them is that they can't be extended into runtime.
So anyway what I want to emphasis is that templates is not substitute for Objective-C's dynamic features. I work daily on a C++ application with +4 million lines of code. The application have a natural fit with OO design because it has a number of data objects which can be inspected, manipulated and visualized in many different sorts of views at the same time. E.g. one view can show the object in 3D while another can show an intersection in 2D. So Model-View-Controller is a natural fit for it.
The more I work on it the clearer Greenspuns Tenth Rule of programming becomes to me:
Any sufficiently complicated C or Fortran program contains an ad hoc, informally-specified, bug-ridden, slow implementation of half of Common Lisp.
Bottom line is that the kind of structure a large application like this would typically have is what Objective-C was designed for. And thus when I look at the app, all we do in C++ is making a bug-ridden slow implementation of Objective-C.
Creation of views for data objects has to be abstracted because we might create new views we never thought about when the data objects were designed. Thus construction of views has to be abstracted. In Objective-C this is easy. Classes can be passed around like objects and instantiated. C++ on the other hand provides no abstraction of object creation so we ended up making this elaborate scheme with Class ids and factory classes.
We created reference counting, a system for sending messages between object for notifications etc. All stuff that Objective-C was designed for. We end up with huge amount of code for the overall architecture. And it is not even working that well.
But we are not the only ones. Every time I look at a large C++ framework or toolkit I realize that they are just re-implementing Objective-C features in a non standard and buggy way. When I say non-standard I mean that every toolkit has those features but they are done differently in each one and thus not compatible.
E.g. VTK (Visual Toolkit) has reference counting, message passing and abstraction of object creation. Open Inventor has their own reference counting etc. The Qt GUI toolkit has basically gone around the limitations of the C++ language for dynamic behavior and essentially created a library that allows for object introspection and runtime and dynamic dispatch. Essentially they have reimplemented Objective-C in C++. While they have don't quite a good job and I love the Qt toolkit it is still a kludge and non-standard.
So what exactly is my point? My point is that while people can argue from a theoretical foundation that C++ really is the better language due to all its features the power of templates etc, reality speaks for itself. In practice C++ very frequently fails as a language. It seems quite plain to me that when any large C++ toolkit seem to make a half bad implementation of Objective-C then something is wrong with the language.
Does this mean that I think Objective-C is a better language? No, far from it. I just think that one should use the best tool for the job. Frequently it seems like Objective-C would have been the best tool but C++ was selected instead. While C++ has an obvious advantage in implementing algorithms and high performance code, it doesn't seem like the mistake has been done as frequently at the Objective-C camp.
My speculation would be that this is because Objective-C's deficiencies are so obvious. While C++ deficiencies are not as clear. One can more easily kid oneself into thinking C++ will solve the problem without any hassle. And not at least because C++ is better known, a Objective-C developer will more likely consider using C++ for specific parts of the program than the other way around.
C++ and Objective-C complements each other very well in my opinion and they have different enough syntax from each other that it should be easy to keep the two apart in a program. But too often people think C++ is the silver bullet that can be used equally well for any task.
Wrapping C++ classes in Lua
Lua is a script language frequently used in game development. Typically the game engine is written in C/C++ and the game itself is defined in Lua. Meaning setting up behaviour, placement of objects like powerups, doors, enemies etc is done in Lua. That way the game can be quickly tweaked without having to go through a time consuming re-compile.
But I am not going to go in depth about the different usage of Lua, nor explain the language itself. An online book at the Lua web page does that very well already. I will here assume you already have some basic knowledge about Lua.
However finding any documentation on how to wrap a C++ class so that it can be used in Lua is difficult. One could of course use one of the ready made bridges. But here we are going to look at how to do it yourself.
The tricky part is deciding on how to do it. Because Lua is such a flexible language there is really a lot of ways you could achieve it.
The naive approach
First lets look at several options, what works and doesn't. My first approach was to light user data to store a pointer to my C++ class. I will use a Sprite class and related classes as examples here as that was the type of classes I was wrapping.
{
int n = lua_gettop(L); // Number of arguments
if (n != 4)
return luaL_error(L, "Got %d arguments expected 4", n);
double x = luaL_checknumber (L, 1);
double y = luaL_checknumber (L, 2);
double dir = luaL_checknumber (L, 3);
double speed = luaL_checknumber (L, 4);
Sprite *s = new Sprite(Point2(x, y), dir, speed);
lua_pushlightuserdata(L, s);
return 1;
}
The code snippet above shows a naive implementation of this approach. Unfortunately it doesn't work. The problem is that a light user data is just a simple pointer. Lua does not store any information with it. For instance a metatable which we could use to define the methods the class supports.
An approach with limited functionality
The next approach would be to use user data. Unlike light user data it can store a reference to a metatable.
{
int n = lua_gettop(L); // Number of arguments
if (n != 4)
return luaL_error(L, "Got %d arguments expected 4", n);
// Allocate memory for a pointer to to object
Sprite **s = (Sprite **)lua_newuserdata(L, sizeof(Sprite *));
double x = luaL_checknumber (L, 1);
double y = luaL_checknumber (L, 2);
double dir = luaL_checknumber (L, 3);
double speed = luaL_checknumber (L, 4);
*s = new Sprite(Point2(x, y), dir, speed);
lua_getglobal(L, "Sprite"); // Use global table 'Sprite' as metatable
lua_setmetatable(L, -2);
return 1;
}
For us to be able to use sprite like this we need to register class first. Basically we need to create a table Sprite which contains all the methods that our user data should support.
static const luaL_Reg gSpriteFuncs[] = {
// Creation
{"new", newSprite},
{"position", position},
{"nextPosition", nextPosition},
{"setPosition", setPosition},
{"render", render},
{"update", update},
{"collision", collision},
{"move", move},
{"accelerate", accelerate},
{"rotate", rotate},
{NULL, NULL}
};
void registerSprite(lua_State *L)
{
luaL_register(L, "Sprite", gSpriteFuncs);
lua_pushvalue(L,-1);
lua_setfield(L, -2, "__index");
}
This will allow us to create instances of Sprite and call methods on it in Lua like this:
local sprite = Sprite.new(x, y, dir, speed)
sprite:setPosition(x,y)
sprite:render()
The final approach
In most cases this approach is sufficient but it has one major limitation. It does not support inheritance. You can change the methods of Sprite in Lua but that will change the behavior of all instances of Sprite. What you would want to do is to be able to change method on just the instance and then use that instance as a prototype for new Sprite instances, effectively creating a class inheritance system.
To do this we need to change the instance into being a table. How do we access our C++ object then? Simple, we just store the pointer to it as user data in one of the field of the table. You might think that this time light user data will be sufficient. However the problem is that only user data is informed of garbage collection, not tables or light user data. So if you want to delete the C++ object when corresponding lua table is garbage collected you need to use user data.
So we then arrive at our final solution. We will store a pointer to our C++ object as user data on the key __self in the table that represents our instance. __self is an arbitrary selected name. It could be anything. We will not register Sprite as as the metatable for our instance but instead register the first argument to the new function as it. This will allow us to support inheritance. Further the garbage collection function will be register on a separate table which will be used as metatable only for the user data. This is to allow it to be garbage collected.
{
int n = lua_gettop(L); // Number of arguments
if (n != 5)
return luaL_error(L, "Got %d arguments expected 5 (class, x, y, dir, speed)", n);
// First argument is now a table that represent the class to instantiate
luaL_checktype(L, 1, LUA_TTABLE);
lua_newtable(L); // Create table to represent instance
// Set first argument of new to metatable of instance
lua_pushvalue(L,1);
lua_setmetatable(L, -2);
// Do function lookups in metatable
lua_pushvalue(L,1);
lua_setfield(L, 1, "__index");
// Allocate memory for a pointer to to object
Sprite **s = (Sprite **)lua_newuserdata(L, sizeof(Sprite *));
double x = luaL_checknumber (L, 2);
double y = luaL_checknumber (L, 3);
double dir = luaL_checknumber (L, 4);
double speed = luaL_checknumber (L, 5);
*s = new Sprite(Point2(x, y), dir, speed);
// Get metatable 'Lusion.Sprite' store in the registry
luaL_getmetatable(L, "Lusion.Sprite");
// Set user data for Sprite to use this metatable
lua_setmetatable(L, -2);
// Set field '__self' of instance table to the sprite user data
lua_setfield(L, -2, "__self");
return 1;
}
We can now work with sprite instances in Lua like this:
local sprite = Sprite:new(x, y, dir, speed)
sprite:setPosition(x,y)
sprite:render()
-- Add method to instance as use it as class
function sprite:doSomething()
print("do something")
end
local derived = sprite:new(x, y, dir, speed)
derived:render()
derived:doSomething() -- This is now a legal operation
There are still a couple of loose ends. We haven't showed how the methods are registered with this new solution nor how we access C++ object pointer in methods. But this is fairly straight forward as I will show.
{
// Register metatable for user data in registry
luaL_newmetatable(L, "Lusion.Sprite");
luaL_register(L, 0, gDestroySpriteFuncs);
luaL_register(L, 0, gSpriteFuncs);
lua_pushvalue(L,-1);
lua_setfield(L,-2, "__index");
// Register the base class for instances of Sprite
luaL_register(L, "Sprite", gSpriteFuncs);
}
We can then implement a method of Sprite like this
{
int n = lua_gettop(L); // Number of arguments
if (n == 2) {
Sprite* sprite = checkSprite(L);
assert(sprite != 0);
real speed = luaL_checknumber (L, 2);
sprite->setSpeed(speed);
}
else
luaL_error(L, "Got %d arguments expected 2 (self, speed)", n);
return 0;
}
To extract the pointer to the C++ object and make sure it is of the correct type we use the following code:
{
void* ud = 0;
luaL_checktype(L, index, LUA_TTABLE);
lua_getfield(L, index, "__self");
ud = luaL_checkudata(L, index, "Lusion.Sprite");
luaL_argcheck(L, ud != 0, "`Lusion.Sprite' expected");
return *((Sprite**)ud);
}
The only thing left is dealing with garbage collection but I leave that as an exercise. You should already have the basic idea of how to deal with it. Please note that I have not tested the exact same code as written here so why the principles are correct there might be minor errors in the code. In my own code I have separated more of the code into separate functions since the code for creating an instance is almost identical for any class, as well as the code for extracting the __self pointer.
Conclusion
While using a bridge might be better for bigger projects I think it is good to know for yourself exactly what goes on under the hood and when you do it yourself you can more easily fine tune what you export and not and in which way. Typically you would want the Lua interface to be simpler and more limited than the C++ interface to your classes.
Intersection test assertion failure in CGAL
This week I fell into the first trap of computational geometry. I stored polygons in
Polygon_2
classes and then performed an intersection test with the do_intersect
function. Unfortunately that too often lead to the program crashing with the assertion failure:
CGAL error: precondition violation!
Expr: Segment_assertions::_assert_is_point_on (p, cv, Has_exact_division()) && compare_xy(cv.left(), p) == SMALLER && compare_xy(cv.right(), p) == LARGER
File: /Users/Shared/CGAL-3.2.1/include/CGAL/Arr_segment_traits_2.h
Line: 730
or
CGAL error: assertion violation!
Expr: *slIter == curve
File: /Users/Shared/CGAL-3.2.1/include/CGAL/Basic_sweep_line_2.h
Line: 591
Eventually I narrowed it down to discover that it happened in specific degenerate cases. For instance when a corner of a polygon intersected an edge of another polygon. Which means two line segments are intersecting another line segment in the exact same spot. Now I know this would normally be a problem for line sweep algorithms but I also know that any computational geometry library with respect for itself should handle such degeneracies. So I could not understand why CGAL should fail.
Well it turns out that I didn't read this part of the CGAL manual:
If it is crucial for you that the computation is reliable, the right choice is probably a number type that guarantees
exact computation. The Filtered kernel provides a way to apply filtering techniques [BBP01] to achieve a
kernel with exact and efficient predicates. Still other people will prefer the built-in type double, because they
need speed and can live with approximate results, or even algorithms that, from time to time, crash or compute
incorrect results due to accumulated rounding errors.
I had expected this kind of problem. So I thought it was because I used float. I changed to double and got the same problem. It turns out I should have used
CGAL::Exact_predicates_exact_constructions_kernel
.
String manipulation: Qt vs Cocoa
Background
I am coding a raytracer program using C++ and Qt. It is program that supports many different algorithms and file formats. One of the algorithms had some bugs that I had problems finding, so I made a OpenGL visualizer of the scene with the hope to be able to visualize different steps in the algorithm to find the error.
Unfortunately the Qt OpenGL implementation proved extremely slow. Instead of looking all over the place to find a solution I decided to create the OpenGL viewer in Cocoa. It seems like Qt4 has several performance problems compared to Qt3. Qt Designer is quite slow when editing properties. Before the last update, it was so slow to type in a text field that I had to type in another window and then cut and paste the text in. Likewise Assistant was so slow I ended up using Safari instead to browse the help documentation.
The scene I read was written for the RenderBitch raytracer. The format is basically like this:
persp 40 1 4.3 .0
material cyan
r_diff .7 1 1
r_refl 0 0 0
# this adds vertices to the scene
vertex 1.25 1 1.3
vertex 1.25 1 -3.3
That is it is a text based file format with some command at each line. So to read the file I had to do a bunch of string manipulation.
I had already done code to read this text in using Qt, so it proved an interesting comparison doing the same task in Cocoa
String classes in Qt and Cocoa
The approach used by Qt and Cocoa is quite different. When looking through Cocoa documentation it reminded me more of how I did string manipulation in script languages like Python and Ruby. Except the methods names were extremely long as is usual in Cocoa (One of the things I dislike the most about Cocoa).
In Qt you deal with strings using the QString
, QStringList
, QTextStream
and QRegExp
classes. In Cocoa the equivalents would more or less be: NSString
, NSArray
and NSScanner
. Cocoa does not have an class for regular expressions.
The difference caused by Cocoa being dynamically typed shows itself in the use of NSArray
instead of a special string list container. It is general purpose and can hold any kind of objects. Since objects in general can create string representations of itself NSArray
also contains methods to joint elements into a string with a separator like QStringList
.
One of the creates strength of Qt comes is visible when dealing with primitive types like int
and float
. By using templates and operator overloading these can be handled a lot easier than Cocoa. In Cocoa you must explicitly box primitive types.
So with Qt I could easily read in data from the text file using QTextStream
and have operator overloading automatically take care of type conversion
The example below shows how I can read in e.g. a vertex line in the scene file. Placing the vertex keyword in the keyword variable and the coordinates in x, y and z:
QString keyword;
float x, y, z;
QTextStream in(line);
in >> keyword >> x >> y >> z;
In Cocoa I would have to do it explicitly:
NSString* keyword;
float x, y, z;
NSScanner* scanner = [NSScanner scannerWithString:line];
[scanner scanUpToString:@" " intoString:&keyword];
[scanner scanFloat:&x];
[scanner scanFloat:&y];
[scanner scanFloat:&z];
However I didn't actually end up using the operator overloading in Qt. There was some benefits to doing in another way and perhaps it was influenced by my experience dealing with strings in script languages that don't have operator overloading and templates.
So instead I chopped the lines into pieces and converted each string separate like this:
QString keyword;
float x, y, z;
QRegExp whitespace("\\s");
QStringList list = line.split(whitespace, QString::SkipEmptyParts);
keyword = line[0];
x = line[1].toFloat();
y = line[2].toFloat();
z = line[3].toFloat();
This looks more complicated but it didn't turn out much different when considering that I read parameters in bulk and they were usually all floats or ints.
In Cocoa I could to similar with this code (although ironically I ended up using the stream approach there):
NSString* keyword;
float x, y, z;
NSArray* list = [line componentsSeparatedByString:@" "];
keyword = [list objectAtIndex:0];
x = [[list objectAtIndex:1] floatValue];
y = [[list objectAtIndex:2] floatValue];
z = [[list objectAtIndex:3] floatValue];
It is not entirely equal because it separates on single space and not on any whitespace. This is why I ended up with NSScanner
because it has more options on separators.
Unicode
Both Qt and Cocoa uses unicode strings. However in Qt this is not something you have to think much about due to operator overloading. You can write line[0] == '#'
and it works, despite line[0]
actually representing a unicode character and '#'
is not. But overloading of ==
takes care of the translation trasparently.
With Cocoa it is a different story, because of operator overloading.So to compare a unicode character to a range of other characters one would have to use the NSCharacterSet
class.
NSCharacterSet* hash = [NSCharacterSet characterSetWithCharactersInString:@"#"];
if ([hash characterIsMember: [line characterAtIndex:0]]) {
''''// Do somehting...
}
Of course in this particular instance when we are just checking for a character at the start of the line there is a simpler solution:
if ([line hasPrefix:@"#"]) {
''''// Do somehting...
}
Experiences with the whole development cycle
It is of course hard to compare the development effort for both environments for this task given that I have much more experience with Qt and did the program in Qt first and then later in Cocoa. So it might even out in the sense that I didn't have to think that much about program design in Cocoa but I had to look up more documentation on how to deal with strings.
With debugging I think Cocoa overall was easier to deal with than Qt. Although I have noticed earlier that the weak typing used in C can cause from problems in Cocoa programs. A number of type mistakes are more easily caught using Qt because it uses C++ which has stronger typing than C.
Some problems with C was also enhanced by how Apple has chosen to let xCode build by default. It uses a Zero linking technique which I thought was great at first because it severely cuts down the compile and link process. However it is quite dangerous because it lets you compile and run programs with calls to functions that don't exist, or with the wrong number of parameters. Now one might argue that this is the case with most dynamic languages in general like script languages. However the combination of late binding and C is a very bad one. There are no proper error handlers when something goes wrong so you remain quite clueless to what went wrong.
However apart from that it is much easier to trace through and debug Objective-C programs in gdb than C++ programs. Almost all Objective-C classes give descriptions of themselves so they can easily be inspected. C functions and Objective-C methods are easier to call because they don't have problems with name mangling like C++ methods and functions.
For instance to debug Qt program I frequently had to make standalone methods that printed out specific objects I had to inspect.
Not an ideal solution because you have to add that code to every Qt program you debug and you have to make code for every class you want to inspect.
Since C++ methods are tricky to call, in debug mode it is hard to change their state through method calls in the debugger.
Conclusion
Although Qt and C++ have some distinct benefits like strong typing, templates and less verbose code, I found it quicker to develop my Cocoa solution because it was much faster to hunt down bugs in the program because debugging was easier.
Graphics: Qt vs Cocoa
The logic of how things work in Cocoa is quite different from most other OO toolkits, especially when it comes to graphics. Here I will give a comparison to how one does some basic graphics operations in Cocoa as opposed to Qt.
I might have chosen to compare Cocoa to Java or .NET frameworks but I am most familiar with Qt. Qt graphics model is quite similar to that use in Java and .NET so the differences explained here should be of interest to Java and C# programers as well.
Introduction
Cocoa and Qt have similar image models for graphics. Both are able to describe images independent of output device (device on which image is displayed) and information about points in drawings (vertices) are retained in the model so that different transformations can be applied to it (e.g. affine transformations.)
Programmin models
However their programming models are different. A programming model can be statefull or stateless and procedure oriented or object oriented. E.g. OpenGL has a statefull and procedure oriented programming model while DirectX has a stateless and object oriented programming model.
In a statefull programming model all drawing operations are affected by the state of the graphics system. E.g. a command to draw a box might be drawBox(x, y, width, height)
. This would draw a box with current color. The color is then a state in the graphics system. In a stateless system the command would be drawBox(x, y, width, height, color).
The reason why many people find Cocoa confusing at first is that it uses a statefull and object oriented programming model. This is uncommon as Qt, Java and .NET all use a stateless object oriented programming model. In this respect Cocoa has a lot of similarities with OpenGL.
Setting drawing target
In Qt drawing commands change the image or graphics of a QPaintDevice
. Typically one of the subclasses like QWidget
is used. Below is an example on how one can draw a line of thickness 2 and blue color through three points p1, p2 and p3 on some arbitrary widget returned by getCanvas()
.
QPainter paint(w);
QPen pen(Qt::blue);
pen.setWidthF(2.0);
paint.setPen(pen);
QPoint p1(x1, y1);
QPoint p2(x2, y2);
QPoint p3(x3, y3);
paint.drawLine(p1, p2);
paint.drawLine(p2, p3);
As one can see the surface to draw on is provided specifically to the QPainter
object as well as the pen and color to use. The graphics system is not in any state. The result of the drawing operations is determined exclusively by the objects involved an their attributes.
Below is an example on how to do the same with Cocoa:
NSBezierPath* path = [[NSBezierPath alloc] init];
[[NSColor] blueColor] set];
[path setLineWidth:2.0];
NSPoint p1 = NSMakePoint(x1, y1);
NSPoint p2 = NSMakePoint(x2, y2);
NSPoint p2 = NSMakePoint(x3, y3);
[path moveToPoint:p1];
[path lineToPoint:p2];
[path lineToPoint:p3];
[view lockFocus];
[path stroke];
[view unlockFocus];
As one can see there are some noticeable differences. There is no one object that represents the state of the graphics system like QPainter
. NSBezierPath
might look like it but it merely keeps track of points on a path and how the lines that connect the points are. No color information is passed to NSBezierPath
in the form of a NSColor
object nor is the surface on which to draw specified as in the Qt example.
Instead the state of the graphics system itself is changed. [color set]
is used to change the color state of the graphics system. Likewise [view lockFocus]
is used change the state for current drawing area in the graphics system. Usually when a views method to update its area is called, the state of the drawing area has already been set to that view. So most of the time the user does not have to call lockFocus
himself.
Drawing bitmaps
One are that is difficult to understand once one gets started with Cocoa graphics is how to deal with bitmaps. It is confusing precisely because when one is not used statefull programming models for graphics it is not obvious how one draws into a bitmap.
Both Qt and Cocoa have two different image formats. However comparing them is difficult because there is no one to one correspondence in functionality. Qt has QPixmap
and QImage
. QPixmap
is used for drawing images that will go on screen. Because graphics can be created in many different ways on screen individual pixel access is not possible. QImage
on the other hand exists off screen in memory an allows individual pixel manipulation. However to put a QImage
on screen it has to be converted to a QPixmap
In Cocoa there is a similar situation. NSImage
vaguely coresponds to QPixmap
. You can not access its individual pixels but you can draw into it and display it on screen. NSBitmapImageRep
corresponds roughly to QImage
. You can access its pixels individually and set up exactly how pixels are stored, how many color components are used etc. However until recently you could not draw directly into it. Instead you would draw to a NSImage
. The reason for this is that NSImage
can represent an offscreen window, while NSBitmapImageRep
is just a bunch of bytes in memory. Windows area can be drawn on by graphics hardware so they can be represented in any number of ways. They could exist in special graphics memory or on a remote machine. Thus accessing individual pixels on an NSImage
makes no sense. However giving drawing commands does because the graphics hardware have drawing commands implemented to do it fast. Doing drawing on NSBitmapImageRep
does not make sense because it is not accessible to graphics hardware and drawing can be done by graphics hardware.
Below is an example on how to manipulate the pixels in a QPixmap
QImage img = pixmap.toImage();
for (int y=0; y < img.height(); ++y) {
''''for (int x=0; x < img.width(); ++x) {
''''''''QRgb pix = img.pixel(x,y);
''''''''doSomethingWithPixel(&pix);
''''''''img.setPixel(x,y,pix);
''''}
}
The code below shows how to do the same thing in Cocoa. That is, to manipulate the pixels in a NSImage
. Notice how you must lockFocus
on the NSImage
to for the pixel grabbing to occur on that particular image.
NSRect rect = NSMakeRect(NSZeroPoint, [pixmap size]);
[pixmap lockFocus]; // Make pixmap target for drawing commands
NSBitmapImageRep* img = [[NSBitmapImageRep alloc] initWithFocusedViewRect:rect];
[pixmap unlockFocus];
for (int y=0; y < [img pixelsHigh]; ++y) {
''''for (int x=0; x < [img pixelsWide]; ++x) {
''''''''NSColor* pix = [img colorAtX:x y:y];
''''''''doSomethingWithPixel(pix);
''''''''[img setColor:pix atX:x y:y];
''''}
}
[pixmap addRepresentation:img];
Drawing images on screen
To round I need to show how you draw images on screen, or more specifically in a window. A
QWidget
in Qt's case and a NSView
in Cocoa's case. Below we can see how to draw rectangular area within an image given by srcRect
inside a rectangular area inside a window (QWidget) given by dstRect
Pixmap pixmap = getImage(); // Return some image we want to draw
QPainter paint(w);
QRectF dstRect(x1, y1, width1, height1) // Area of window to draw into
QRectF srcRect(x1, y1, width2, height2); // Area of image to draw
paint.drawPixmap(dstRect, pixmap, srcRect);
Below we have the corresponding code for Cocoa. Notice that the drawing method is called on the NSImage
itself. This does not however mean that drawing is performed inside the image as Qt, Java and C# programmers would easily assume. The target for drawing commands is always the surface/canvas which has focus.
NSImage* pixmap = getImage(); // Return some image we want to draw
NSRect dstRect = NSMakeRect(x1, y1, width1, heigth1); // Area of window to draw into
NSRect srcRect = NSMakeRect(x2, y2, width2, heigth2); // Area of image to draw
[w lockFocus];
[pixmap drawInRect:dstRect fromRect:srcRect operation:NSCompositeCopy fraction:1.0];
[w unlockFocus];
Final thoughts
It should be clear that drawing in Cocoa takes some time getting used to for Qt, Java or C# programmers. I have only scratched the surface in this post. From my own experience using both Java and Qt, it is a lot easier to get up to speed on graphics in Qt and Java at first.
However as is typical with everything else with Cocoa it might not be fast to get started but when you dwell into more complicate things, that is when Cocoa start to shine. Likewise with graphics. My own impression from using it (although I am no wizard in Cocoa graphics) is that when it comes to more complicated graphics Cocoa is much more capable than Qt.
It is also my impression that for short examples as given here Qt and Java would usually require less code, but when the graphics is more complicated less code is required in Cocoa.
However I must say that dealing with bitmap graphics seems overly complicated at times in Cocoa. Probably owing a lot to the fact that NEXTStep had totally vector based graphics. The graphics system would output post script drawing commands to the window server and not individual pixels. Basically vector graphics was made easy at the expense of pixel manipulation and raster graphics.
Thoughts on Objective-C 2.0
One of the most exciting changes coming in Mac OS X Leopard, is the new xCode 3.0 with a major update of the Objective-C language. While I think Objective-C is a great programming language and Cocoa is a great framework, it is getting a little bit long in the tooth in a few places.
This has attracted considerable bashing from different people. Nitesh Dhanjani of O'Reilly for instance started a big debate about whether Apple should offer more choices to developers.
The basic argument is that Microsoft is way ahead of the curve with C# and the .NET framework. .NET allows developers to choose any language in theory, thus giving them more choice. This is not something apple offers at the moment.
However with Objective-C 2.0 it seems like Apple has narrowed the gap to more modern languages like C# and Java. It is not official yet but it seems like Objective-C will offer:
- Optional garbage collection. This was actually an option in Tiger, but toggling it didn't do anything.
- for each statement. Which makes iterating through objects in containers easier. This is similar to the C# for each.
- Properties. You don't need to explicitly use setter and getter methods. If you access or set a variable, access methods will be used if they are defined.
- Method attributes. Not so sure about this but apparently it is similar to what is found in C#.
Here are some examples of the new syntax. The for each syntax:
for (NSString *s in arr) {
NSLog("%@\n", s);
}
And here is an example of properties:
int foo;
float baz;
}
@property int foo;
@property(getter=_getBaz,setter=_setBaz) float baz;
@end
@implementation MyClass
@synthesize foo;
- (int)_getBaz { return baz; }
- (void)_setBaz: (float)newBaz { baz = abs(newBaz); }
@end
int main(void) {
MyClass *m = [MyClass new];
m.baz = -5.0;
printf("%f\n", m.baz); /* -> 5.0000 */
}
I think this means that Mac developers don't have to consider themselves second class citizens. Although I think it never was that bad. A lot of the advantages of .NET and C# are highly theoretical.
Even though you can make any language that language is still limited in functionality to that of the IL code and the .NET framework. The Cocoa framework exploits a lot of the particular strengths of the Objective-C language. It would be difficult to make a framework similar to Cocoa within the .NET world.
So far it seems like most .NET developers are using C#. The other major .NET languages offer little extra functionality or benefits.
Even if Apple does not have an IL type language on which to build other languages they are not really that trapped in language offerings. Because of the highly dynamic nature of Objective-C which is based on message passing instead of method invocation. Other dynamic languages can easily be made to interface with Objective-C classes. Some examples are Ruby Cocoa and PyObjC, which is a Python bridge to Cocoa. One only needs to create a bridge or in other words code that translates the message passing in Objective-C to that of the other language. E.g. in Java there needs to be defined a type for each Objective-C class to maintain the bridge. Unknown Objective-C classes can not be used. For more in-depth look at the problems with bridging Objective-C and Java look at cocoadev.
Performance
Another important point is that I think Objective-C is simply a much more versatile language than C#. Although high performance can be squeezed out of C# it is not nearly as easy. "Easy" is a keyword here, because no language can really do anything another language can't. All popular programming languages today are Turing complete. The difference is then about with what flexibility, ease and performance one can do a certain task.
In a lot of language you can create modules written in C for critical sections and call them from the "mother" language. You can do this in Java through Java native calls. In Ruby you can add your own classes written in C. In C# you can mix managed and unmanaged code. E.g. you can have certain sections written in plain C++.
However the ease which one does this differs. Objective-C is a superset of C and interacts with it easily. Writing performance intensive parts of the program is thus easy and straight forward. One simply tries to use as much plain C as possible and as little object oriented features.
The same can not be said about .NET. I have learned the hard way that mixing managed and unmanaged code is no trivial task. It looks easy in theory and doesn't seem that hard when one does it with small example code. But as soon as you have to deal with a large real world application, problems start piling up.
This means that for performance critical tasks it will be much easier to fine tune a Objective-C application than a .NET application.
Missing features from Objective-C
So if the rumors are right, what will be missing from Objective-C to make it competitive with C# and other modern languages? I could actually only thing of one thing and that is support for namespaces. At the moment this is solved by prefixing class and function names with two letters, to distinguish them from classes and functions with the same name.
Some might argue templates or generics is missing but that is only something that is needed on a static language like C++, Java and C#. In Objective-C data types (except primitives) can be easily inserted and accessed from a container class. No up-casting is needed.
Conclusion
Mac development is going to be better than ever. The only thing that I miss now is a better IDE. I think the debugger in xCode has a lot of problems. It should be improved and so should code completion. Code navigation and re-factoring support could also be improved. At the moment I use TextMate a lot for my coding because it is a lot faster to jump around in code. Apple+T brings up a window that does name completion of files so I can jump to a new file with some quick typing. it also does tabs.
Perhaps Apple should simply improve the integration of the code editor with third party tools. For instance allows some hooks so that after a failed build one can jump to errors in an external code editor.
Also they should do something to reduce the amount of windows that are open at any given time. I do like multiple windows because it allows you to organize your work more when you have a lot of screen real-estate. But at the moment it just seem to become too many windows to keep track of: debug window, code window, run window, console window, project window etc.
If not reducing the number of windows at least provide a better way of managing them (although I guess Spaces might solve a lot of the problem).
My last wish is that Cocoa method names was a bit shorter. Do we really need:
Isn't it just as readable to simply write:
Actually I find it almost hard to read Objective-C code at times because the Cocoa method names are so long that you can't actually glaze at them to see that they say. E.g. "thisIsAVeryLongMethodNameIndeed" takes some time to read. Of course this is all wishful thinking because it is too late to change the names not. But one can always dream.
-
It has been said that design patterns is a symptom of language deficiency. If you need a pattern that indicated a language feature should ha...
-
I never cease to amaze me how I am always learning new things when programming C/C++ even after this many years. However I thought that most...
-
On the surface Go and Java might seem to have a lot in common. They are both modern C/C++ like languages with garbage collection, supporting...