Back in 1987, I was looking around for an advantage for my C compiler (Zortech C). I looked at ObjectiveC (by Stepstone) and C++ (by AT&T). Both had NNTP newsgroups for them, and both had about the same amount of traffic, and hotly debated each other.
I had to choose, and knew little technically about either. But Stepstone wanted royalties for developing a clone of ObjectiveC. I contacted AT&T's lawyer (William Ryan) who said I didn't need a license to develop a clone of C++. I asked if I could call it "C++" and he said sure. He laughed, and thanked me, saying I was the only one who ever bothered to ask.
So C++ it was, and Zortech C++ came out in 1988 (the first native C++ compiler for DOS, and arguably at all. g++ was out, but as a beta.)
The action for programming at the time was on DOS, and C++ pretty much exploded with the availability of an inexpensive, fast, native compiler for DOS. The comp.lang.c++ traffic grew by leaps and bounds, and comp.lang.objectivec faded away.
AFAIK pre-nextstep Objective C was also a very simple layer over C that only had "id" as a type with things like protocols, etc being introduced later by next.
(also i wonder if Stepstone had a leg to stand on regarding implementing a clone of Objective C... you couldn't call it Objective C as i guess they had a trademark over it, but what else would they license?)
Reminds me of discussing music piracy with my musician friends, all of whom fervently wanted to be sufficiently popular to actually HAVE people pirate their stuff!
(Not the piracy part, the "you really want people to listen to you, first" part...) :-)
Yep, I love objective-c, sure with a few syntax frustrations: ([[]]). I don't do any apple work anymore, but I'd love to be able to use named parameters, and it's message passing in plain c. Every couple of years I waste an enjoyable few days on exploring github looking at hobby project to bring smalltalk features to c.
So if anyone has any recommendations of oo in c projects i could checkout I would appreciate it.
Similar to how the posix API works with struct arguments for extensibility reasons, in C++ you can pass structs by reference with named arguments and init them in place, like:
void dothing(const dothingArgs& args);
...
dothing({ .arg1 = 1, .arg2 = someOtherThing });
Register colouring should make it run the same as regular args for the most part in optimized builds.
This has two benefits:
1) Changing the order of the struct args won't introduce ordering bugs like with function signatures.
2) Default arguments are more explicit and there's less chance of breakage as the argument list grows over time and gets more default values, as tends to happen.
And 2 drawbacks:
1) Passing complex types as arguments without copying can be tricky.
2) The compiler errors don't tell you specifically which arguments are broken, at least in g++. This one sucks the most imo.
FYI - designated initializers in C++20 require that the designators appear in the same order as the field declarations in the struct or class[1]. If the designators aren't in the same order as they appear in the struct declaration, the behavior of the designated initializer statement is undefined. Note that this is different from C - C99 supports a broader variety of designated initializers than C++20 - there's a table in the document I linked.
Because of this, the statement "Changing the order of the struct args won't introduce ordering bugs like with function signatures" is actually not true - I know from experience that clang will sometimes just silently leave the struct uninitialized, which could lead to nasty bugs. The behavior can also vary between optimized and debug builds, so it might not surface when you run your unit tests, etc.
I believe that once C++20 is standardized both compilers will probably introduce warnings or errors to let you know when you've unintentionally initialized fields out of order, but for right now the feature is a landmine and I wouldn't recommend using it.
g++ gives an error if you do things out of order. But what I meant is that this is still better than a regular function signature.
ie: int f(int a, int b) changed to f(int b, int a) works but the arguments will be wrong if the call site doesn't change.
f({ .a = 1, .b = 2}) will fail at the call site when the struct is changed to expect f({ .b = 2, .a = 1})
But yes, while ideally the order of declaration wouldn't matter, C++ is going the other way, being extremely strict about initialization order to avoid complex constructor errors. I wish POD types would be treated differently for those warnings/errors.
Makes sense - I misunderstood what you meant by order. It’s good to know that g++ gives you an error if the designator are out of order - thanks for pointing that out.
Forgive what may be a dumb question (I'm not a C++ user), but why should dothing() accept a reference to struct? Why is this better than just making it accept a struct like
void dothing(const dothingArgs args);
and then just passing it an anonymous struct that only briefly sits on the stack when you call
dothing({ .arg1 = 1, .arg2 = someOtherThing });
A reference would make sense if you already have a struct sitting somewhere else in your program, but if you're putting together the struct at the time you call the function, wouldn't it make more sense to just pass by value?
Because otherwise the struct will be copied when the function is called.
Though your question is kind of getting at r-values and move semantics, which is a solution to prevent copying the temp value both when calling the function and in its execution. You can read about it because it will take too long to explain it here.
The C99 designated initialization (where you give member names in the initialization) AFAIK only works in clang when compiled in C++ mode, gcc and MSVC doesn't appear to support it.
C++20 will get a fairly limited subset of the C99's designated init though.
Note that what's been added is a subset of the C99 behavior - there are a bunch of things that work in C99, but are not allowed (and might introduce undefined behavior) in C++20. See the "Design Decisions" section of this document for a summary of differences: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p032...
A number of early attempts at OO in C, like BOOPSI on Amiga and Xt Intrinsics on Unix, used varargs to pass a list of tag-value pairs to functions and methods, allowing for both named parameters and initialization lists for complex objects.
In C99 you can sort-of get named optional parameters via designated-initialization, e.g. if a function takes a a struct "person_t" by value you can write:
Any struct items which are not assigned a value will be zero-initialized (I wish it would be possible to define default-values in the struct declaration though).
I question the value of bringing OO principles into plain C though ;)
Is it really dead? In other words are there examples of applications you can build with Swift that you simply can’t in Objective C? I haven’t built iOS apps since before Swift existed so I’m genuinely curious. If I got back into it I’d obviously prefer Objective C out of familiarity but not if Apple crippled it.
Apple has slowly started bringing out frameworks that are Swift only, and in addition the SwiftUI runtime is... well, Swift only.
All the old ObjC stuff still works, though, there's nothing stopping you using it. I released an app recently that was 50/50 ObjC/Swift (for a few weird reasons) recently and it's really painless in comparison to some other modern stacks.
Can't come soon enough. The syntax is a head scratcher. I don't understand why anyone could have possibly thought [[[[]]]] was a good idea! Don't get me started on the closure or variable syntax. Oof.
The syntax is all about C compatibility. Objective-C (unlike C++) is a strict superset of C. Anything using bracket notation in Objective-C is using dynamic OO message-passing. Anything that reads like C syntax is plain-old C.
The closure syntax also relates to C compatibility. Because the Clang blocks extension isn't an extension to Objective-C, but to C. The syntax matches C's function pointer syntax, just replacing the * with ^. ^ was chosen because (A) it has no unary form in C, and (B) it can't be overloaded in C++.
The brackets do have the advantage that they make the order of evaluation and individual callees totally explicit, unlike some languages with optional parentheses or a mix of dots and other syntax elements.
As someone with the unusual distinction of learning to code with ObjC, I found the syntax super helpful as a newbie.
Everything looked distinct (e.g. in other languages blocks might only differ from hashmaps by their context), everything was verbose as if explained to a child (Thing *thing = [Thing thingByConvertingOtherThing: otherThing];) and so on.
Yes. But I think these things are relevant today. People are forgetting that the newest hot languages die, and maybe if we didn't forget it we'd have better languages, and more measured responses to them.
I really liked ObjC, it made all sense to me as an OO language, unlike C++, to the point I didn't see much innovation in Swift at all, I felt it basically removed the C and [funny syntax] (my opinion about Swift mellowed since then).
I had to choose, and knew little technically about either. But Stepstone wanted royalties for developing a clone of ObjectiveC. I contacted AT&T's lawyer (William Ryan) who said I didn't need a license to develop a clone of C++. I asked if I could call it "C++" and he said sure. He laughed, and thanked me, saying I was the only one who ever bothered to ask.
So C++ it was, and Zortech C++ came out in 1988 (the first native C++ compiler for DOS, and arguably at all. g++ was out, but as a beta.)
The action for programming at the time was on DOS, and C++ pretty much exploded with the availability of an inexpensive, fast, native compiler for DOS. The comp.lang.c++ traffic grew by leaps and bounds, and comp.lang.objectivec faded away.