OS X app in plain C
github.com
github.com
The ability to use a high-level, dynamic language (ObjC), C, and even inline assembly in a single source file is unique to Objective-C (at least among the "mainstream" languages), and something I think is often under-appreciated.
One comment on the code here: for message calls returning type 'id' (object type), you don't need to cast the dispatch function. It would make the code much more readable. For other return types, you need a cast, but I'd wrap it in a macro. You could even use a C11 generic macro to handle floating point return values (unfortunately necessary).
On OS X if OBJC_OLD_DISPATCH_PROTOTYPES is not defined, you will get this declaration with zero arguments: void objc_msgSend(void /* id self, SEL op, ... */ ), which is possible to call from C without cast by using implicit function declaration, but this call will fail if compiled as Objective-C code with error "too many arguments to function call", I've decided to write in a style that compiles as C and Objective-C code.
PS. Would be nice to be able to use functions from clang Objective-C runtime [1], but for some reason they are not exposed in the public headers.
- [1] http://clang.llvm.org/docs/AutomaticReferenceCounting.html#r...
For one, C vararg type promotion makes it impossible to pass certain types in as parameters otherwise. For example, you can't pass a float.
It's also easy to make mistakes because of type mismatches. For example, if you pass `42` to a CGFloat parameter, this works fine when you cast msgSend to the right type, but will fail amusingly if you rely on varargs to make it through.
Perhaps most importantly, vararg calls aren't guaranteed to be compatible with a non-vararg target (as most/all methods you call will be), and you'll see this in action on ARM64.
Currently, you can #define OBJC_OLD_DISPATCH_PROTOTYPES 0 to have the compiler enforce this requirement. (This declares the functions to take (void) rather than (id, SEL, ...).) It's likely that Apple will eventually make this the default.
As for whether casts add much safety, I admitted it's debatable. C++ prefers casts (e.g. on malloc) whereas C doesn't. A downside of using explicit casts is that if the type changes elsewhere in the program, the casts (spread throughout the code) can mask errors. If you write code such that casts are unnecessary (which I'm agreeing is not possible in this case!), you will get a compile warning/error right away.
I think both perspectives are valid and there are risks either way. I try to avoid casts when writing plain C though.
C++ doesn't prefer casts, it requires them in places where C doesn't. Casting the return value from malloc isn't optional in C++ (unless you're assigning to a void *, anyway).
> It's also easy to make mistakes because of type mismatches. For example, if you pass `42` to a CGFloat parameter, this works fine when you cast msgSend to the right type, but will fail amusingly if you rely on varargs to make it through.
All of your other claims are completely valid. This one, I'd say, is partially valid.
No need to pick nits with my usage of the word "prefers."
objc_msgSend(window, sel_registerName("setAlphaValue:"), 1);
Versus: ((void (*)(id, SEL, CGFloat))objc_msgSend)(window, sel_registerName("setAlphaValue:"), 1);
The former will set a garbage alpha value, the latter works. More generally, the former requires much more care and attention to get the types right, whereas the latter just requires that the cast match the method declaration. ((void (*)(id, SEL, double))objc_msgSend)(window, sel_registerName("setAlphaValue:"), 1);
If the method signature changes (unlikely for a system header, but possible if you're calling into your own code) then you're hosed. Or you could just mess up and copy/paste the wrong signature, or forget to change it.Basically, it's pretty error-prone no matter what you do. Using Objective-C directly is much safer (and luckily you usually can, since ObjC is pretty much a drop-in replacement for C).
C calling conventions (cdecl, stdcall) and memory layout is pretty well standardized. So I'm not sure what else you could be referring to here.
However, since C is the low-level implementation language for almost every dominant operating system, a given platform's Operating System ABI is almost always locally synonymous with the C ABI on that platform, and people tend to speak of "being compatible with the C ABI" when what they really mean is that they're compatible with the OS's ABI.
But if you move to, say, a Mainframe OS, the OS ABI is dramatically different than the "C ABI" you're imagining from Wintel.
For example, I don't think there is a portable way to let your compiler use Pascal calling conventions, or to pass information in specific registers or in processor flags.
As an extreme example, in classic Mac OS, you could register a function to be called for line breaking in text input boxes that had the ABI "Parameters are passed to the routine in registers A3, A4, and D0, and output is returned in the Z flag of the Status Register." (https://developer.apple.com/legacy/library/documentation/mac..., page 2-31 gives several other special-cased ABI's in Mac OS)
Browsing that, I also encountered "The routine follows the C calling conventions employed by the THINK C software development environment. Arguments are passed on the stack from right to left, and a result is returned in register D0."
(Aside: that compiler must have treated vararg functions differently)
So, apparently, one cannot even count on C compilers to push arguments from left to right. So, if your ABI says "push X first, then y", I don't think you can portably call that function from C.
Right-to-left is pretty much the most sensible way to implement variadic functions with a stack-based calling convention - the argument that the function uses to interpret the rest of them must be found at a known location relative to the stack "top", and due to how stacks work, that implies the varying portion has to be pushed first, thus right-to-left.
Maybe if C decided to design things a little differently:
int printf(..., const char *format);
...
printf(5, "There are %d lights.\n");
we would instead have right-to-left being the dominant C calling convention.http://opengrok.libreoffice.org/xref/core/include/sal/types....
The design of C is predicated on an approach to portability that involves shipping source code, not binaries, around, with the destination machine having a compiler for its own architecture. (Given this, it's kind of shocking that things like Docker work at all. Goes to show how large a server-side monopoly those Intel ISAs currently hold.)
---
But wouldn't it be interesting if the "C compiler" were made into a low-level part of the operating system—basically taking the form of a JIT with a persistent cache—and then an executable format were specified, which really was just an archive of C source that was JIT-compiled by the OS when you first execv(3)ed it? Then you would have portable C binaries, because you'd be using the "correct" C ABI: C source-code.
Well, if you tack on a bit of pre-chewing to annotate the C source a bit (and maybe do some arch-neutral optimizations), and you have Apple's current LLVM-IR-based "Bitcode" approach to binaries.
Also it's Gott im Himmel ;-) (unless it's a reference I didn't get)
It's sort of like saying that there's no such thing as "rules of the road", because the road makers don't specify how you're supposed to drive on them, and the rules differ by jurisdiction and sometimes you're in a wilderness with no roads. The term is commonly understood to mean "rules for using roads, in the road-containing jurisdiction which you're in". I think the same thing is true of the phrase "C ABI", although if people are coming away thinking there's a cross-platform C ABI, then yes, we should be more precise.
Every platform has an ABI, obviously, and yes it's generally formed in view of C.
This doesn't change the fact that the phrase "The C ABI" is, in a vacuum, meaningless and that "The C ABI on Platform X" is just a convoluted, cart-before-the-horse way of saying "Platform X's ABI"
Put another way, I'm arguing that "C" is a more useful descriptor than "platform X's", because the sentence "Rust is compatible with the C ABI" is short for "forall X, Rust on platform X is compatible with the C ABI on platform X", and means something different from "Rust is compatible with the SysV ABI" (which implies that it follows the SysV ABI on Windows and OS X, too). And since the typical way of doing that is by interfacing with a C compiler or the C library (... and you could make this exact argument about the phrase "C library", I think), it's worth mentioning C. For instance, Vala and Nim, which both compile to C, are always compatible with the C ABI. If you port them to a mainframe they remain compatible with the C ABI, but not with the native ABI.
its a subtle difference some devs will pull out when they want to boost their egos by talking about things that sound smart, but they don't realise it requires that special kind of "smart-yet-dumb naivete" to consider worth caring or talking about.
You can even use C++, too, which is awesome. Putting Objective-C objects in C++ structs "just works" thanks to ARC.
One thing most people don't know is that Objective-C was originally implemented as a precompiler for C.
The same goes for C++ or Swift. Getting the function pointer for a method can be involved, but all functions adhere to the C ABI.
Just write an asm stub to call, that sets up ecx and other things right.
This is clearly false since at least one C++ compiler generates functions that does not adhere to any of the common calling conventions for C on that platform (or "C ABIs" if you will).
Besides that, I said you could not call them from C. An asm stub is not C. Neither is inline assembly even though it's a common extension to C.
they break it on tiny incremental updates sometimes... at least historically (in fairly recent history at least).
Some x86 C compilers supports calling conventions that lets you put things in registers, e.g. Microsofts "fastcall". But I wouldn't consider that part of the standard "C ABI" for Windows given that it's practically never used for externally visible functions. As I said "without specfic compiler support".
Using inline assembler is just cheating, it's not C.
> but cl is easily the worst compiler for a C++ ABI. they break it on tiny incremental updates sometimes... at least historically (in fairly recent history at least).
That is more about name mangling schemes and layout of standard library classes though. As far as I know the calling conventions has been quite fixed for a long time.
Source: https://en.wikipedia.org/wiki/GNU_General_Public_License#Leg...
> In order to circumvent the terms of the GPL, NeXT had originally intended to ship the Objective-C frontend separately, allowing the user to link it with GCC to produce the compiler executable. After being initially accepted by Richard M. Stallman, this plan was rejected after Stallman consulted with GNU's lawyers and NeXT agreed to make Objective-C part of GCC.
Source: https://en.wikipedia.org/wiki/Objective-C#Popularization_thr...
No.
Objective-C was developed in the early '80s (as a C pre-processor, as the Grandparent post said) by Brad Cox, and commercialized by himself and Tom Love via a company they created together called Stepstone. Cox wrote a book about their product, Object Oriented Programming: An Evolutionary Approach, in 1986. I own a copy. It's pretty good!
Some time later, NeXT decided to build their user-space APIs around Objective-C, and bought the rights to Objective-C from Stepstone outright. It was at that point that they started modifying GCC to directly compile Objective-C rather than pre-process it into C.
In particular, in the beginning Objective-C was a lot looser typed at compile time than it is now: no NSString* or NSWindow*, it was all just id. It was an attempt to make C look like Smalltalk.
The language eventually evolved more toward the static-typed end of the spectrum, culminating in support for lightweight generics and ostensibly birthing Swift along the way.
No. Objective-C started out as a set of C Macros, then an actual pre-processor was created, mostly to deal with uniquing selectors. Once the pre-processor was there it was used to create actual syntax.
Documented in "Object Oriented Programming: An Evolutionary Approach".[1] Still one of the best books on OO out there, because it treats OO as an architectural style with tradeoffs relative to other styles, rather than as a religion to be accepted (or nowadays as the devil to be cast into hell).
It also clearly describes the deliberate hybrid style that seems to be mostly forgotten now: object largely implemented in C, but connected via dynamic messaging. "Software-ICs".
I am somewhat surprised by seeing the 1986 release date, I guess I must have gotten it pretty soon after it was published. Used it as a template to implement an Objective-C pre-processor + runtime + basic classes on my Amiga (had just gotten a C compiler for it).
One of the reasons I got a NeXT was because NeXTStep was largely implemented in Objective-C, which to me meant they "got" it. And it also meant I could enjoy programming in Objective-C without having to maintain my own :-)
[1] http://www.amazon.com/Object-Oriented-Programming-Evolutiona...
I'm curious. What did that early form of Obj-C look like?
another thing most people forget is that the entire programming community rejected obj-c as deficient in its very early days... it only persists thanks to the ego of former nextstep employees afaik. and now apple employees got lumped with it... which i'd suspect helped create the internal pressure for swift... so some good came at the end of it all. :)
It's not worth listening to anyone who dislikes Obj-C just because of the [] syntax, which doesn't matter after the first 48 hours.
http://twistedoakstudios.com/blog/Post8237_a-years-worth-of-...
Not true anymore; ObjC has generics with type erasure. (But not all the cases can be exported to Swift.)
> Instead, Objective-C has nil, which is like null except it won’t stop the program when you accidentally use it.
That's… not quite right… but I guess he has the visible effects down. Anyway, ObjC has nullability annotations now for this.
> For example, in Objective-C many objects have both a “core foundation” form and a “new style” “NeXTSTEP” form
This is not legacy baggage, it's an intentional C bridge.
> This is justified by conventions strongly favoring you not catching exceptions, and apparently the speed benefits are non-negligible, but it still blows my mind that Apple has their language default to incorrect behavior.
He shouldn't use ARC+exceptions, because Cocoa itself is not exception-safe. Just quit the app if you get one.
That's pretty much my thoughts these days. If you have only the most superficial understanding of these languages, then that's what you'll get caught up on, but that's like saying Java and JavaScript are similar because they both use curly braces and dot notation for their methods. Yea, that is true, but it's a superficial similarity.
https://infinum.co/the-capsized-eight/articles/android-devel...
> The ability to use a high-level, dynamic language (ObjC),
> C, and even inline assembly in a single source file
> is unique to Objective-C (at least among the
> "mainstream" languages)[...]
Well Perl is mainstream enough to be installed on every *nix box, here's examples of assembly and C functions interoperating with Perl in the same source file: https://metacpan.org/pod/distribution/Inline-ASM/ASM.pod#SYN... & https://metacpan.org/pod/distribution/Inline-C/lib/Inline/C.... > The "C ABI" (although there is no such thing)
Of course if you do this in Perl every one of your calls will need to go through a foreign function interface where Perl's structures are translated back & forth between its idea of datastructures and the OS's idea, avoiding that is what people really mean when they talk about the "C ABI".* Call any method (send message in objc parlance) based on the string name of the method * Add methods to existing classes you don't own * Swap existing method implementations of a class you do not own at runtime (method swizzling) * Get and set variables by their string name for subclasses of NSObject * Check if an object has a method with a given name (great for invoking methods conditionally)
http://genius.com/Soroush-khanlou-objective-c-isnt-what-you-...
I know not many people agree with me. I liked the square braces and crazy long function names. I know Swift is decent... It's not the same.
Oh well. Lamenting my path to software engineering doesn't mean much for anyone else. But I really am going to miss it.
WWDC 2016 will hopefully have some nice objective C announcements just like last years did.
Also, are there any particular resources that you would recommend for beginners? I'm looking forward to reading this article since it might expose the low-level details of how, say, message passing works under the hood. Most of the guides are a bit too "beginner-oriented"; I'd love to find something that pulls up the carpets to reveal all the infrastructure underneath.
But maybe I'm wrong, and I'd be happy if I were.
If you really want to write (and read) code like this:
id titleString = ((id ()(id, SEL, const char))objc_msgSend)((id)objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), "sup from C");
((void (*)(id, SEL, id))objc_msgSend)(window, sel_registerName("setTitle:"), titleString);
instead of this
[window setTitle:@"sup"];
then I guess it would be easier to write a Objective-C to C converter (if there isn't one already) and convert your Objective-C app into C for added coolness. I
That's what the first Obj-C (and C++ compilers) were
* Or B, perhaps, if we're to be really pedantic.
Incidentally, that's pretty much how the language got started -- as a pre-processor add-on for plain C:
http://users.telenet.be/stes/compiler.html
However, the dialect this one implements is very different from Apple's, and it doesn't quite produce ANSI C (the generated code does some things which you can usually get away with but are technically illegal, like casting object pointers to function pointers). It's mainly of historical interest now.
This happens with Qt apps on OS X, as well, and it drives me bonkers. If you launch from the command line you have to command tab away and back for the menu to work
https://www.mikeash.com/pyblog/friday-qa-2012-11-16-lets-bui...
[1] http://stackoverflow.com/questions/19366134/what-does-object...
For comparison, a similar Win32 app in plain C:
http://www.winprog.org/tutorial/simple_window.html
X Windows app in plain C:
http://www.paulgriffiths.net/program/c/srcs/helloxsrc.html
...and just for fun, the same simple Win32 app from above in Assembly language:
http://win32assembly.programminghorizon.com/tut3.html
Looking at the code again, if I were forced to write an OS X app in plain C, there would be plenty of macro usage around objc_msgSend.
Personally, I'd blame UI latency more on a deeper composition stack and rendering sophistication rather than object orientation. Unless you're using late bound method calls for pixel-level drawing primitives, they shouldn't be a significant percentage of the event dispatch loop that turns input into visual results.
> If I were forced to write an OS X app in plain C, there would be plenty of macro usage around objc_msgSend
You will have re-invented the early versions of Objective C which were all done in the preprocessor :)
Secondly, compilers are a very different programming task to developing GUI apps; the Swift team choosing to use it has nothing to do with Apple’s assessment of how you should write an application—Apple are the people who have been leading objc development (they own the objc IP) in the last 10 years and use it in all first party apps and many system frameworks.
> Many of the low level libraries in OS X and iOS are written in plain C for speed and simplicity.
And don't forget that OSX/iOS are based on Darwin BSD, where most of heavy lifting is done by programs written in C.
- [1] https://developer.apple.com/library/mac/documentation/String...
And internally apple use other languages a lot, but frameworks like UIKit, AppKit, and many of the other public frameworks they expose in the SDK are objc, with foundation for example abstracting a lot of CoreFoundation C code.
That's not a knock against anyone who does or doesn't write apps in a particular language; but folks should know what the reality is, and Objective C or Swift are by no means the only or even necessarily the most profitable ways to writes apps for OSX, though those languages are perfectly fine for many circumstances.
Adobe products have codebases going back decades, of course they are written in C/C++. Furthermore most Adobe apps have fairly advanced processing needs that your run-of-the-mill CRUD GUI does not.
> anything built with Qt
Qt isn't even stock C++, they had to hack a meta-compiler on top of C++ to effectively serve the needs of their GUI system.
> even the Swift language itself
By this logic, thousands of GUI apps are written in x86 assembly.
Between C#, Objective-C, Swift, JS+Electron, Mono, and many other mature, high-level frameworks that don't trash memory at the slightest provocation, youd need some very good reasons to use C or C++ for a GUI app these days.
Many people write quite excellent software in C/C++ every day. Just as many people use chainsaws, drive cars, fire guns and take pills every day. These are all productive activities with low floors and high ceilings. Anyone can jump in and make a mess. It takes care and attention to do them well.
If this was an enterprise/cio/it/gov discussion web site then all the conversations would be java and .net vs c++
if this was a scientific computing website then python, c/c++ and fortran and would be mentioned etc.
if this was a hardware/firmware website it would be c vs (whatever black magic is used by hardware wizards).
[whatTheHell:is this:shit];
Is ARC really that much of a savior here? At least method calls get resolved at compile time.
Only if you write C code in Objective-C.
> PLUS it is a syntactical nightmare. [whatTheHell:is this:shit];
Honestly, get over it. This is largely a surface-level detail that makes no difference once youve used the language for more than a week. I take it you have similar complaints against Ruby, Python, Lisp, or any other language that dares to break ranks with the C++/Java syntax idiom.
> Is ARC really that much of a savior here?
Yes, manual memory management is one of the main things that C/C++ programmers seem to consistently screw up.
There's no reason why memory management _should_ be something that C++ programmers screw up now with containers like smart_ptr and std::string. Yes, if you malloc/free stuff and use things like memcpy, you're playing with fire, but the whole point of "modern C++" is that we have better and safer alternatives.
On the other hand, Obj-C message passing (which really means method calls that are resolved at runtime) is another way, like manual memory management, to really shoot yourself in the foot if you make a mistake.
> I take it you have similar complaints against Ruby, Python, Lisp, or any other language that dares to break ranks with the C++/Java syntax idiom.
I actually don't. Forced named parameters and "array brackets" denote method calls are arguably poor choices for syntax.
On the other hand, getting rid of semicolons and enforcing whitespace a la Python is okay, and Lisp is really just an extension of RPN.
The point is your point that I dislike anything by default that is "not C/C++/Java" is actually pretty incorrect.
I do have opinions, but that isn't the basis through which they are formed, and quite honestly, I'm offended that you even made such an assumption about me in the first place.
> Honestly, get over it.
Bad advice, I'm allowed to have an opinion (and I do substantiate it above).
Regardless, I have "gotten over it" given that a large portion of my day job is spent writing Obj-C, and while I do like it better than Java as a language overall (even not having to deal with JNI for interop is a dream), god help the syntax.
You've already made the point that my opinion is a popular one. To wit, I find it amusing that the Google autocomplete for "Objective C syntax" is "horrible."
As for X11 and Gtk, well, I argue with THEM.
See here for a tutorial using it for basic GUI stuff:
http://zetcode.com/gui/winapi/firststeps/
I've even seen several games that use it directly for keyboard handling and such to this day. And all the new, fancy GUI frameworks which exist on Windows are basically just wrappers on top of WinAPI.
Even Word and Excel were still C (although Office shared much C++ code in a common DLL)
Gotta love legacy code from the 1980s/1990s.
Since the new "Going native" wave, C++ has taken the role of main systems programming language, even the DDK now supports it.
Regarding office check their CppCon presentation how the code was ported to C++, refactored and made portable across OS. About 2h session.
What killed Vista was politics between OSDev and DevTools units.
http://tylergaw.com/articles/building-osx-apps-with-js
(Atwood's Law in action)
#include <objc/objc.h> #include <objc/runtime.h> #include <objc/message.h> #include <objc/NSObjCRuntime.h>
A commenter at stackoverflow provides some code that "shows how to access Cocoa GUI from pure C/C++ and build a truly functional GUI application"
He notes that you must "link against Cocoa framework".
To be fair, I think that's a perfectly reasonable goal in and of itself - showing how to set up an OS X app "from scratch" is definitely something I'm interested in - but we shouldn't pretend this isn't strongly relying on Objective-C to actually get things done.
EDIT: indeed, the makefile copies main.c to main.m before invoking clang, which means it's using the Objective-C compiler.
What would be much more impressive is if this used only the C Core* frameworks (Core Foundation and Core Graphics in particular) to do the same thing.
This is no different than writing a COM application in C. Both COM and objc runtimes provide similar dynamic OO services over a plain C API.
clang -framework Foundation -framework OpenGL -framework Cocoa -o test test.c
I have also used COM from C, and while it's not as pleasant as the pure Win32 API, it doesn't involve the massive amounts of function pointer casting shown here. vtables are involved but they can be defined as C structures, whereas this example seems to show objc_msgSend() returning a very large number of different possible function pointer types.
Looks like I was wrong, and I was working harder when I wrote Windows C programs than I needed to. The Windows API documentation is sort of confusing and I got the impression that only the CRT functions have a C interface.
Any alternate method of finding and calling those functions would amount to simply reimplementing objc_msgSend()
i'm sure it was a good exercise in learning objective-c but this is not an amazing achievement. i'd expect much better from someone competent reading the god awful docs and reverse engineering by inspection.
don't give up though. doing things like this over and over, especially in the face of nay saying arses like me, is what makes great programmers. :)