Topic: Declarations of Member Functions Outside Class Definitions


Author: hopper@ux.acs.umn.edu (hopper)
Date: 2 Aug 90 23:46:21 GMT
Raw View
In article <65729@lll-winken.LLNL.GOV> howell@bert.llnl.gov (Louis Howell) writes:
>In article <46393@brunix.UUCP>, sdm@cs.brown.edu (Scott Meyers) writes:
>|>Proposed:
>|>
>|>  That it be possible to declare member functions outside of class
>|>  declarations exclusively for the purpose of later friend declarations.
>|>
>|>Example:
>|>
>|>  class Example;                      // currently legal
>|>  void Example::f(int);               // currently illegal, but would be ok
>|>
>|>  class Sample {
>|>    friend void Example::f(int);      // currently legal, provided class
>|>                                      // Example has been defined
>|> <stuff deleted here>
>|>  };
>
>Why should even this be necessary?  The friend declaration contains all
>of the information necessary to deduce the declarations of Example and
>Example::f(int), so why not omit these declarations too?

Stuff deleted.

 I'm not precisely certain of how this is implemented, but what if
you declared a virtual member function of a class, and then use the
mechanism proposed in this article to declare it to be a friend? If you
called this function, how would the compiler generate the code to select the
proper virtual function if it was unaware of the other parts of the class
declaration?

 As I understand it now, the compiler knows how many virtual
functions you have, so it just makes a reference to the proper member of an
array of function pointers. If the compiler didn't know how many virtual
functions had been declared, it would have to somehow pass this task on to
the linker. This would mean substantial changes to the linker and it's
ability to change references to symbols into hard addresses. At least I
think so.

