Topic: C++ nested functions and C interoperability
Author: chased@rbbb.Eng.Sun.COM (David Chase)
Date: 16 Dec 1992 23:35:27 GMT Raw View
>In article maxtal@extro.ucc.su.OZ.AU (John MAX Skaller) writes:
>> Do you personally favour nested functions?
>> If not, why not?
In article <24400@alice.att.com> ark@alice.UUCP () writes:
>In general, yes. For C++, I'm not sure.
>The main argument against it is that C doesn't have them, which means
>that having them in C++ would make C interoperability more difficult.
>It would also be more difficult to interface C++ programs with
>low-level assembly-language things like on-board controllers. I don't
>care about that personally, but I know that other people do.
C interoperability is elementary, and already done before. This
should also be transparent to other assembly languages like those used
in on-board controllers. (Besides which, people in those situations
have been known to live with restrictions before.)
Implementation method #1: generate code into your stack frame to load
up the lexical pointer and branch to where the "real" function is.
For a 68k, this means pushing an additional parameter. For a SPARC,
this means (for example) loading a value into %g2. The assembly
language for this looks something like:
sethi func>>10,%g1
sethi addr>>10,%g2
jmp %g1+func&0x3ff
add %g2,addr&0x3ff,%g2
Of course, when you generate this, you had better do the appropriate
cache flushes. "func" is entered with %g2 containing a pointer to
whatever representation of the parent frame you find most suitable.
(I got this trick from Thomas Breuel [sp?], and used it in an early
Modula-3 implementation on the 68k. It worked just fine -- Modula-3
functions looked just like C functions.)
Implementation method #2: generate about 512 pieces of code of the
form (SPARC-specific, for concreteness)
sethi abslocX>>10,%g2
ld [%g2+abslocX&0x3ff],%g1
jmp %g1
add %g2,abslocX&0x3ff,%g2
and about 512 8-byte chunks of data.
(In this case, "func" is entered with a ptr to a thunk_data, and
not a direct ptr to the parent frame.)
where you have
struct thunk_code {unsigned int sethi, ld, jmp, add; };
struct thunk_data {unsigned int function, parent_frame; };
struct thunk_code_page { struct thunk_code code [512]; };
struct thunk_data_page { struct thunk_data data [512]; };
Given a code page, code[i] has abslocX equal to data+i in a
corresponding data page. The code page is initialized once, flushed,
mmap'd no-write, and nested functions are created by allocating them
out of this pool and filling in the appropriate entries in the data
page. If you run out, create a new pair of pages. Note that
deallocating a thunk is now necessary (much like running destructors)
but because exceptions in C++ are synchronous and interaction with
longjmp is undefined, everything will work (ho, ho, ho). At worst,
longjmp creates a storage leak that can probably be dealt with by
putting a little bit more information into a thunk_data.
So, given that we can implement nested functions and maintain
compatibility with C, why not?
David Chase
Sun