Topic: Proposal: injection of boiler-plate methods/code


Author: Marc Girod <girod@trshp.trs.ntc.nokia.com>
Date: 1996/12/12
Raw View
The original posting, by Nick Thurn, is not available anymore on my
server.

I wanted to agree loudly with its proposal.
Recently, Bjarne Stroustrup stated to my surprise that #ifdef is the
last preprocessor functionality which has no replacement in C++.

Clearly "template injection" is another. The examples of macros Nick
provides (I know well the ObjectSpace binary streaming) are indeed a
good proof: they are almost as clean as possible, and without macros,
you would need to resort to cut and paste to provide equivalent
functionality!

The problem is one of lack of orthogonality between classification and
genericity: optimally, these should be orthogonal design dimensions,
but C++ templates always bring in a new scope (class or function), and
never "inject" generic features into an existing one.

The "trick" proposed by Darron Shaffer

class Foo: public virtual BoilerPlate<Foo> { /*...*/};

...with the Barton / Nackman refinements is only a workaround, and has
obvious limitations: it brings a new scope, BoilerPlate<Foo>, which is
in fact not less specialized than the Foo derived from it, i.e. almost
equivalent.

"Almost" will typically be insufficient when using multiple
inheritance:

class Foo: public virtual BoilerPlate<Foo>, public virtual Bar<Foo> ...

or for propagating friendship (not the same thing to be a friend of
Foo and of BoilerPlate<Foo>...).


The next thing that comes to mind is the following.

An injection mechanism would advantageously replace common misuses of
private inheritance. E.g. from D&E p 419:

class B {
  public:
    void f(char);
};

class D: private B {
  public:
    using B::f;
};

One wants here to inject in D's interface the f member, without
offering to the public the possibility to handle all Ds as Bs.

Best Regards!

--
Marc Girod                                   Phone:  +358-9-511 27703
Nokia Telecommunications   P.O. Box 370      Fax:    +358-9-511 27432
Kilo RD 4                  00045 NOKIA Group marc.girod@ntc.nokia.com
---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: Darron.Shaffer@beasys.com (Darron Shaffer)
Date: 1996/11/30
Raw View
A neat trick for injecting "boiler-plate" code into a class is:

template<class X>
class BoilerPlate
{
   // definitions that depend on class X
};


class Foo: public BoilerPlate<Foo>
{
   // specialized definitions
};


In this way the boiler plate can both be derived from (inserted) and adjusted
for the type it will eventually be used within.


Darron Shaffer
--
Darron Shaffer
Darron.Shaffer@beasys.com
---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: Nick Thurn <nickt@bain.oz.au>
Date: 1996/12/01
Raw View
Darron Shaffer wrote:
>
> A neat trick for injecting "boiler-plate" code into a class is:
>
> template<class X>
> class BoilerPlate
> {
>    // definitions that depend on class X
> };
>
> class Foo: public BoilerPlate<Foo>
> {
>    // specialized definitions
> };
>
> In this way the boiler plate can both be derived from (inserted) and adjusted
> for the type it will eventually be used within.
> Darron,

I'm aware of this one. Unfortunately it can't supply the implementation
of virtual functions.

template <class T>
class BoilerPlate
{
   //
 virtual X foo();
};
class Base
{
 //...
 virtual X foo()=0;
};
class Derived : public Base, public BoilerPlate<Derived>
{
 //... no go, still must declare and implement foo
};

You still need to inject the actual foo method somehow even if
BoilerPlate can supply the implementation.

Another solution is deriving BoilerPlate from Derived.

template <class T>
class BoilerPlate : public T
{
 //...
 virtual X foo();
};

This solves the virtual problem but you now have to create
BoilerPlate<Derived>'s instead of Derived's and lose access
to the methods in children of Derived. I guess you could
interpose a BoilerPlate<...> between each class superclass
pair (a Sandwich Pattern? ;-)

cheers
Nick


