
Can you recreate Objective-C in C? - ingve
https://orangejuiceliberationfront.com/can-you-recreate-objective-c-in-c/
======
saagarjha
> Objective-C under the hood compiles every method call into a call to the
> function objc_msgSend().

Well, kinda. You need a family of functions (objc_msgSend, of course, but also
objc_msgSend_stret, objc_msgSendSuper, and objc_msgSendSuper_stret), to get
this to really work for when you have structures as your return value or are
calling a superclass's implementation (or both!). Since the inline assembly
that is objc_msgSend really has no idea what the types of function it's
calling is, the compiler must call an appropriate function based on the type
information it has.

~~~
sdegutis
This is also why dynamic languages like PyObjC, RubyObjC, RubyCocoa, MacRuby,
Nu, and F# had difficulty bridging certain functions and methods, because you
have to either create an insanely complex function that's able to take any
@encoding and turn it into a runtime type, or you have to shimmy it with
guesses for the most common ones like NSRect et al.

~~~
ewmailing
LuaCocoa author here.

While the objc_msgSend, objc_msgSend_stret, et. al, is an annoying detail,
that isn't really the hard part. (And in fact, all the good bridges use libffi
to do more direct invocations and avoids a lot of this problem.)

The hard part of bridging is those places where Objective-C introspection is
not powerful enough to discover the types of parameters or signatures of
functions. Typically, this is all the C stuff.

So for example, Obj-C's runtime introspection cannot tell you the make up of a
struct. So it cannot tell you the size of NSRect, NSPoint, NSSize, or the
individual data types inside the struct (and the names of each item if you
need to access them).

And the Obj-C runtime introspection cannot tell you about what C functions
exist and what the parameter types and return types are. So for example, there
is no Obj-C runtime way to look up that CGPointMake takes two CGFloat
parameters and returns a CGPoint struct.

Also, inline functions (marcos) in C/Obj-C are also problematic for these
bridges since they need to call the functions at runtime.

Apple shipped a framework called BridgeSupport in Mac OS X 10.5 which contains
XML data for all the things that cannot be determined at runtime. It also
contains .dylibs with symbols for inline functions. BridgeSupport is still in
macOS today, but it isn't getting much love and Apple keeps (accidentally?)
breaking things in BridgeSupport every release.

------
gok
This post seems to confuse "shouldn’t it be possible to recreate an
Objective-C program in straight C" and "can you recreate the ObjC _runtime_ in
C". The answer to the second is no, because of the reason mentioned. You can't
jump to an arbitrary function with any set of arguments and get the right
calling conventions in C. But you can easily _call_ objc_msgSend from C; you
just need to cast it to the right function signature first.

The real problem is that there's also no way in C to express the ObjC metadata
properly. You could in principle register all the classes/protocols/methods at
run time, I suppose, but that would be slow and gross.

~~~
rurban
It's certainly not slow and gross, it's in fact how you should implement a
proper dynamic language. lua, potion or self e.g. did it this way. Without any
assembler or ffi tricks. apply in C is easily doable if your data is tagged.

------
kovrik
Ok, maybe I'm missing something, I'm not C nor Objective-C dev but...

Language is just a specification, right? Which is just a document.

Then you have to implement that language using that spec: interpreter,
compiler, VM, runtime, whatever.

So I was always under impression that you could implement any language in any
other language (almost; sometimes with the help of another, more low-level
lang, say assembly, C etc.).

It is like saying "Java has no Tail-Call optimization (yet), hence you can't
implement Scheme (which requires TCO) in Java". Surely you can! Just use
trampolines or even use your own stack and calling convention.

What am I missing?

~~~
georgeecollins
I think what you are missing is that while any Turing complete system can
solve the same problems as any other, not all programming languages have the
same functionality. An easy example is that some languages are typed and some
are untyped. Some have objects (Objective C) and some don't (C). Can you
replicate the functionality of Objective C in C. Apparently not completely.

~~~
kovrik
But that is my point: if host language doesn't have feature X, you still can
implement that feature yourself, can't you?

If host language is untyped, then write your own type system.

Another example: Java has no co-routines, has no continuations, but Kotlin
(written in Java) does. They compile Kotlin code with co-routines into finite
state machines in Java.

------
boomlinde
BOOPSI is another one of these message based object oriented systems tacked
onto C, but without the convenient pre-processor. It was introduced with Amiga
Workbench 2.0, its developers supposedly impressed by NeXTSTEP. I think it's
worth having a look at if you know C and are thinking of how to architect an
object oriented system.

------
arcticbull
It's worth pointing out that you can in fact forward function calls using GCC
builtins, although not in clang without adding some assembly.

To quote the API documentation on Constructing Function Calls: "Using [built-
in functions], you can record the arguments a function received, and call
another function with the same arguments, without knowing the number or types
of the arguments." [1]

[1] [http://gcc.gnu.org/onlinedocs/gcc/Constructing-
Calls.html](http://gcc.gnu.org/onlinedocs/gcc/Constructing-Calls.html)

------
adamnemecek
This goes into a lot of detail on all this
[http://phrack.org/issues/66/4.html](http://phrack.org/issues/66/4.html)

------
verstaen
You can use dlopen()/dlsym() to replicate something like objc_msgSend().
That's what I was doing for AppSketcher on BeOS.

~~~
saagarjha
I don’t quite follow how you’re using the functions in dlfcn to replace the
Objective-C runtime?

------
acchow
"C is a language that is turing-complete. So is Objective-C. So shouldn’t it
be possible to recreate an Objective-C program in straight C?"

Major misunderstanding about turing completeness.

A language is so much richer than simply enabling you to write a map from
inputs to outputs. Sure you could probably rewrite my Haskell program in
Fortran in the sense that the Fortran program produces the exact same outputs
for any given inputs...but it's hardly convincing to me that you "recreated"
my program in Fortran after dropping my monad transformers and lenses.

Regardless, you wouldn't even be able to generally _prove_ (assuming you lack
dependent typing) that your program is functionally equivalent to mine (since
this is undecidable).

~~~
philwelch
Yup. To think of it another way: one way of "recreating a Foo program in Bar"
would be to write a Foo compiler or interpreter in Bar, and then use that
interpreter to run your original Foo program. Problem solved!

------
ksherlock
The GNU Objective C runtime can be implemented in C.

    
    
      [object method: args...]
    

compiles to (more or less)

    
    
      IMP tmp = objc_msg_lookup(object, @selector(method:))
      *tmp(object, @selector(method:), args...)