Probably just being stupid,
UUCP: rutgers!umn-cs!ux.acs.umn.edu!hopper   (Eric Hopper)
     __                    /)                       /**********************/
    / ')                  //                         * I went insane to   *
   /  / ______  ____  o  //  __.  __  o ____. . _    * preserve my sanity *
  (__/ / / / <_/ / <_<__//__(_/|_/ (_<_(_) (_/_/_)_  * for later.         *
Internet:              />                            * -- Ford Prefect    *
hopper@ux.acs.umn.edu </  #include <disclaimer.h>   /**********************/




Author: howell@bert.llnl.gov (Louis Howell)
Date: 3 Aug 90 00:37:13 GMT
Raw View
In article <1946@ux.acs.umn.edu>, hopper@ux.acs.umn.edu (hopper) writes:
|>In article <65729@lll-winken.LLNL.GOV> howell@bert.llnl.gov (Louis
Howell) writes:
|>>In article <46393@brunix.UUCP>, sdm@cs.brown.edu (Scott Meyers) writes:
|>>|>Proposed:
|>>|>
|>>|>  That it be possible to declare member functions outside of class
|>>|>  declarations exclusively for the purpose of later friend declarations.
|>>|>
|>>|>Example:
|>>|>
|>>|>  class Example;                      // currently legal
|>>|>  void Example::f(int);               // currently illegal, but
would be ok
|>>|>
|>>|>  class Sample {
|>>|>    friend void Example::f(int);      // currently legal, provided class
|>>|>                                      // Example has been defined
|>>|> <stuff deleted here>
|>>|>  };
|>>
|>>Why should even this be necessary?  The friend declaration contains all
|>>of the information necessary to deduce the declarations of Example and
|>>Example::f(int), so why not omit these declarations too?
|>
|>Stuff deleted.
|>
|> I'm not precisely certain of how this is implemented, but what if
|>you declared a virtual member function of a class, and then use the
|>mechanism proposed in this article to declare it to be a friend? If you
|>called this function, how would the compiler generate the code to select the
|>proper virtual function if it was unaware of the other parts of the class
|>declaration?

More stuff deleted.

I'm not sure that I understand what your objection has to do with the idea
of friends.  I'm not proposing any change to the virtual function mechanism
or the required order of declarations.  As I understand it, at least, all a
friend declaration accomplishes is an override to the privacy rules.  I want
to be able to tell the compiler to remember an exception of the following form:

"The private data (and functions) of this class should be accessible to a
function called f(int) in a class called Example."

It shouldn't matter if the compiler has ever heard of Example or
Example::f(int), all it has to do is remember that a function with that
name should have access to private parts of class Sample.  If the function
is virtual, then a separate friend declaration would be required for the
functions Derived_from_Example::f(int), Also_Derived_from_Example::f(int),
and so on, if they are also to have special access.
Am I missing something here, or are we talking about two different things?

Louis Howell

#include <std.disclaimer>




Author: sdm@cs.brown.edu (Scott Meyers)
Date: 1 Aug 90 20:05:31 GMT
Raw View
Proposed:

  That it be possible to declare member functions outside of class
  declarations exclusively for the purpose of later friend declarations.

Example:

  class Example;                      // currently legal
  void Example::f(int);               // currently illegal, but would be ok

  class Sample {
    friend void Example::f(int);      // currently legal, provided class
                                      // Example has been defined
    function()
      {
        void (Example::*pmf)(int) =
          Example::f;                 // currently illegal and would
                                      // remain illegal
      }
  };

Motivation:

  Such a change would make it easier to declare certain friend
  relationships without having to embed #include directives in the middle
  of header files.

  As an example, consider the following classes:

    - WithFriend, which has a private int member and which declares the
      constructor for class Derived as a friend.

    - Base, which has a constructor that takes an int as an argument.

    - Derived, a derived class of Base, that has a constructor that takes a
      WithFriend reference as an argument and initializes the Base
      constructor with the private int member of the referenced WithFriend.

  This must currently be declared as follows:

    class Base {
    protected:
      Base(int baseArg) {}
    };

    class WithFriend;

    class Derived: public Base {
    public:
      Derived(WithFriend& derivedArg);
    };

    class WithFriend {
      friend Derived::Derived(WithFriend&);
    private:
      int privateMember;
    };

    inline Derived::Derived(WithFriend& derivedArg)
    : Base(derivedArg.privateMember) {}

  With the proposed change, it could be declared as follows:

    class WithFriend;
    extern Derived::Derived(WithFriend&);            // currently illegal

    class WithFriend {
      friend Derived::Derived(WithFriend&);
    private:
      int privateMember;
    };

    class Base {
    protected:
      Base(int baseArg) {}
    };

    class Derived: public Base {
    public:
      Derived(WithFriend& derivedArg)
        : Base(derivedArg.privateMember) {}
    };

  The latter declaration is a little cleaner, in my opinion, because all
  the forward declarations are at the beginning and there is no need to
  separate the inline body of the Derived constructor from the class
  declaration, but that's hardly compelling.  The important situation --
  and the common one -- is when WithFriend is declared in a different file
  from Base and Derived, say WithFriend.h and Base.h, respectively.  Then
  the declaration must be as follows:

    class Base {
    protected:
      Base(int baseArg) {}
    };

    class WithFriend;

    class Derived: public Base {
    public:
      Derived(WithFriend& derivedArg);
    };

    #include "WithFriend.h"

    inline Derived::Derived(WithFriend& derivedArg)
    : Base(derivedArg.privateMember) {}

  In this case the #include directive must occur in the middle of the file,
  contrary to intuition and common convention for #include file usage.
  With the proposed change, the #include directives need only occur at the
  top of files:

    class WithFriend;                               // file WithFriend.h
    extern Derived::Derived(WithFriend&);

    class WithFriend {
      friend Derived::Derived(WithFriend&);
    private:
      int privateMember;
    };

    -------------------------------------------------------------------------

    #include "WithFriend.h"                         // file Base.h
    class Base {
    protected:
      Base(int baseArg) {}
    };

    class Derived: public Base {
    public:
      Derived(WithFriend& derivedArg)
        : Base(derivedArg.privateMember) {}
    };

  Note that allowing member functions to be declared but not used is
  similar to the standard C++ facility for allowing class names to be
  declared but no objects of those classes to be used prior to class
  definition.


Scott
sdm@cs.brown.edu




Author: howell@bert.llnl.gov (Louis Howell)
Date: 2 Aug 90 00:10:31 GMT
Raw View
In article <46393@brunix.UUCP>, sdm@cs.brown.edu (Scott Meyers) writes:
|>Proposed:
|>
|>  That it be possible to declare member functions outside of class
|>  declarations exclusively for the purpose of later friend declarations.
|>
|>Example:
|>
|>  class Example;                      // currently legal
|>  void Example::f(int);               // currently illegal, but would be ok
|>
|>  class Sample {
|>    friend void Example::f(int);      // currently legal, provided class
|>                                      // Example has been defined
|> <stuff deleted here>
|>  };

Why should even this be necessary?  The friend declaration contains all
of the information necessary to deduce the declarations of Example and
Example::f(int), so why not omit these declarations too?

Perhaps I'm missing something here, but I don't see why it should be illegal
to declare a member function of a class not yet defined to be a friend.
You can declare a non-member function to be a friend whether that
function has been declared or not; likewise with an entire class.  Why the
special restriction for member functions?

Here's a particular case that's caused me trouble.   Say I have two
classes, low_level_object (LLO) and high_level_object (HLO).  HLO's are
really big things, including arrays of several different kinds of LLO's.
Since HLO's are built up out of LLO's and manipulate them extensively,
it makes sense to declare the LLO's first, right?  In the later stages
of development, however, there may be a significant optimization possible
by permitting one or two HLO member functions direct access to LLO data.
I would like to simply declare those one or two functions friends of the
LLO class, but instead I have to declare the whole HLO class to be a friend
of the LLO class and throw data hiding out the window.  Note that this is
the logical direction for friendship to go; no LLO function will ever
need access to HLO data or functions, but HLO's manipulate LLO's all the
time.

Yes, I know I could order things differently.  Declare LLO to be a class
but don't define it, then define HLO, then define LLO and its inline
member functions, then finally define HLO's member functions including the
inline ones.  Besides being brain damaged, this approach prevents me from
defining HLO inline functions that call LLO functions in the HLO class
definition.  The code is therefore bulkier, harder to read and harder to
maintain.  Privacy has become counterproductive when it makes it harder
to write well structured code.

In addition, no such kludge will permit you to define two classes, each
of which has a member function of the other as friend.  The best you can
do is declare one whole class to be a friend of the other.

Opinion:  I think data hiding is a Good Thing.  Occasionally, however,
you have to make exceptions to it---hence the friend mechanism.  This
mechanism should be as narrow and selective as possible, or data
hiding will be severely compromised.

OK, I'm done.  Now everbody all at once tell me why this is stupid and
couldn't possibly work.   :-)

Louis Howell

#include <std.disclaimer>