Since the last official VMs for Self were build, some time has gone by. Compilers changed, standard libraries evolved, other software ceased to exists or is not as widely used anymore as in the early days of Self’s history.
As a vidid example, enter a play with me, the play of “Friend or Foe.” Our protagonists:
- Ego. Our beloved Self VM, which is to be built.
- Wildebeest. This old friend used to build Self for some ten years or more. He has changed over time. (Also known as gcc)
- Boing. A young fellow, who recently stepped up to claim the lands of Wildebeest. (You might know him as clang).
- A Herald.
ACT I
Ten to Fifteen Years Ago.
In light space.
[Enter Ego, Wildebeest.]
Ego:
Hello, young Wildebeest. Your colleagues from Solaris have sent me to you, as I wish to be build on this platform called Linux. Can you do that for me?
Wildebeest:
Although I am younger than you, I think that will work.
[Wildebeest tries to built Ego.]
Wildebeest:
Yes, this should do it. A little rough around the edges, but that will sort out.
Ego:
There we go.
[Exit.]
ACT II
In 2012.
Early summer in Germany.
[Enter Ego, Wildebeest.]
Ego:
Wildebeest, old friend, we haven’t met in a long time. Grant me to be built once again, for that Linux and please for that sixth OS X.
Wildebeest:
Yes, let’s do that.
[Wildebeest tries to built Ego.]
Wildebeest:
Oh, we have to do something, I handle friends different now.
See, you have lots of code like this:
class mapOopClass: public oopsOopClass { … public: // constructor friend mapOop as_mapOop(void* p) { return mapOop(as_memOop(p)); } }
Well, and you then use as_mapOop everywhere. I don’t do this anymore.
[Enter Herald.]
Herald:
“The -ffriend-injecton option.
Inject friend functions into the enclosing namespace, so that they are visible outside the scope of the class in which they are declared. Friend functions were documented to work this way in the old Annotated C++ Reference Manual, and versions of G++ before 4.1 always worked that way”
[Exit Herald.]
Ego:
Then let us go on and use that option.
Wildebeest:
But know that I won’t support that option somewhen in the future.
Ego:
Beg your pardon?
Wildebeest:
You should manually declare your former friend method in an outer context. Like this:
// Forward-declaration for friend. mapOop as_mapOop(void* p); class mapOopClass: public oopsOopClass { … public: // constructor static mapOop as_mapOop(void* p) { return mapOop(as_memOop(p)); } }
[Ego sighs]
Ego:
Well then, it just around five dozen of files to modify…
[Exit.]
ACT III
In 2012.
Same place, a little later.
[Enter Ego, Boing.]
Boing:
Ah, greetings, Ego. I am the new star on the horizon. Want to try me?
Ego:
And who shall you be?
Boing:
I am the new default compiler on the OS Xen, I am part of that LLVM project, and last but not least, I have influential friends in the FreeBSD world. They even let me build their kernel.
Ego:
That is impressive indeed.
Boing:
I am the future™.
Ego:
If you say so… lets give it a shot.
[Boing tries to build Ego.]
Boing:
I can certainly compile you but not link you. I am missing many things like as_mapOop. Do you have an implementation for those functions?
Ego:
Well, yes, they are right there in their classes.
Boing:
Ego [hesitating]:
Are you sure? What if I leave out the forward declaration like in the original code I had.
class mapOopClass: public oopsOopClass { … public: // constructor friend mapOop as_mapOop(void* p) { return mapOop(as_memOop(p)); } }
Boing:
Ok, now I can find the implementation. But you have to somehow declare them in the outer context of the class to still use these functions as before.
Ego:
But that is exactly what I had before that and you weren’t able to find the implementation, were you?
What if I just pass you an -ffriend-injecton option?
Boing:
There is no such thing like an -ffriend-injection option with me.
Ego [angry]:
So you are the new rising star and cannot cope with me?
Leave me, I have to think.
[Exit.]
EPILOGUE
[Enter Herald]
Herald:
So Ego had to change again to satisfy both, Boing and Wildebeest. And so it did.
See what it came up with:
class mapOopClass: public oopsOopClass { … public: // constructor static mapOop as_mapOop(void* p) { return mapOop(as_memOop(p)); } … }; static inline mapOop as_mapOop(void* p) { return mapOopClass::as_mapOop(p); }
This worked out for all our three heroes.
[Exit.]
END
Eventually, this peculiarity of friend declared function could be resolved by introducing several inline functions that act as mere aliases to their static, class-bound counterparts.
However, I did not convert every friend function that had its implementation in the class header into a static member/static inline non-member pair.
- “True” friend functions, i.e., those that are accessible globally and not class-bound but have to access private members of other classes, were left as is. However, their implementation has been moved to a respective implementation (i.e., .cpp) file.
- Friend functions, that were mere shortcuts and so could be used without full qualification have been turned into normal static functions. They now have to be called with their class prefixed.
- Constructors (like create_objVector) and converters (like as_mapOop) are now static members of their class and also have a static inline non-member function.
I hope this finally ends the tale of friends for Self.