Topic: Dynamic scopes


Author: Alexandre Oliva <oliva@dcc.unicamp.br>
Date: 1998/03/04
Raw View
Esa Pulkkinen writes:

>   virtual okButton.ButtonPressed()  // New syntax

Is this any different from defining a nested non-abstract derived
class?  Of course, you wouldn't gain access to the containing class
automatically, but that can be easily worked out.

> 1) Virtual functions of any class can be overridden from any scope by
> using the scope resolution operator ("::") or member access operator
> (".") in definitions of functions. Such a definition overrides the
> definition given in the class, and is visible at the scope of definition
> of the overriding definition. Any use of the class where the definition
> is visible, will use the overridden version of the function.

that's kind of dangerous.  What happens when such object is
copy-constructed?  Does it preserve the modified virtual method list
or not?

--
Alexandre Oliva
mailto:oliva@dcc.unicamp.br mailto:aoliva@acm.org
http://www.dcc.unicamp.br/~oliva
Universidade Estadual de Campinas, SP, Brasil
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: Esa Pulkkinen <esap@cs.tut.fi>
Date: 1998/03/04
Raw View
> Esa Pulkkinen writes:
> >   virtual okButton.ButtonPressed()  // New syntax

Alexandre Oliva <oliva@dcc.unicamp.br> writes:
> Is this any different from defining a nested non-abstract derived
> class?  Of course, you wouldn't gain access to the containing class
> automatically, but that can be easily worked out.

There are (at least) two differences:

1) You can override methods deep in the containment hierarchy. For
nested derived class, you can really only override methods in the next
level of the containment hierarchy [Or have to duplicate existing code,
which is evil]. I think the lack of this property is one reason why deep
containment hierarchies aren't as reusable as they could be, and even
one factor in one common misuse of multiple inheritance of
implementation.

2) There is no need (or possibility) to duplicate constructors of the
overridden class.

[I'm not talking about the dynamic change of virtual function tables
here - they add another level of differences between this feature and
any existing C++ feature (I think)]

The point of this feature is to encourage the use of containment (and
public data members!) instead of using pointers to dynamically allocated
memory holding those subobjects whenever you don't need to dynamically
change the object's type at run-time [In my experience, most of the uses
of pointer subobjects are such uses], but need a specialized version of
a virtual function to communicate with the enclosing class. I see this
feature (or something similar) necessary for enabling interface
composition, i.e. building larger interfaces from smaller ones [And
therefore allowing each of those subinterfaces to remain small], and
(even more importantly) to reuse the implementations of the
subinterfaces in a natural way.

> Esa Pulkkinen writes:
> > 1) Virtual functions of any class can be overridden from any scope by
> > using the scope resolution operator ("::") or member access operator
> > (".") in definitions of functions. Such a definition overrides the
> > definition given in the class, and is visible at the scope of definition
> > of the overriding definition. Any use of the class where the definition
> > is visible, will use the overridden version of the function.

Alexandre Oliva <oliva@dcc.unicamp.br> writes:
> that's kind of dangerous.  What happens when such object is
> copy-constructed?  Does it preserve the modified virtual method list
> or not?

No. IMO, it's better it takes the methods from the scope where the
object is copied (just like for any construction). I think this is what
you would expect anyway (because copying in C++ at least behaves that
way already). The copy and the original (or the object containing the
copied object) shouldn't have implicit dependencies between them. The
only situation, where this might matter (IMO) is:
struct Y
{ A a;
private:
  virtual void a.func2() { ... }
  void f()
  { A copyOfA = a; // the copy has an overridden func2 but doesn't
                   // know anything about X::y.a.func() below
  }
};
struct X
{ Y y;
  virtual y.a.func() { ... }
};

This doesn't have any effect on the copy inside Y::f(), i.e. it's still
the old A that's used. Note if A has virtual functions, the copies
should be made using virtual clone() function anyway.

