
OS X app in plain C - dmytroi
https://github.com/jimon/osx_app_in_plain_c
======
btrask
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).

~~~
conradev
> 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.

~~~
valarauca1
Objective-C was a modification of the GCC developed by NeXT. NeXT didn't
publicly release their patches and the FSF/GNU threatened legal action. NeXT
released their sources and the GCC now supports Obj-C.

Source:
[https://en.wikipedia.org/wiki/GNU_General_Public_License#Leg...](https://en.wikipedia.org/wiki/GNU_General_Public_License#Legal_status)

~~~
msbarnett
> Objective-C was a modification of the GCC developed by NeXT.

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.

~~~
pixelglow
I have the book too. It's interesting as a history lesson and initial
motivations for Objective-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.

------
justsaysmthng
Don't do this at home. It looks so ugly because it's dangerous and vice-versa.

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
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].

\- [1]
[https://bitbucket.org/rmitton/tigr/src](https://bitbucket.org/rmitton/tigr/src)

\- [2] [https://github.com/nothings/stb](https://github.com/nothings/stb)

~~~
btrask
I haven't looked at tigr yet (it seems interesting!) but could you just use
'#ifdef __OBJC__'?

~~~
dmytroi
Not really, because everything is C/C++ there by means files are .c, .cpp and
not .m or .mm, its a bit tricky from build system point of view to treat the
file with extension .c/.cpp as Objective-C.

~~~
btrask
A .h file can be compiled as C, C++, or Objective-C. Isn't that how single-
file libraries usually work?

~~~
dmytroi
Yes, and this .h file will be used from .c/.cpp because looks like it's way to
bulky to add additional .m/.mm file just for OSX/iOS case when creating cross
platform applications. Check tigr - it's possible to run exactly the same
C/C++ code on Windows and OSX without any changes at all.

~~~
comex
Since Objective-C is a true superset of C (unlike C++) and there's also
Objective-C++, you can just tell the compiler to compile all .c/.cpp as if
they were .m/.mm. That is, if you can figure out how to coax your build system
to pass the right flags.

------
santaclaus
> 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

~~~
chillacy
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.

~~~
santaclaus
It started for me around on the Mountain Lion to Mavericks upgrade, so
whatever changed then.

------
0x0
Relevant: "Let's build objc_sendmsg()"

[https://www.mikeash.com/pyblog/friday-qa-2012-11-16-lets-
bui...](https://www.mikeash.com/pyblog/friday-qa-2012-11-16-lets-build-
objc_msgsend.html)

------
bdrool
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]

[1] [http://stackoverflow.com/questions/19366134/what-does-
object...](http://stackoverflow.com/questions/19366134/what-does-objective-c-
is-a-superset-of-c-more-strictly-than-c-mean-exactly)

------
userbinator
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.

For comparison, a similar Win32 app in plain C:

[http://www.winprog.org/tutorial/simple_window.html](http://www.winprog.org/tutorial/simple_window.html)

X Windows app in plain C:

[http://www.paulgriffiths.net/program/c/srcs/helloxsrc.html](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](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
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.

------
Hydraulix989
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.

~~~
codemonkeymike
C was not designed to create gui's. So its a feat of accomplishment as opening
a bottle with your teeth.

~~~
ultramancool
Windows, X11 and Gtk would argue with you.

~~~
coldtea
Windows is written in C++ mostly.

As for X11 and Gtk, well, I argue with THEM.

~~~
q3k
Yes, but WinAPI (which is all that is required to do GUI programming in
Windows) is C-centric.

~~~
coldtea
Yeah, but nobody (ie. very few if anybody) does GUI programming in WinAPI, so
I don't see how it's relevant in determining whether C is good for GUIs.

------
sgt
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.

------
conradev
And introduced in OS X 10.10 is the ability to build an OS X app in pure
JavaScript:

[http://tylergaw.com/articles/building-osx-apps-with-
js](http://tylergaw.com/articles/building-osx-apps-with-js)

(Atwood's Law in action)

------
mcmatterson
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.

------
eyeareque
I'm not a developer, but does it count when they use objc header files?

#include <objc/objc.h> #include <objc/runtime.h> #include <objc/message.h>
#include <objc/NSObjCRuntime.h>

~~~
wtallis
I think those are all valid C header files. They just define typedefs and
macros that are _about_ ObjC, but themselves are plain C.

------
mayoff
Richard J. Ross III did it a few years ago for iOS:

[http://stackoverflow.com/a/10290255/77567](http://stackoverflow.com/a/10290255/77567)

------
tosseraccount
As a side note: [http://stackoverflow.com/questions/525609/use-c-with-
cocoa-i...](http://stackoverflow.com/questions/525609/use-c-with-cocoa-
instead-of-objective-c)

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
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.

~~~
dmytroi
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.

\- [1]
[https://www.celedev.com/en/codeflow/](https://www.celedev.com/en/codeflow/)

------
icodestuff
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.

~~~
hunterwerlla
So obviously it's impossible to write a linux app in anything pure but C
because the kernel is written in C. That makes no sense.

------
justinlardinois
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.

[https://en.wikipedia.org/wiki/Component_Object_Model](https://en.wikipedia.org/wiki/Component_Object_Model)

~~~
userbinator
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.

------
pps43
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).

------
beamatronic
Outstanding! I have been wanting to see this for a long time. I really want to
build OS X apps but I personally prefer to build GUIs programatically.

~~~
leonatan
So build programmatically. But use Objective C or Swift.

------
whitehat2k9
Well, that looks rather unpleasant.

------
chris_wot
Would love to see this annotated.

------
sigjuice
I don't buy the "plain C" claim, given the gratuitous use of objc_msgSend().

~~~
msbarnett
...objc_msgSend() is a plain C function that grovels around in various runtime
data structures to call the correct function pointer for GUI and OS
functionality.

Any alternate method of finding and calling those functions would amount to
simply reimplementing objc_msgSend()

------
jheriko
sorry, but mediocre at best, full of mistakes at worst.

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. :)

~~~
gcr
As a beginner to Objective-C, I found reading the source here highly
informative, because it gives you a rare peek at the actual machinery
underneath everything that's going on.

