From: chased@rbbb.Eng.Sun.COM (David Chase)
Newsgroups: comp.std.c++
Subject: C++ nested functions and C interoperability
Date: 16 Dec 1992 23:35:27 GMT
Organization: Sun
NNTP-Posting-Host: rbbb
Summary: It is elementary.  Next objection, please.


>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


Newsgroups: comp.std.c++