I don't believe this would be any more dangerous than ordinary virtual
functions. I would expect most functions overridden are abstract
anyway [Just like for ordinary virtual functions].
--
   Esa Pulkkinen                        | C++ programmers do it virtually
   E-Mail:  esap@cs.tut.fi              | everywhere with class, resulting
   WWW   :  http://www.cs.tut.fi/~esap/ | in multiple inheritance.
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: Esa Pulkkinen <esap@cs.tut.fi>
Date: 1998/03/03
Raw View
[Sorry, this got a little lengthy].

This is _not_ a proposal for a new language feature to C++ [I wouldn't
dare do that after the consequences outlined in D&E for people proposing
new language features :) ], but I think the following feature might be
useful, and would like some help in finding out what's wrong with it,
i.e. reasons why this is/isn't a good idea.

Here's an example what you could do with it:

class Button
{
protected:
  color_t myColor;
public:
  Button();
  void Hide();
  // ...
  virtual void ButtonPressed() = 0;
};

class ButtonUser
{
  Button okButton; // Would be valid: instantiating an object of
            // abstract class.
  Button cancelButton;

  virtual okButton.ButtonPressed()  // New syntax
  {
    cancelButton.Hide(); // Allows access to everything in this scope.
    Hide();                // also allows access to okButton's public and
    myColor = COLOR_GREEN; // protected members
  }
  virtual Button::ButtonPressed() // Another piece of new syntax.
  {
    // ...
  }
public:
  Button *BuildButton()
  { return new Button;
 // Builds a new button with both okButton and cancelButton's
        // callback overridden by Button::ButtonPressed above.
  }

};

Here's a sketch of what kind of rules could be used to describe the
semantics of this new construct:

1) Virtual functions of any class can be overridden from any scope by
using the scope resolution operator ("::") or member access operator
(".") in definitions of functions. Such a definition overrides the
definition given in the class, and is visible at the scope of definition
of the overriding definition. Any use of the class where the definition
is visible, will use the overridden version of the function.

2) It is possible to instantiate objects of abstract classes, provided
that all abstract functions have been overridden (or redeclared
abstract) in the scope of instantiation. It's further possible to
introduce local special cases for virtual functions of an object
declared elsewhere. A function of an instantiated object may be
abstract, but then any use of the object requires a definition in the
scope of use. It's possible to override virtual functions of a
class from namespace, function and class scopes [even though standalone
virtual functions in namespace or function scopes are not allowed].

For example:

   Button bu;
   // the following will explicitly force the object's function as
   // abstract to require clients to provide one. If this wouldn't
   // be given, the instantiation would be ill-formed.
   virtual void bu.ButtonPressed() = 0;

   class MyClass
   {
   public:
     void f()
     {
       bu.Hide(); // This requires an override of the function in bu.
     }
     void bu.ButtonPressed() { } // ... which is provided here.
   };

3) In the scope of such overridden function, the names from both the
scope of declaration of the class, and from the scope of definition
of the overriding function definition are visible.

4) In the scope of such overridden function, the names of all public and
protected members of the class whose method is being overridden are
accessible.

5) The names of such overridden versions of functions obey normal rules
for hiding w.r.t to the scope of definition of the overridden function.

6) If the lifetime of the scope in which the overridden function is
declared ends, or if the lifetime of the object referred to by the
definition ('bu' in the above example) ends, any use of
the overridden function is undefined behaviour.
[This might occur for dynamically allocated objects allocated within
the scope containing such definitions, if that object uses any of those
overridden definitions].

I've left out all implementation considerations, I think this feature
could be implemented, although haven't worked out all the details
yet. Basically, this allows selective temporary modification of an
object's virtual function table pointer at run-time depending on which
scope it's used from.
--
   Esa Pulkkinen                        | C++ programmers do it virtually
   E-Mail:  esap@cs.tut.fi              | everywhere with class, resulting
   WWW   :  http://www.cs.tut.fi/~esap/ | in multiple inheritance.
---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://reality.sgi.com/austern_mti/std-c++/faq.html              ]