[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: "nicolas (n.) chapados" <chapados@nortel.ca>
Date: 1996/12/03
Raw View
Nick Thurn wrote:
>
> I'm aware of this one. Unfortunately it can't supply the implementation
> of virtual functions.
>
> template <class T>
> class BoilerPlate
> {
>         //
>         virtual X foo();
> };
> class Base
> {
>         //...
>         virtual X foo()=0;
> };
> class Derived : public Base, public BoilerPlate<Derived>
> {
>         //... no go, still must declare and implement foo
> };
>

In this particular case, the solution is quite simple
(thanks to Barton and Nackman):

class Base
{
 virtual X foo() = 0;
};

template <class T>
class BoilerPlate : public virtual Base
{
 // with an appropriate definition somewhere...
 virtual X foo();
};

class Derived : public virtual Base, private BoilerPlate<Derived>
{
 // Dominance rule of virtual inheritance ensures
 // that BoilerPlate<Derived>::foo is accessible here
};

The problem however is that in many practical problems, this solution
looks
better than it actually is.  (For example, when working on a persistance
mechanism, it seemed quite impossible to automatically inject the
appropriate
operator<< and operator>> into the global scope --- at least with the
compiler
I had.  Finally, a couple of macros did a good job. :-)

One is left to wonder what forms could take a nice non-macro
``injection''
feature, for persistence-like functionality.  [[Yes, I know about name
injection,
and no, it doesn't do what I want...]]

---
Nicolas Chapados                         Nortel Technology Ltd.
(formerly BNR)
chapados@nortel.ca                       Montreal, Canada
---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: Chelly Green <chelly@eden.com>
Date: 1996/12/03
Raw View
nicolas (n.) chapados wrote:
>
...
> In this particular case, the solution is quite simple
> (thanks to Barton and Nackman):
>
> class Base
> {
>         virtual X foo() = 0;
> };
>
> template <class T>
> class BoilerPlate : public virtual Base
> {
>         // with an appropriate definition somewhere...
>         virtual X foo();
> };
>
> class Derived : public virtual Base, private BoilerPlate<Derived>
> {
>         // Dominance rule of virtual inheritance ensures
>         // that BoilerPlate<Derived>::foo is accessible here
> };

But you're using virtual bases, so some other use in the same tree could
cause problems. I very rarely use virtual inheritance. I thought someone
had suggested this (but not shown the code):

    class Base { // interface class
    public:
        virtual void f() = 0;
    };

    template<class Base,class Derived>
    class Boilerplate : public Base {
    protected:
        // couldn't we make this private and make Derived a friend?
        Boilerplate() { }
    public:
        virtual void f()
        {
            // safe because we are always created as part of Derived
            Derived& derived = static_cast<Derived&> (*this);
        }
    };

    class Derived : public Boilerplate<Base,Derived> {
    public:
        // ...
    };

Or maybe that's what I was thinking. Doesn't this work? Boilerplate can
define functions that call inlines for Derived, or whatever.

> The problem however is that in many practical problems, this solution looks
> better than it actually is.  (For example, when working on a persistance
> mechanism, it seemed quite impossible to automatically inject the appropriate
> operator<< and operator>> into the global scope

A friend function?

> --- at least with the compiler
> I had.  Finally, a couple of macros did a good job. :-)

Why do you need different << and >> for each class? Wouldn't a virtual
function in a base class work?

    class Persistent {
    protected:
        virtual void put( ostream& ) const = 0;
        virtual void get( istream& );
    public:

        friend ostream& operator << ( ostream& s, Persistent const& p )
                { p.put( s ); return s; }
        friend istream& operator >> ( istream& s, Persistent& p )
                { p.get( s ); return s; }
    };

I guess I wouldn't want stupid virtual function overhead for a class
that wants to be persistent. I would use RTTI and a map of get and put
functions, and a polymorphic tag system.

> One is left to wonder what forms could take a nice non-macro ``injection''
> feature, for persistence-like functionality.  [[Yes, I know about name injection,
> and no, it doesn't do what I want...]]

Wouldn't a base class work?

    // I assume you mean a raw bits output
    void put_object( void const*, size_t );

    template<class T>
    class Persistent {
    protected:
        Persistent() { } // only let derived create
    public:
        friend ostream& operator << ( ostream& s, Persistent const& p )
        { put_object( &static_cast<T const&> (p), sizeof (T) ); return
s; }

    struct Point : Persistent<Point> {
        int h, v;
        // ...
    };

If compiler complain about Point, etc. (in the above examples) being
incomplete for the casts, then move the template function definitions
out of the class so they won't be instantiated immediately (maybe they
can still be inline, yet still have deferred instantiation).
--
Chelly Green | chelly@eden.com | C++ - http://www.eden.com/~chelly


[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: boukanov@sentef2.fi.uib.no (Igor Boukanov)
Date: 1996/12/04
Raw View
Chelly Green (chelly@eden.com) wrote:
> But you're using virtual bases, so some other use in the same tree could
> cause problems. I very rarely use virtual inheritance. I thought someone
> had suggested this (but not shown the code):

>     class Base { // interface class
>     public:
>         virtual void f() = 0;
>     };

>     template<class Base,class Derived>
>     class Boilerplate : public Base {
>     protected:
>         // couldn't we make this private and make Derived a friend?
>         Boilerplate() { }
>     public:
>         virtual void f()
>         {
>             // safe because we are always created as part of Derived
>             Derived& derived = static_cast<Derived&> (*this);
>         }
>     };

>     class Derived : public Boilerplate<Base,Derived> {
>     public:
>         // ...
>     };

Or maybe something like:

struct Base {
    virtual bool f() = 0;
};

template<class Base, class Derived1>
struct Boilerplate : Base, public Derived1 {
// or virtual Base, Derived1 - it depends
    virtual bool f()
    {
       return Derived1::f();
    }
};

template<class Base, class Derived1, class Derived2>
struct Boilerplate2 : Base, public Derived1, public Derived2 {
    virtual bool f()
    {
       return Derived1::f() || Derived2::f();
        //Or another code to select the order of f calls
    }
};

struct C1 : Boilerplate<Base,Derived> { };
struct C2 : Boilerplate2<Base,Derived1,Derived2> { };

BTW, here Base do not need to be a base class for Derived.

--
Regards, Igor Boukanov.
igor.boukanov@fi.uib.no
http://www.fi.uib.no/~boukanov/
---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: mcg@wheezy.CS.Berkeley.EDU (Michael C. Greenspon)
Date: 1996/11/24
Raw View
In article <572ooi$lu1@plath.bain.oz.au>, nickt@mailhost.bain.com.au (Nick
Thurn) wrote:

> Sorry if this has been discussed before.
>
> One of the common uses of macros is injection of boiler-plate
> code into classes in a particular hierarchy. This often means
> streaming or persistence code. Examples are in the ObjectSpace
> universal streaming interface, Orbix (last time I looked), MFC
> OWL, etc...
>
> IMO there is a valid need to support this functionality in a
> safer way.
>
> What about a "template" for the code and methods?


Good idea! Some way to 'mix in' some template interfaces which depend on
the enclosing scope.

The half-macro, half-type nature of C++ templates has bugged me for a
while. For example one often wants to do something like--

template<class T>
struct X {
   static string typename = #T;
   static string typekey = X_##T;
   // ...
};

but of course that won't work since # and ## are preprocessor operations
even though the template construct is essentially just doing text
substitution for its parameters....

IMHO the C++ standard has been driven too much by a love of language use
(perhaps the designers should have been lawyers instead) and legacy
considerations and not enough by practical contemporary programming needs.

I mean if templates are really compile-time factories for types, then why
not have conditional code generation, etc. so one can be fully general in
how types are produced? In fact why not have the full power of the
language available for creating types? i.e. why not make the language
fully interpreted and dynamically compiled on demand?  Hey, why not just
use Java!

---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: fjh@mundook.cs.mu.OZ.AU (Fergus Henderson)
Date: 1996/11/29
Raw View
mcg@wheezy.CS.Berkeley.EDU (Michael C. Greenspon) writes:

>The half-macro, half-type nature of C++ templates has bugged me for a
>while. For example one often wants to do something like--
>
>template<class T>
>struct X {
>   static string typename = #T;
>   static string typekey = X_##T;
>   // ...
>};
>
>but of course that won't work since # and ## are preprocessor operations
>even though the template construct is essentially just doing text
>substitution for its parameters....

C++ provides ways of doing both of these operations.
The alternative to `#T' is `typeid(T).name()'.

The alternative to `X_##type' with

 const string X_int = "int key";
 const string X_float = "float key";

is `TypeKey<T>::val' with

 template <class T> struct TypeKey { static const string val; }
 const string TypeKey<int>::val = "int key";
 const string TypeKey<int>::val = "float key";

Here, instead of using X_int, X_float, etc., I've used
the names TypeKey<int>::val, TypeKey<float>::val, etc.
Using templates works better for type names that are not plain
identifiers or keywords, e.g. `char *'.

>I mean if templates are really compile-time factories for types, then why
>not have conditional code generation, etc. so one can be fully general in
>how types are produced?

Template specialization can give you conditional code generation.
For example, have a look at the implementation of auto_ptr that I
recently posted here which conditionally uses either a pointer and a
bool or a tagged pointer depending on the size of the type involved.

>IMHO the C++ standard has been driven too much by a love of language use
>(perhaps the designers should have been lawyers instead) and legacy
>considerations and not enough by practical contemporary programming needs.

Perhaps you are just not familiar with what can be done with the
facilities that the C++ standard provides.

--
Fergus Henderson <fjh@cs.mu.oz.au>   |  "I have always known that the pursuit
WWW: <http://www.cs.mu.oz.au/~fjh>   |  of excellence is a lethal habit"
PGP: finger fjh@128.250.37.3         |     -- the last words of T. S. Garp.
---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: nickt@mailhost.bain.com.au (Nick Thurn)
Date: 1996/11/22
Raw View
Sorry if this has been discussed before.

One of the common uses of macros is injection of boiler-plate
code into classes in a particular hierarchy. This often means
streaming or persistence code. Examples are in the ObjectSpace
universal streaming interface, Orbix (last time I looked), MFC
OWL, etc...

IMO there is a valid need to support this functionality in a
safer way.

What about a "template" for the code and methods?
The general idea is that instantiating the "template" would
inject it's methods into the surrounding type scope with
the supplied typing parameters.

I have no idea what the full implications of this are. I believe
a new keyword would be required eg "protocol" or "interface"

The following is an example of how it *could* work.

declaration:

 // .h file
 protocol <class A, class B> Name // not a type ???
 {
  // No constructors or destructors
   private:
  void   blah1(const A&);
  virtual void  blah2(const B&)
  static void  blah3(C*);

  D  d;
  static E e;

   public:
  F*  blah4() const; // user implemented
  typedef A g;
 };

 // .cpp file
 protocol <class A, class B...> inline void
 Name<A,B>::blah1(const A&)
 {
  // ...
 }
 protocol <class A, class B...> void
 Name<A,B>::blah2(const B&)
 {
  // ...
 }
 protocol <class A, class B...> void
 Name<A,B>::blah3(C*)
 {
  // ...
 }

usage:

 class X
 {
  protocol <Y,Z> Name;
  class Q
  {
   protocol <R,S> Name;
   // rest of Q...
  };
  // the rest of X...
 };

 inline F* X::blah4() const
 {
  //...
 }
 inline F* X::Q::blah4() const
 {
  //...
 }


As you can see this is totally undeveloped however I believe
there is a need for a better mechanism than macros to perform
this widely used function.

Comments?

cheers
Nick (my opinions only)
---
[ 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         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]