There was a thread the other day where people were talking about languages that were easily interoperable with the "C ABI" (although there is no such thing). Languages listed included C++, Rust, and maybe a few others--but no mention of Objective-C.
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).
You should still cast the dispatch function even when your return type is id.
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.
> 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.
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.
I'm going to miss Objective C terribly. It is, hands down, still my favorite language (and ecosystem). It's interoperability with C got me into C. It was insanely powerful and fun.
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.
> 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)[...]
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".
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.
SVR4 specified standard-conformant calling conventions (stack layout, register use etc). IIRC VMS went one "better" and tried to specify a standard calling convention that was supposed to apply to all programs (sadly, there's more to interoperability than calling convention).
You do actually need to cast the dispatch function, because there is no guarantee for all platforms that the ABI for calling a variadic function is the same as the ABI for calling a non-variadic function with the same arguments.
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
One practical example would be tigr [1] - a cross platform window framework for making simple applications, could be compiled as one header drop-in only lib. You simply cannot make it one header without making calling ObjC runtime from C. Why it's necessary to make it work as one drop-in header? Well it's a new trend in C libraries, which allows using libraries with the least resistance possible, it's like a package manager but nicer - you only have one file! Great example of modern one header libs is probably the famous stb package [2].
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.
> For some reason if we run the app from command line than menu is not accesible by mouse at first time
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
Its because of the missing .app bundle and appropriate Info.plist. I just submitted a pull request to fix this - maybe its useful for you too, check it out:
Strangely this happens on my cocoa app too... but it hasn't always been the case and I've had this thing for about 5 years now, so I suspect that somewhere, something got messed up.
This is what Objective-C looks like if you "unroll" it to plain C. Not too surprisingly, it's very verbose. Other than as an exercise, there's no reason to do this, particularly given that Objective-C is a strict superset of C.[1]
As someone who has done a lot of Win32 programming in plain C, I can recognise some common patterns like an event loop and window creation, but it's quite amazing how much extra "cruft" there is just to deal with what appears to be the contortions of OOP'ing everything. All those string constants are rather surprising too. The interesting thing is, the few times I've had to use a Mac the GUI didn't feel quite as responsive as Windows on the same hardware --- it wasn't jerky/stuttering, just what I'd describe as "smooth but sluggish", and the binaries were noticeably larger, and I now wonder if this increased abstraction and complexity has anything to do with it.
Windows is effectively already an OO system; you "override" methods (message codes) in your window procedure, with the base implementation provided by the default window procedure. Windows even uses the terminology of window classes to describe the behaviour of window instances.
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.
This example is extremely contrived, of course it's going to be a nightmare in C. GUI latency probably has little to do with the overhead of objC message sending and more to do with tuning how things respond for UX purposes. For instance it's well known among gamers that on OSX the mouse has acceleration which makes it smother but less responsive in gaming. I wouldn't be surprised if this carried over to scrolling, animations, etc.
> 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 :)
It's amazing and sad for computing that somehow managing to use a sane language to develop software for a very commonly used platform is seen as a serious feat of accomplishment.
If you compile it, you'll see that the ObjC executable with no ARC and the C-only executable are exactly the same size stripped. The ObjC version with ARC is 392 bytes bigger.
A few years ago I dove into the idea of trying to make a C only iOS app (well actually, it was a quick exploration of how hard it would be to build your own UI kit without using UIKit). The dive bottomed out pretty quickly, as I couldn't figure out a way to access windows without resorting to UIWindow methods, and `UIGraphicsGetCurrentContext()` acted super wonky calling it without 'proper' bootstrapping around it. I was expecting more, given the usual layered approach that Apple takes with their system libraries.
My interest in this comes from wanting to build Objective-C based OSX-UIs using scripting languages that are only embeddable in C, like Lua. When (if?) that day comes, I'll be like a kid in a candy store all over again.
I would recommend you to try CodeFlow from celedev [1], it's a dynamically generated Lua binding to Objective-C. Plus a nice editor with hot reloading support and other cool features.
I don't buy it. Yes, the language may be C, but if you're linking the ObjC runtime and frameworks, all you're really avoiding here is the nib-loading machinery.
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.
I just ran the code through clang (Apple LLVM version 5.1) with no problems. You don't have to rename the source - but perhaps there are issues in other configurations.
clang -framework Foundation -framework OpenGL -framework Cocoa -o test test.c
I'm sure the Windows version would be just as bad. Most of the useful parts of the Windows API don't have a C interface, so you have to use COM, which essentially means editing vtable-like structures by hand.
I've been programming Windows GUI apps for over a decade. Most of the Windows API, i.e. Win32, is not COM.
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.
You wouldn't have to use COM for this at all.
You could use the Win32 API directly, which is a C API and would be much less code. In fact I'll link a comment from above that shows this: https://news.ycombinator.com/item?id=11641545
The two replies to my comment both need a response, so I'll make this a sibling.
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.
That's a lot of boiler plate code. Is there a way to make it smaller? For example, if all I need is to open and process a file with no GUI (maybe a progress bar).
[+] [-] btrask|10 years ago|reply
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).
[+] [-] mikeash|10 years ago|reply
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.
[+] [-] conradev|10 years ago|reply
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.
[+] [-] themartorana|10 years ago|reply
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.
[+] [-] radarsat1|10 years ago|reply
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.
[+] [-] avar|10 years ago|reply
[+] [-] dmytroi|10 years ago|reply
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...
[+] [-] mintplant|10 years ago|reply
[1] https://github.com/fpco/inline-c/blob/master/README.md
[2] https://hackage.haskell.org/package/language-c-inline
[+] [-] gumby|10 years ago|reply
[+] [-] eridius|10 years ago|reply
[+] [-] jheriko|10 years ago|reply
[+] [-] justsaysmthng|10 years ago|reply
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
[+] [-] dmytroi|10 years ago|reply
- [1] https://bitbucket.org/rmitton/tigr/src
- [2] https://github.com/nothings/stb
[+] [-] deanCommie|10 years ago|reply
[+] [-] bdrool|10 years ago|reply
Incidentally, that's pretty much how the language got started -- as a pre-processor add-on for plain C:
https://en.wikipedia.org/wiki/Objective-C#History
[+] [-] david-given|10 years ago|reply
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.
[+] [-] bluedino|10 years ago|reply
That's what the first Obj-C (and C++ compilers) were
[+] [-] santaclaus|10 years ago|reply
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
[+] [-] fit2rule|10 years ago|reply
https://github.com/jimon/osx_app_in_plain_c/pull/1
[+] [-] chillacy|10 years ago|reply
[+] [-] 0x0|10 years ago|reply
https://www.mikeash.com/pyblog/friday-qa-2012-11-16-lets-bui...
[+] [-] bdrool|10 years ago|reply
[1] http://stackoverflow.com/questions/19366134/what-does-object...
[+] [-] userbinator|10 years ago|reply
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.
[+] [-] barrkel|10 years ago|reply
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.
[+] [-] chillacy|10 years ago|reply
> 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 :)
[+] [-] Hydraulix989|10 years ago|reply
[+] [-] codemonkeymike|10 years ago|reply
[+] [-] fbonetti|10 years ago|reply
[+] [-] ddoolin|10 years ago|reply
[+] [-] sgt|10 years ago|reply
[+] [-] conradev|10 years ago|reply
http://tylergaw.com/articles/building-osx-apps-with-js
(Atwood's Law in action)
[+] [-] mcmatterson|10 years ago|reply
[+] [-] eyeareque|10 years ago|reply
#include <objc/objc.h> #include <objc/runtime.h> #include <objc/message.h> #include <objc/NSObjCRuntime.h>
[+] [-] mayoff|10 years ago|reply
http://stackoverflow.com/a/10290255/77567
[+] [-] tosseraccount|10 years ago|reply
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".
[+] [-] sdegutis|10 years ago|reply
[+] [-] dmytroi|10 years ago|reply
- [1] https://www.celedev.com/en/codeflow/
[+] [-] icodestuff|10 years ago|reply
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.
[+] [-] thought_alarm|10 years ago|reply
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.
[+] [-] convivialdingo|10 years ago|reply
clang -framework Foundation -framework OpenGL -framework Cocoa -o test test.c
[+] [-] hunterwerlla|10 years ago|reply
[+] [-] justinlardinois|10 years ago|reply
https://en.wikipedia.org/wiki/Component_Object_Model
[+] [-] userbinator|10 years ago|reply
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.
[+] [-] seabrookmx|10 years ago|reply
[+] [-] justinlardinois|10 years ago|reply
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.
[+] [-] pps43|10 years ago|reply
[+] [-] beamatronic|10 years ago|reply
[+] [-] leonatan|10 years ago|reply
[+] [-] whitehat2k9|10 years ago|reply
[+] [-] chris_wot|10 years ago|reply