[comp.lang.c++] TYPESAFE FUNCTIONS THAT TAKE POINTERS TO THEMSELVES AS ARGUMENTS

jar@io.UUCP (Jim Roskind x3266) (06/14/89)

Warning: This is my first posting, and I am new to C++.   Have  mercy 
on me if I under-, over-, or mis-state, or mis-route.

I don't have the message numbers, but...

Ron Guilmette wrote:
>>How can you declare a function which can accept a pointer
>>to itself as an argument (in a type-safe manner of course)?

Andrew Koenig replied:
>	You can't in C++ or C.


It is interesting to note that you can get much closer than expected
to this goal using ANSI C (and you can get (for all practical
purposes) EXACTLY the desired result using C++).  Koenig is correct,
but, the following can be achieved in C++:

  typedef int return_type;

  /* cute stuff goes here, complete code given below.
   The result is a new pointer type called P, which "acts" like a 
   "pointer to a function", with a protypical argument P.
   */

  // Typical monadic function definition
  return_type function(P p_to_function) {
    p_to_function = & function;  // support assignment to pointer
    p_to_function = function;    // equivalent form under ANSI C
                                        // or C++
    // support comparison ==, !=
        p_to_function == function; 
        function      == p_to_function;
        p_to_function == p_to_function;

    // Support all forms of function call
        function      (function); 
        p_to_function (function);
        function      (p_to_function);
        p_to_function (p_to_function);
        
    return 0;
    }


The  reason  why Koenig probably said it was impossible is because it 
would require a self-referant type, which is usually not supported by 
C.   The  trick  to  exploit  is  the  fact  that  self-referant type 
definitions are  allowed  using  struct/union/class.   The  following 
example  provides  the desired C definition. It should be appreciated 
that the code generated when using the construct given below will  be 
exactly identical to the user's "desired type" (pointer to function).  
At least on most machines, a pointer is as large as the smallest size 
structure/union (structures usually have significant minimal size due 
to alignment restrictions).   Since  pointers  are  generally  fairly 
large,  no memory is wasted.  Most compilers are also smart enough to 
notice that accessing the first element (in this case, only  element) 
of  a  structure  uses a zero offset.  Hence NO effort is expended in 
object  code  to  support  this  completely  type-safe  source   code 
construction.

    typedef int any_type;
    union P { any_type (* point)(struct P )} ;

Now  P  is EFFECTIVELY a pointer to a function.  The gotcha of course 
is that the member operator ('.' or '->') must be used to make a call 
to  such  a  function.   (It  is  cute  to  notice that a union (or a 
class/struct) with a single element unambiguously refers to its  lone 
member,   given  only  identifier  name.   I  don't  know  of  any  C 
implementations that currently support this. I wonder  if  "anonymous 
union" sort of tricks would work here??)

Typical (?) C use is then:

    any_type function(union P p_to_function)
        {
        p_to_function.point(p_to_function);
        }

Having  seen  the  above  code, it is straight forward to see how C++ 
allows us to reach our desired goal completely:

cut here
---------------------------
// Except for 2 lines, Zortech compiles this  with  no problem.   */

#include <stream.hpp>
typedef int return_type;  

  class P {
    return_type (*point)(P); // Simple self reference

    P(return_type (*real_pointer)(P)) {point=real_pointer;}

    return_type operator()( P argument) {return point(argument);}
    friend int operator==( P left, P right){return left.point==right.point;}
    friend int operator!=( P left, P right){return !(right == left);}
    };

  // Typical monadic function
  return_type function(P p_to_function)
    {
    static stupid_counter = 100;
    if (0 > stupid_counter) return 0;  // recursion terminator
     stupid_counter--;
    
    p_to_function=function;     // support assignment 
    
    //Zortech v1.07 doesn't like the next 2 lines, but '&function' could be 
    //    used to satisfy it.  I believe the code is correct however.
    p_to_function == function;    // support comparison ==, (!= also works)
    function      == p_to_function; 
    p_to_function == p_to_function;

    // Zortech likes all the following lines (and so do I).
    function(function);           // type-safe argument of itself
                                       // although there is an arg conversion
    p_to_function(function);      // Overloaded operator() after arg conversion
    function(p_to_function);      // no conversions needed
    p_to_function(p_to_function); // Simple overloaded operator()
    
    return 0;
    }

--------------
end cut

...and all is type safe. No casts, no slight of hand.

In a correspondence with Koenig on this topic, he indicated  that  he 
now  recalls  seeing code of this sort, and he refers to such classes 
as "functionoids".  (Classes that act like functions).

Although  the  class  definition  certainly   leads   to   the   name 
functionoids, the underlying concept here appears to me to be that of 
"smart pointers".  Smart pointers (at least as far as I use the term) 
are classes that are used syntactically as though they were pointers, 
but their use typically causes some  additional  background  activity 
such  as null pointer detection, dynamic type verification, or memory 
management.  In this case  the  background  activity  is  the  smooth 
cast-free motion through a self-referant type.

As  an aside, the issue of passing functions has come up in my C code 
in the past.  (So I certainly don't think this is a silly  question).  
I  enjoy  seeing  the  completeness  of  the  resolution  under  C++.  
Function prototyping and enhanced  type  checking  are  some  of  the 
GREATEST things to happen to C.

In  a real life application, where I only had to write the above code 
once, and then everyone  could  use  the  typedef-name  (and  include 
file),  I  would  probably  use  the above C++ code.  For a quick and 
dirty solution under  C,  I  would  probably  leave  the  the  second 
generation  self-referant  prototype  as  "()",  and allow the ANSI C 
compiler to "not complain" during the assignment of "compatible"  but 
not  identical  types.   This  is  actually  a hack that exploits the 
pre-prototype type system (in some sense, either an "implicit"  cast, 
or an official exemption from the type check system).

Example:  
        typedef return_type (*p2_func)();   
        typedef return_type (*p_func)(p2_func);

The  above two types are compatible under ANSI C.  The latter is well 
enough defined that an identifier of that type can be used to  direct 
a  function  call  (without requiring a cast of the argument).  Under 
C++, these types  are  not  assignment  compatible  (the  p2_func  is 
defined  to  have  a void arg list in C++, and putting in "..." would 
not help matters).  Under C++, I am forced to either  use  the  fully 
type  safe code given in this note, or cast (hiss, boo, hiss: abandon 
the safety of type checking).

Two of my favorite rules in C programming are:
        1) NEVER use cast operators. They obscure errors.
        2) Be sure you have a good reason when you do use a cast.

Seems like with C++, there is just one less reason to cast.

               Jim Roskind
               Independent Consultant 
               (Contracting with Interleaf, Cambridge MA)
               516 Latania Palm Drive
               Indialantic Florida, 32903-3816
               (407)729-4348
               Current Email address:
               ...!mit-eddie!ileaf!jar