Development Tools • 53:52
Learn about Apple's enhancements to the GNU C Compiler (GCC) in the Project Builder IDE. Enhancements include reduced compile time, improved code generation, better IDE integration, and other new features.
Speakers: John Graziano, Devang Patel, Zem Laski
Unlisted on Apple Developer site
Transcript
This transcript was generated using Whisper, it has known transcription errors. We are working on an improved version.
Thank you, and thank you for coming this morning. We're on the fourth day of WWDC. I imagine a lot of you are beginning to hit information overload. I hope you still have a lot of energy because we have a lot to present in this session. Compilers are such an essential part of development tools.
We're all affected by their performance. We have a lot of things to say about that. We'd be interested to know how many people in the audience have downloaded the Darwin source set. Just raise your hands. About half, two-thirds. Okay. How many people actually contribute to the Darwin repositories? A lot fewer. Okay? With no further ado, I'd like to introduce John Graziao, the manager of the compiler team.
So today we're going to be talking about Mac OS X compiler technologies and I just wanted to say a little bit about what those are. On my team, we handle the compiler which is GCC, the assembler, the linker, the dynamic loader which is responsible for starting up your application, and a lot of utilities like Strip, NM, O'Tool, and a few others.
The point that we want to make throughout this talk is that while all of these are usable as command line tools, the recommended way that we want for people to use GCC is through Project Builder. We've got a lot of nice things in Project Builder to integrate with it. So we're not going to cover all of these things today, but what we will talk about is how to use GCC, give you a little overview of the architecture, touch a little bit on dynamic linking and pre-binding. and also talk about some of the upcoming features.
Okay, there's two slides on upcoming features, so don't get too excited. So the GNU C Compiler. This is the main compiler for Mac OS X, and for those of you that are curious looking for it, it's basically User Bin CC for Unix heads in the audience. It's free software by the Free Software Foundation. If you're curious about that, you can go to GNU.org on the web and look around there. It supports ANSI C, C++, and Objective C. It's got a very robust C++ implementation, very robust C++ front end.
It's got many years of work and testing, compiles for tons and tons of platforms. It's basically the reference compiler for many developers. It's used in education all over the place. And the most important thing is it compiles all of Mac OS X. Every piece of source code that runs on Mac OS X is compiled. goes through GCC.
So a little overview of GCC for those of you that aren't familiar with it. It is a command line tool. And it basically takes C files and puts out .o files or object files which we then send to the linker. But behind the scenes what's actually going on is GCC is a driver that first invokes There we go. It's the only graphic I have today. I want it to work. It first invokes a preprocessor which outputs preprocessed source code.
That then is fed into the compiler, which outputs assembler code. It then invokes the link, the assembler, which outputs object code. And finally that goes to the linker, which binds everything together and creates your application or your library or whatever you're making. All this happens behind the scenes, but the important thing to keep in mind is that there are a lot of switches, if you look at the documentation, that can be sent to any portion of these sub-processes to control a lot of what goes on during this process.
So what we've done at Apple is we've taken GCC and we've extended it in a few ways. We've added Project Builder Integration. We've added Objective-C. This has been added for a while, but now I'd like to-- I'm happy to say that we are the owners of Objective-C for the Free Software Foundation. We're the maintainers. We support our frameworks. We support our version of pre-compiled headers. We have support in for the Velocity Engine, which most of the compiler people in the audience know as AltaVec. And of course we support our Maco object file format.
There's a few differences that you have to keep in mind for GCC. The first one is that we have some stricter ANSI C++ compliance. So if you're used to compiling on Mac OS 9, you may find out that you get a few more warnings and maybe a few more errors compiling your source code on Mac OS X. Mostly in the area of C++ syntax and especially type checking. Another thing to keep in mind that will bite a few people rarely is that our static initializers will execute possibly in a different order from Mac OS 9.
Because of the way that we link, because of the fact that we have pre-binding, which I'll talk about a little bit later, if you have static initializers which have implicit reliance on one another, it's not explicit. If you have a dependency graph that we can follow, we get that right. But if you have implicit dependencies through global variables or something, you may find out that the behavior will be a little different when you build and run on Mac OS X, so that's something to be careful about.
So, since public beta, we've added a few things and done a lot of work on GCC. The first thing is we're fully based now on GCC 295.2. We used to be running, I think, the driver and the preprocessor from 272, and now we're running everything in the 2952 code base. We've also added a lot of C++ improvements, coalesce symbols, zero overhead exceptions, and we've made precomps usable from C++, which has a dramatic effect on compile time for C++.
The first thing we added is coalesced symbols. What this does is removes multiple copies of functions that has been created through inlining and templates. What we've seen is the finder binary went down about 300K from 2MB to 1.7MB. And we've seen similar reductions in other C++ programs, especially PowerPlant programs.
And this is giving us an overall reduction in the system memory footprint. And you're going to hear this theme returned to over and over through this talk. The most important thing you can do to optimize your application on Mac OS X to make it run really fast is to reduce the system memory footprint, reduce the working set in memory, because you can optimize the CPU usage as much as you want, but as soon as your app starts paging in and out through the virtual memory system, you're going to get slower.
So another thing we've done along this route is added zero overhead exceptions. Before, what we do is put a set jump in, a system call, when you have a try block. Now we've removed that. So try blocks are basically free, as free as any code that's not inside of a try block. The common case is very fast, so we've seen about 2x improvement in some real world apps. For leaf functions, we've actually seen about a 20x improvement, if it's just a bare leaf function that has a try block in it.
We've also seen size reduction. We knocked the finder size down by an additional 400k when we added zero overhead exceptions. The big thing to keep in mind, though, is for throws, this will add a memory penalty and a speed penalty, especially for the first time you throw, because we do page in a lot of code to unwind the stack at that point.
So our basic policy is we treat exceptions as exceptional conditions for error messages. If you have something that's in your main line, a processing that uses C++ exceptions, you may see a slight performance lose with zero overhead exceptions. But the vast majority of people are going to see a big win.
What we've also added are precompiled headers usable from C++. What we've seen in here is compile times improve as much as 5x for Carbon applications. So the way that we did this is precomps can be accessed from C++ now. We have an extra C++ aware precompiled header in all of our umbrella frameworks.
But they cannot contain C++ syntax due to a limitation on the way that we do precompiled headers. And the other thing is that macros and compiler flags have to match as with most precompiled headers. If you change a macro setting, the precompiled header will not be able to be used. We'll have to go back to the standard text header.
So I'd like to talk a little bit about how we use GCC on Mac OS X. So, and some of the things I'm going to cover are just Project Builder Integration, how you can use and take advantage of precompiled headers, optimization, and I'll touch a little bit on Altevec as well.
So GCC and Project Builder have been designed for a while to work very well together. Project Builder is our preferred way to use GCC on Mac OS X. It's got a lot of nice integration for you. It handles most of the command line switches with GUIs, most of the most common command line switches, and there's also ways to access some of the uncommon command line switches directly from Project Builder.
It has integrated error messages so that when GCC has an error, a syntax error, it comes up in Project Builder. And those of you who have been at the Project Builder session, how many of you have been at the Project Builder session? So we didn't call that out this year just because it's been around for a while now.
But basically GCC actually sends a message up to Project Builder and Project Builder will display the error message and jump to the line in source code. GCC also provides source code indexing information to Project Builder. So for any cross-reference information that you need doing global searches or running the new class browser that we have in Project Builder, all of that comes off of information that the compiler supplies.
Let's talk a little bit about the Precompiler. The Precompiler creates a file of preprocessed and tokenized headers. So it basically takes the group of headers that we have for say Carbon or Cocoa and it tokenizes them and puts them all into one big file that we can then access.
The big thing for us is that it not only does this which reduces some of our I/O costs and reduces some of our lexing costs, but it also at compile time selectively unparses the declarations based on what's used. Some of our measurements when we're working on compile time, we see that 174,000 lines of declarations get brought in from Carbon.
So, not everyone uses, obviously, even a small fraction of that. So, the big win in the precompiler is it can selectively pull in those declarations that you're using. What this does is it reduces the size of that pre-processed file that we generate and reduces the overall memory footprint of the compiler itself so it doesn't have to push out the IDE to make room for the parsing that it has to do.
So when we're using precompiled headers, we already supply you with precompiled headers for the frameworks that we ship for the major umbrella frameworks. So Carbon and Cocoa already have their precompiled headers. If anybody's gone nosing around inside of the framework, you'll see a .p file living in there, and now a .pp file, which is the C++ version of those precompiled headers.
Project Builder also supports creating your own set of precompiled headers. You can actually have more than one precompiled header. In a minute I'll bring Devang up to show how we do that. And the preprocessor will automatically find these precompiled headers, so you don't have to worry about telling Project Builder or telling the preprocessor where these headers are. We'll look for them if they're in the same spot as the .h that it was created from.
So again, there are some limitations to precompiled headers. Macro settings, compiler settings, and the timestamps that they were built on have to match. If there's a change in them, we have a fallback position where we go to using the text-based header, which is going to slow down your compile times. They can be included from C++ files, but they cannot contain C++ syntax. So I'd like to bring up Devang Patel and we're going to go through how you create your own pre-compiled header with Project Builder.
So can we have demo two up on both of the screens here? So what we have is just the example project appearance sample, and we brought it up in Project Builder. And the first thing we've done is we've pre-cooked a small header. And if those of you can read this, what Devang's gone in and done is set a macro to be debug level two.
What this is going to do is basically break the relationship between this project and the pre-compiled header that we ship with the Carbon Framework. So the compiler will fall back on using the text-based header and bring in all 174,000 lines of declarations. So let's see what that looks like.
We've basically just tweaked one file And let's go ahead and build that. So we're going ahead and building that, and you're seeing as this happens that we get a lot of warnings saying that we can't use the pre-compiled header. And there we're done. And that was one file, just to compile all of those declarations.
So, what to look for if you're seeing slow compile times with C++ is definitely look for those warnings. Make sure that you're using the precompiled header. Now what we're going to do is go to the build settings for this target and actually make our own precompiled header. Now the first thing that's done, that's a new feature in Project Builder, is that you can actually add an implicitly included header so that every header comes into the project, so that every source file in the project uses this header.
Some people are softly clapping. This is a good thing. The other thing that we have is we've got a switch that you can just click and you can precompile this header. You can obviously add as many of these as you want to this table and you can precompile all of them.
So what Devang has done is he's actually clicked that he's going to precompile this. And what we'll do is go back and click Build again. And you'll see that in addition to doing some of the prep work, what it's saying now is it's running precompile header. And it's actually creating the two precomps that we'll use in this project. So that's done. So now let's go back and run the same build again. So Devang's just going to tweak this a little bit. And Command S.
And there we go. So what we're seeing is on average about 5x improvement in your compile times when you're using a precompiled header. And this is, you know, the sort of preferred way that a lot of IDEs get their compile times up is by building precompiled headers. And the big thing for you is getting out the, you know, the huge amount of declarations we have in Carbon and Cocoa and being able to get those in a tokenized fashion.
You can selectively use them and speed up your compile time significantly. Now the other thing that Devang is highlighting, because we didn't have a slide on this, is for those of you that are interested, there's a command line tool called PDUMP. If you're not able to use your precompiled header for some reason, you can't figure it out, or if you just want to poke around inside of the precomp and see what's in there, you can use PDUMP.
You can use PDUMP. And PDUMP-help, dash dash help, which will give you a lot of information on what you can pull out of there. So it might be an interesting exercise just to poke around inside the precomp and see what's in there and how this stuff is recorded. So that's it for the demo. Thanks Devang.
Let's talk about optimization. So the two things I want to talk about here are controlling the compiler optimization levels and actually what they do. So I'll warn you, this will descend a little bit into compiler geekery, but not much. So the first one is Optimization Level 0. It's basically the default for GCC.
If you pass no optimization flag on the command line, you get no optimization. You can also pass "-o 0." The reason they have that is it allows you to override a previously passed optimization flag. It does no optimization. So what you get is slower and bigger code, but code that you can easily follow in the debugger.
It also gives you the fastest turnaround time, but compared to something like using a precompiled header versus not using a precompiled header, it's basically in the noise. And it's the default setting for Project Builder deployment builds, or Project Builder development builds. So when you set the build style in Project Builder to be the development build, what you get is unoptimized code.
So Optimization Level 1, basically -01 on the command line, turns on a bunch of basic optimizations. It turns on register allocation, it turns on data flow analysis, dead code elimination, some jump optimizations, and also peephole optimization. I won't get into, for those of you that don't know what that is, don't worry about it. It basically turns on some sort of basic level of statement level optimization. The key point here is that it results in much smaller and faster code than level 0.
So, for those of you that can read the bottom line here, it says binary sizes can reduce as much as 50%. We did some initial testing on the Finder front end and a couple other things. We built them unoptimized. We built them with -01. And the size of the binary went down by 50%, and you should expect that your code execution, just raw CPU, throughput will go up by about a factor of two between unoptimized code and optimizing it at -01.
For Optimization Level 2, this provides sort of function level optimizations. It brings in common sub-expression elimination, loop optimization, strength reduction, and it turns on the instruction scheduler. It produces faster, though not necessarily smaller, code than -01. So we'll see some benchmarks that rely on these optimizations going up by about 10 to 15 percent.
But the code will not get that much smaller. And in fact, in some cases, if you have a lot of loop unrolling going on, the code actually grows by a little bit. The other thing is, because we're doing statement level optimization at this point, we're doing things which will move statements around, will move variable settings outside of loops. What you'll see in the debugger is that sometimes as you're stepping, you may not go exactly where you think the source code is going to go. You also may have trouble finding variables that have been hoisted into registers.
So the other optimization level that we support inside of GCC is optimization level 3, which turns on the inliner. So this is automatic function inlining, not just when you say inline inside of C++. So in some cases, this can result in faster code, but the thing to keep in mind is it might increase your binary size. So you may get faster code on a benchmark or faster code when you test one particular piece of your application. But overall, you may bloat your binary enough that you start paging a little more and cancel out the results of getting this extra inlining.
So profiling is very important. Measuring this is very important to see what kind of performance you're getting if you're using -03, just to make sure that you're doing the right thing for your particular application. And I'll point you to a session that's happening in here at 5:00 today on the performance tools where Robert Bowditch is going to get up and talk about some of the tools you can use to make sure that you're doing the right thing for your particular application. talk about some of the tools you can use to measure your application. And of course when we're talking about doing automatic inlining, debugging will get, again, a little more difficult for you when you're tracing through inside of the debugger.
So, some recommendations for optimization. We always use internally no optimization when we're building, when we're just doing development builds. This lets us get access to all the stuff in the debugger. There's no statement movement or anything that confuses us. It gives us the best turnaround time. It's generally what we recommend for external developers as well. Our build style gives you -0 when you choose the development build style.
We also recommend for deployment builds for most people to use -02. It doesn't have a big effect on the size of your binary over -01, but turning on any optimization is a big win. Turning on -02 gives you that extra 10-15%. Again, it has that trade-off that if you're using -02 and then trying to debug it, you may run into a couple confusing situations. And again, -03 when you have CPU critical cases, when you're trying to get that last ounce of performance out of it, but definitely make sure that you're not loading your binary to the point where it's not, it's canceling out the results results of the change.
So just a little bit on AltaVec in GCC. The big thing to keep in mind is that we support all of the Altevec intrinsics in GCC. So if you're used to using Altevec through Code Warrior, the same thing applies. You'll be able to use that same code inside of Project Builder, inside of GCC. And again, it requires though that you pass a -f Altevec flag to Project Builder, which we can show you how to do. If I can bring Devang back up here.
[Transcript missing]
There's sort of a general table called Build Settings. And for the compiler flags that aren't represented inside of the GUI, you can actually get at these through this table. This is sort of an expert view for people. But all of these flags basically correspond, all of these uppercase variables on the left-hand side, correspond to variables that get passed into our build system and expanded.
Some of the stuff that you're going to be interested in looking at are other C flags and other LD flags and then optimization C flags. What you can do there is basically set, here Devang is setting the -f altavec flag for those of you that can see the font.
The other thing that's nice if you're really a gear head is you can pass in flags that turn on and off any of the individual optimization passes that GCC does. So if you want to turn on loop unrolling but you don't want to have strength reduction, for example, you can pass in the individual flags inside of these C flags.
For those of you that are wondering where are all these things documented, in the project builder release notes, there's a build.html file. And if you go and look at that, the settings that are listed here are all documented so you know what's going to happen when you set some of these different settings.
Again, this is just basically an expert view, but if there are flags that you want to get at that aren't represented in the GUI, this is where you go. And you should be able to basically, you know, for people who are used to using GCC from the command line, this gives you access to all the flags that you want to use. So, thanks, Devang.
So I want to say a couple things on dynamic linking. This is something that happens outside of the compiler. This happens with our dynamic linking process called DYLD. And this is how your app starts up. Basically, when your app is compiled, the references are not resolved. And this is how we actually use shared libraries. Basically, if you compile your app that has dynamic references into shared libraries, when your app starts up, what happens is we first bind the references to functions and data at startup time.
The function references allow us to do lazy initialization. So in the basic case, When you start up your application, the function references are not resolved until you actually call through to those functions. The thing to keep in mind is that any data references, if you're referencing global data, those references have to be resolved at startup time before you hit main because we have no way to trap on first use and go and run any initialization routines that have to happen. So if you're accessing data, some library init routines may have to be run before main actually starts up.
So the reason we're talking about this is because I also want to mention this thing we have called pre-binding. What pre-binding does is it actually sets the dynamic link addresses at static link time. So if you're running against a shared library, the linker will actually go out and grab those addresses and set them up at static link time. This can give you a dramatic improvement in launch times because you're not having to search through the symbol tables of the libraries at all. And most importantly, again getting back to the memory theme, you're not having to page in those symbol tables to do the search.
The tradeoff is this gives you no lazy initialization. All of this stuff has to be set up at compile time. So we're just going out through addresses at that point, so we can't trap on first use. So we have to actually initialize the libraries and the modules before the app actually hits main. In a lot of cases, this doesn't give you a dramatic slowdown. It's still a big win to do pre-binding, but that's definitely something that is a trade-off.
Applications are built by default prebound in Project Builder. So this will give you prebinding if you're building an application that links against the system libraries, this will give you a prebound application. But the big thing to keep in mind is that binaries which are prebound have to be updated when the libraries change.
So, for those of you that have been poking around the web and seeing this thing on what is all this system optimization that's going on, I just downloaded the developer package and it's optimizing my system again. What's basically happening is it's going through and it's re-prebinding those binaries. So it goes through, it looks for binaries on the system, and it basically fixes up the addresses to the libraries as they've been updated.
So, you know, once that's been run, the libraries are prebound. The Mac OS X GM shipped prebound, so running that on a stock Mac OS X GM system isn't going to get you anything. But when libraries are updated, going through and re-prebinding this actually is a way that helps us fix up our binaries so that they run as fast as possible.
Let's talk a little bit about upcoming features in the compiler and in the compiler toolchain. So the first thing we're going to talk about is GCC3, what's coming with that. We'll talk about a feature in the linker called two-level namespace, which most of you who have worked in the CFM world are already used to. And of course, what a lot of you are here to hear about is Objective-C++, and then we'll talk about some things that are coming further down the road. Soft applause for Objective-C++.
So GCC3 is what we're currently working on. We've got it building some of Mac OS X, but not all of it right away. What this gives us is an integrated preprocessor. It gives us some better C++ support, better C++ compliance. It has a lot of code gen improvements, especially for the PowerPC.
And something to keep in mind for people as we're rolling this out is it does give you C++ ABI changes. So if you're using a shared library that contains exported C++ calls, exported C++ classes, or anything like that, there will be ABI changes. You'll have to ship a new binary for that if you compile it with GCC3.0.
The other thing we've added is two-level namespace. Before what we had on Mac OS X on public beta and on GM is a flat namespace. So all of your libraries and all of your applications, just basically all the symbols went up into a big pool. And if there were any name collisions, you basically lost at that point.
The thing to keep in mind is that if you're loading bundles, if you're loading plugins, we actually have a two-level namespace implemented for plugins. So things like QuickTime and other applications with load plugins already can take advantage of this. What we're doing is expanding this feature to the rest of your binary the way that it works on Mac OS 9. So we link now by symbol name and library name.
What this does is it protects against name collisions in modules. So if we add a new API to our libraries that conflicts with a function that you call in your application, it doesn't break your application now. So as we update libraries moving forward, it won't mess up the applications that you already have. This also speeds dynamic linking for non-prebound applications because instead of having to search the entire set of symbol tables, we go right to the library and we look for it there.
The thing to keep in mind for people who are used to compiling and the flat namespace coming over from Mac OS X Server or having done some other Unix applications, there is one restriction that you have to keep in mind. That if you reference a symbol now out of a library, you have to explicitly include it on the link line.
So in case you're wondering about compatibility, Mac OS X GM apps work just fine on the new system. So we'll have a two-level namespace system. Things that have shipped for Mac OS X GM will work just fine. Two-level apps will also work just fine on GM systems. So you build your app for two-level namespace when this is rolled out and it'll run fine on Mac OS X GM. And any combination of flat and two-level libraries will work as well. So you can have a two-level app that links against a flat library that links against another, you know, our system libraries, which will be two-level at that point. And that will all work just fine.
So Kevin Enderby is the engineer that worked on this and he did a really good job making sure that all these corner cases got covered. He'll be on stage later so you can ask him some questions about this. So now what I'd like to do is bring up Zeb Laski who's the responsible engineer for Objective C++ and he's going to walk you through what we've been doing on Objective C++ and what some of the details are there. Welcome Zeb. Thank you much.
Thank you. Well, as John mentioned, I did do most of the work, so at least for the initial release, if things break, you can blame me. So basically what I'd like to do is just give you a really broad and really quick overview of what Objective-C++ is and what it is not.
So the most important thing to remember—actually, there are two very important things. The first one is that it's really just a combination of C++ and Objective-C, or perhaps more accurately, it's a superimposition of Objective-C on C++, with C++ sort of being the senior partner. And the reason we did this is very simple. We would like to allow C++ programmers to utilize the numerous Objective-C frameworks that we have on the system, most notably Cocoa.
In conjunction with this rollout, we concocted a new file extension, .mm, which should be used for Objective-C++ source code. There's a timer here. Some of you who are coming over from the next days might also have seen that .capm as an extension, but we hereby declare this to be no longer politically correct.
The HFS file system is case-preserving but not case-sensitive. There are also other issues, so we strongly encourage you to move away from .capm. A few words about the implementation as it currently exists. It is a separate compiler, a separate frontend that I was working on, which is separate from the C++ frontend and the other ones. And now I come to the second important point, the first one being the combination of the two languages. The second important point is that an Objective-C++ program, once you compile and run it, interacts with two runtimes: the C++ runtime and the Objective-C runtime.
Now I'm not sure how much you're aware of one or the other, which direction you're coming from, if you're coming from the C++ world, then you probably know more about the first one. If you're coming from the Cocoa/Next-Step world, you probably know more about the second one. But the important thing here is that these two runtimes are separate.
They are different, they have different semantics, and at least at present, they are mutually unaware of one another, or as I like to say, mutually oblivious of one another. Syntactically, the Objective-C constructs and C++ constructs are also separate in the sense that you can easily tell just by looking at a construct which language it belongs to, which of course makes life a lot easier for us in implementing it, and also for you in terms of figuring out what is what and what semantics go with it.
So first off, I'd like to give you sort of the good news, the ways in which you can mix C++ and Objective-C code. So first of all, you can declare an instance of an Objective-C object, which is really a reference or a pointer to that object, like in Java. You can declare that anywhere you have a C++ declaration. So wherever you can declare a C++ variable, you can declare these Objective-C variables. Secondly, Objective-C classes, the declarations of the classes themselves, may contain within them C++ classes or C structs.
There are some exceptions to that, which I will get into later. Objective-C message sense, which you see in this square bracket notation there, if you haven't seen this before, this really is a typed expression. So you can use that anywhere you have C++ expressions in your code, and an example is forthcoming.
And finally, Objective-C++ relies on the C++ type checking semantics for some of the lower level type checking. And the reason I bring this up is that if you're used to Objective-C coding, it relies on C type checking. So for instance, if you have a signed character and you assign it to an unsigned character in C, that's usually fine. The C compiler will not complain. What you'll find here is that the C++ part of Objective-C++ will, you know, raise its head and complain. In other words, some code that is valid Objective-C code might not necessarily be valid Objective-C++ code because of the increased C++ strictness.
So here I concocted sort of a two slide example showing you what an Objective C++ program looks like. And for ease of understanding, I've color coded it so that the Objective C part of it is in blue and the C++ part is in yellow. So you see first off, we call this "Hello World" as we always should.
Then we import "coco.h", that's a typical Objective C thing. And by the way, John mentioned before that you can use precompiled headers with C++ now. You will also be able to use it with Objective C++. So there will be a "coco.pp" on your system when this whole thing is released, so this will go very fast.
Then in yellow you see a forward declaration of a C++ class. Then we get to a declaration of an Objective C class, which we call "println". We derive it from NSObject, which is sort of the root of the "coco" object. We have a pointer to the C++ class we previously mentioned. And then we declare prototypes for two methods in Objective C.
Then here, this is the second part of the example. First we flash out the C++ class, so you have Hello World. Here we have an ID, a member variable of type ID, which is sort of analogous to Java lang object, if you've used Java. It's sort of a generic pointer that can point to any Objective-C object.
And we define a constructor and a destructor, so you can see in the constructor for the C++ class, we actually allocate and init the corresponding Objective-C class, and that is, by the way, how you usually create an Objective-C object with this alloc init idiom. And then we sort of, you know, we say hi, so you call printf, which is a standard C function, of course.
Then you have SayHello, which basically turns around and invokes a method called SayHi in the Objective-C class. And down below, we have an implementation of the Objective-C class, which is in blue. So you can sort of see the notation here. NSLog is also a standard Cocoa function. It's actually a C function, if you really look at it.
So it basically prints "Hello World" on the screen. The example is sort of silly, as all examples are, but it kind of shows you how you can mix and match. So you can see within Objective-C, we actually use p arrow SayHi to call the C++ method. And vice versa, from a C++ class, you can invoke an Objective-C method.
Okay, and now we get to the sort of the important part of this talk in the sense that we'd like to sort of emphasize why certain things cannot be done in Objective C++. And the reason most of these things cannot be done is what I mentioned at the outset, is that the runtimes are separate.
Because they are separate, they do not know of one another, and therefore certain things really cannot happen. So, you know, number one obviously you can't do is mix up class hierarchies. If you have a, you know, hierarchy of C++ objects, that's fine. If you have a, you know, a hierarchy of Objective C objects, that's fine too.
You cannot mix them, because it will just be very confusing for the compiler and the runtime. As far as exception handling, we really encourage you to use the C++ runtime for this. The, you know, the try, throw, catch paradigm. Objective C does have some set jump, long jump based.
Exceptions, which are basically macros that expand. And we really do not discourage you from using this, because C++ just won't know what's going on when you do this. And one thing to keep in mind is that when you use C++ exceptions, you have to clean up any Objective C objects that you have lying around. So here you have a try block where we, you know, instantiate an Objective C object. And then we have a catch clause which frees it.
And then you re-throw the C++ exception to be possibly caught by some other handler in an enclosure. So you can see that you have a closing scope, you know, in your stack. You have to do this, because again, the C++ runtime will not know about my obj that it's an Objective C class or what to do with it.
This is a recurring theme. C++ classes cannot receive Objective C messages or vice versa. This is fairly trivial. I mean, you cannot, the functions in C++ that you call an object and the methods in the Objective C land are really semantically different. You cannot treat one as if it were the other. The search mechanism is different. It's, they do not mix. So, I mean, and the compiler will diagnose this correctly. So you shouldn't run into any problems there. You cannot statically allocate new or delete Objective C objects.
And actually the new and delete keywords should be in yellow, because that's in C++ construct. And the reason again is if you create an object, you have to initialize the Objective C object, which the C++ runtime wouldn't know how to do. If you're familiar with virtual functions in C++, there is always sort of a Vtable pointer at the beginning of your object. Objective C has something analogous that needs to be initialized. And C++, you know, wouldn't know what to do with that. I believe this is my final restriction type slide.
I mentioned earlier that you can embed C++ classes within Objective C classes, but there is a catch. You may not do this if you have any virtual functions. Again, this is because you would have a Vtable pointer, and in this case the Objective C runtime wouldn't know what to do with the Vtable pointer. So this really goes both ways. If you have a non-virtual constructor, like in the struct class 2 example there, we will let it slide.
However, we will give you a warning because that constructor won't be called. So if you're expecting to have the class 2 member initialized, that won't happen. And the reason we allow this is because in Objective C, you can put C structs in like this. And of course that's fine because you don't expect any initialization semantics in C. Is this my final example? I believe so.
So this is the most important slide probably, how you get your hands on this stuff. We are really very close to starting seeding this thing, having people try it out. And if you are interested in being one of our early victims, and I'm really joking. It's actually quite stable at this point.
We've used it as a C++ compiler on our system very successfully. So I'm really proud of it. Anyway, if you're interested in becoming a seed partner with us, please approach Godfrey, our session manager, at the end of our session here after the Q&A. And now I'll hand it back to John for some closing thoughts.
Thanks, Zem. I just want to say that we brought Zem on in February, and he started on this in March, basically using the old crufty 2.72 Objective C++ implementation. Crufty because of the 2.72 C++ implementation in GCC was pretty bad. And he actually got this up and running, and just last week we were able to compile the full Finder code base, which is all PowerPlant as well, with this compiler. So it's not only compiling all of our Objective C++ code, but it's also a real, honest-to-God, C++ compiler. So Zem did a great job, and I think we should give him a hand for agreeing this.
So all of you who are interested in bringing your C++ applications, your C++ Unix tools, and putting a Cocoa front end on them, this is your path to success. This will get you there. And it's a very nice thing. I personally have written a lot of Objective C++ code using the old compiler, and I'm really looking forward to being able to use this new compiler. It's going to be really great.
[Transcript missing]
So, just to wind it up, GCC is our road tested open compiler for Mac OS X. You can go out and get the sources today if you want them. It works from the command line. It's a usable command line tool. It works even better from Project Builder. We have a lot of support in there that is actually difficult to access from the command line where we take advantage of a lot of the things that the IDE provides and really use the features in GCC.
And the big thing to keep in mind is that Apple is fully committed to GCC as our main line compiler. We run all of Mac OS X through GCC. Every binary that you bring up when you install the Mac OS X GM has been run through the GNU compiler. It does everything, all of our Objective C, C++, C, and now it's going to be doing all of our Objective C++ as well. So, with that, turn it back over to Gautri.
Thank you very much, John. A big hand for the team. I think they've done a fantastic job. Some information resources, as usual. Information pages for all the Mac OS X tools are available at the Apple Developer website under the Tools area. We also, for this scene, because GCC is an open source project, we wanted to give you the pointer for open source at the Apple web pages. And the GNU project web source as well, since they're the baseline for all the GNU stuff.
And another pointer also to our mailing list main server. We have several mailing lists that are germane to the development tasks. Project Builder Users is the one that has been very heavily used. Got over 500 subscribers now. Cocoa Dev and Carbon Dev, over a thousand, thank you Dave. Cocoa Dev and Carbon Dev are also getting populated very rapidly as well.
Roadmap for more tool sessions. A very important session this afternoon at 5:00. Robert Bowditch in 7:05 will show you how to use the performance tools that we're supplying on disk. This has tremendous benefit for everyone. Tomorrow, 7:06 in the main hall at 9:00, debugging on Mac OS X using GDB. The Darwin Documentation Project and also the feedback forum tomorrow afternoon are also very good opportunities for you to talk to the development team and get more questions and answers.
With that, and especially for the Objective C++ seeding, this is my contact information on top. I would ask, again, as we put in the slide, if you're looking to be a candidate for as an Objective C++ seed site, that's a mouthful, please put that in the subject line so that I can identify them easily and manage the seed program. My group, Technology Management, does all the management of seeding for Apple Computer.
The Mac OS X Tools Feedback Address sends your email directly to the key personnel in the development teams in our tools groups. It's a good way to get feature enhancements, ideas expressed directly to us. And the URL at the bottom of the screen, which reappears on the Q&A slide, is a master page of all the URLs organized by session for the show. So if there's one URL that you should write down, you should write down the last one because that will allow you to get to all the URLs and all the information resources that we distribute throughout the entire show.