The Artima Developer Community
Sponsored Link

Weblogs Forum
Bolt-Ins for Contract and Extension Classes

16 replies on 2 pages. Most recent reply: Apr 28, 2005 5:53 PM by Christopher Diggins

Welcome Guest
  Sign In

Go back to the topic listing  Back to Topic List Click to reply to this topic  Reply to this Topic Click to search messages in this forum  Search Forum Click for a threaded view of the topic  Threaded View   
Previous Topic   Next Topic
Flat View: This topic has 16 replies on 2 pages [ 1 2 | » ]
Christopher Diggins

Posts: 1215
Nickname: cdiggins
Registered: Feb, 2004

Bolt-Ins for Contract and Extension Classes (View in Weblogs)
Posted: Apr 27, 2005 2:36 PM
Reply to this message Reply
Summary
At Matthew Wilson's behest, I have attempted to further explain my rationale behind separate extension classes, and contract verification classes. It turns out they fit very nicely in a design pattern which Matthew refers to as a bolt-in.
Advertisement

Bolt-Ins

A bolt-in is a sub-case of template parameter inheritance. Matthew Wilson defines a bolt-in (a term he attributes to his colleagues Leigh and Scott Perry) in his book Imperfect C++ as follows:
Bolt-ins are template classes with the following characteristics:
  • They derive, usually publicly, from their primary parameterizing type.
  • They accomodate the polymorphic nature of their primary parameterizing types. Usually they also adhere to the nature, but this is not always the case, and they may define virtual methods of their own, in addition to overriding those defined by the primary parameterizing type.
  • They may increase the footprint of the primary parameterizing type by the definitiion of member variables, virtual functions, and additional inheritance from nonempty types.

- Imperfect C++, page 375, by Matthew Wilson, published by Addison-Wesley, 2005

In all frankness I consider that to be a somewhat fluffy defintion, and I would have stated it more concisely as follows:
Bolt-ins are template classes which inherit from their primary parameterizing type, while accomodating its polymorphic nature.
The other parts of the definition are superflous, and do not add to the definition. They appear to be solely intended to distinguish bolt-ins from veneers. Matthew goes on to disintiguish a bolt-in primarily by its role:
... bolt-ins are concerned with significantly changing or completing the behavioural nature of (often partially defined) types. [...] The primary purpose of a bolt-in is to add or enhance functionality.

- Imperfect C++, page 375, by Matthew Wilson, published by Addison-Wesley, 2005

Unfortunately I find it difficult to identify specifically when I am using a bolt-in and when I am using plain old template parameter inheritance. However I will use the term bolt-in for the time being. I would however like to encourage Matthew to further refine the definitions of bolt-ins, veneers and attempt to formalize the other forms of template parameter inheritance patterns. On that note, Joel de Guzman suggested the term Parametric Base Class Pattern (PBCP) for all the forms of template parameter inheritance.

Separation of Concerns

The software engineering notion of "separation of concerns" refers to the fact that within a well-engineered piece of software, we should find code which is responsible for a specific concern located within a single module.

Following the principle of separation of concerns has the benefit of reduction of code coupling, and improvement of code cohesion. These are both well-known good software engineering practices. The advantages are that following these principles more naturally leads to more maintainable and reusable code.

Bolt-ins can be used to enforce separation of concerns, and to reduce code coupling.

Programming with Contracts

When it comes to the concern of verification of contractual conditions (i.e. preconditions, postconditions and invariants), this is commonly tangled with the implementation of the class itself. For instance consider the following class:
  class SimpleClass {
  public:
    Init() {
      // do initialization
    }
    void DoSomething() {
      // precondition: object must be initialized
      // do something
    }
  }
There is here an implied precondition to calling DoSomething() which is that the member function Init() must have been previously called on a particular instance. A common and somewhat naive approach to assuring the precondition is done by introducing a member variable:
  class SimpleClass {
  public:
    SimpleClass() : mbInit(false) {
    }
    Init() {
      // do initialization
      mbInit = true;
    }
    void DoSomething() {
      assert(mbInit);
      // do something
    }
  private:
    bool mbInit;
  }
Notice that mbInit is solely used for verifying that the class is used correctly. The problem is that it probably will only be needed during debug builds. The naive solution then would be:
  class SimpleClass {
  public:
    SimpleClass()
#ifdef _DEBUG
      : mbInit(false)
#endif
    { }
    Init() {
#ifdef _DEBUG
      // do initialization
      mbInit = true;
#endif
    }
    void DoSomething() {
      assert(mbInit);
      // do something
    }
  private:
#ifdef _DEBUG
    bool mbInit;
#endif
  }
Needlessly to say that is a mess. The problem results from a failure to separate concerns modularly. One improved solution is to use inheritance:
  class SimpleClass_with_contract : SimpleClass_impl {
  public:
    void SimpleClass_with_contract() : mbInit(false) {
    }
    void Init() {
      SimpleClass_impl::Init();
      mbInit = true;
    }
    void DoSomething() {
      assert(mbInit);
      SimpleClass_impl::DoSomething();
    }
  private:
    bool mbInit;
  };

  class SimpleClass_impl {
  public:
    void Init() {
      // do initialization
    }
    void DoSomething() {
      // do something
    }
  };

  #ifdef _DEBUG
    typedef SimpleClass_with_contract SimpleClass;
  #else
    typedef SimpleClass_impl SimpleClass;
  #endif
This solution is good, but the contract verification class is not reusable because it is coupled with that particular implementation. This arises because we have only partially separated the concern of contract verification. Ideally I want something reusable, so I will use a class which inherits from its template parameters (a bolt-in).
  template<typename Impl_T>
  class Simple_contract : Impl_T {
  public:
    void Simple_contract() : mbInit(false) {
    }
    void Init() {
      Impl_T::Init();
      mbInit = true;
    }
    void DoSomething() {
      assert(mbInit);
      Impl_T::DoSomething();
    }
  private:
    bool mbInit;
  };

  class SimpleClass_impl {
  public:
    void Init() {
      // do initialization
    }
    void DoSomething() {
      // do something
    }
  };

  #ifdef _DEBUG
    struct SimpleClass :
      Simple_contract<SimpleClass>
    { };
  #else
    struct SimpleClass :
      SimpleClass_impl
    { };
  #endif
This new contract verification class can now be used with other classes with similar interfaces:
  class AnotherSimpleClass_impl {
  public:
    void Init() {
      // do initialization
    }
    void DoSomething() {
      // do something
    }
  };

  #ifdef _DEBUG
    struct AnotherSimpleClass :
      Simple_contract<AnotherSimpleClass>
    { };
  #else
    typedef AnotherSimpleClass_impl AnotherSimpleClass;
  #endif
This demonstrates how through the diligent practice of separation of concerns we can arrive at code which is more reusable.

Extension Classes

The public member functions of many classes can be divided up into two relatively distinct groups, core functions and derived functions. The derived functions can be thought of as syntactic sugar, that is functions which can be defined in terms of the core functions. These derived functions are a good candidate to use within a bolt-in. Consider the case of the following naive and minimalist string implementation:
  class MyString_impl {
  public:
    void SetCount(int n) {
      m.resize(n);
    }
    void SetChar(int n, char c) {
      m[n] = c;
    }
    char GetChar(int n) const {
      return m[n];
    }
    int Count() {
      return m.size();
    }
  private:
    std::vector<char> m;
  };
This class is too minimalist to be of much use as-is, however with this basic implementation you can implement virtually every other concievable string member function. By writing the derived functions within a bolt-in, I can reuse the derived set of memember functions on any class which matches the code interface. For instance consider the following bolt-in:
  template<typename Impl_T>
  class String_ext : public Impl_T {
  public:
    void Concat(const Impl_T& x) {
      int n = Count();
      SetCount(n + x.Count());
      for (int i=n; < Count(); i++) {
        SetChar(i, x.GetChar(i-n));
      }
    }
    void Assign(const Impl_T& x) {
      SetCount(x.Count());
      for (int i=0; i < Count(); i++) {
        SetChar(i, x.GetChar());
      }
    }
    Impl_T SubString(int n, int cnt) const {
      Impl_T s;
      s.SetCount(cnt);
      for (int i=0; i < cnt; i++) {
        s.SetChar(i, GetChar(i + n));
      }
      return s;
    }
    // and so on and so forth
  };
This extension class can be bolted on to any compatible class implementation as simply as:
  typedef String_ext<MyString_impl> MyString;
The advantage of doing this is that the extension class can be reused on any class matching the core interface. This also reduces the coupling between extension functions and the implementation to a set of four functions. Refactoring and debugging the code as a result becomes much easier.

We can also define a contract verification class for MyString as follows:

  template<typename Impl_T>
  class String_contract : public Impl_T {
  public:
    void SetChar(int n, char c) {
      assert(n < Count());
      Impl_T::SetChar(n);
      assert(GetChar(n) == c);
    }
    char GetChar(int n) const {
      assert(n < Count());
      return Impl_T::GetChar(n);
    }
  }
Tying everything together gives us:
  #ifdef _DEBUG
    typedef String_ext<String_contract<MyString_impl> > MyString;
  #else
    typedef String_ext<MyString_impl> MyString;
  #endif

The Big Gotcha

Hopefully I have demonstrated the theoretical advantages of separating classes into reusable components as bolt-ins. There is however a big problem with all of this, constructor inheritance. In C++ constructors are not inherited, which really puts a wrench in the gears (and makes me wish I was working with Heron instead of C++).

One solution I am aware of is to use template constructors. This is not a perfect solution though.

My currently preferred solution to this design conundrum is to forego initializing construction entirely within my libraries. A rather drastic and contentious measure, but perhaps one which may pay off, because it opens the door more widely to sophisticated usage of template parameter inheritance techniques such as bolt-ins, contract verificiation classes, extensions classes, and more. Some debate on the merits of this approach can be found in the comments of my earlier blog entry Two Stage Construction in C++ versus Initializing Constructors


Kristian Dupont

Posts: 22
Nickname: chryler
Registered: Dec, 2003

Re: Bolt-Ins for Contract and Extension Classes Posted: Apr 27, 2005 3:54 PM
Reply to this message Reply
Sorry if I am annoying now, but your first example with the init() method and minit flag is a school example of how wonderful RAII is.. Get rid of initalizing and finalizing functions and have your compiler enforce your contract rather than your running program.
(Alright, I know that you were demonstrating a point.)

Does Heron require two-level initialization of members?

Christopher Diggins

Posts: 1215
Nickname: cdiggins
Registered: Feb, 2004

Re: Bolt-Ins for Contract and Extension Classes Posted: Apr 27, 2005 4:25 PM
Reply to this message Reply
> Sorry if I am annoying now,

Heavens no! :-)

> but your first example with
> the init() method and minit flag is a school example of
> how wonderful RAII is.. Get rid of initalizing and
> finalizing functions and have your compiler enforce your
> contract rather than your running program.
> (Alright, I know that you were demonstrating a point.)

Yes, but there are scenarios where two stage construction is more appropriate. For instance when we want an array of that particular object.

> Does Heron require two-level initialization of members?

Not in the current specification, I followed C++'s lead, however it does have inheritance of constructors, so this is less of a headache IMHO. This is possible in Heron and not C++ because Heron doesn't allow multiple inheritance.

indranil banerjee

Posts: 38
Nickname: indranil
Registered: Nov, 2004

Re: Bolt-Ins for Contract and Extension Classes Posted: Apr 27, 2005 4:47 PM
Reply to this message Reply
Very interesting. I did need to see this second post before I felt able to comment.

You know the String_ext class only depends on the public interface of the String and has no member data of its own. So I would strongly favour implementing those methods as non member functions. This is the approach Boost took with the string_algo library, std::string's interface is quite large enough!

There are a couple of other advantages to non members. If someone has got a 'raw' String_Impl class or another class statisfying the String interface, they cant use the methods of String_ext without creating a new String_ext fom the String_Impl. Doesnt this defeat the dynamic typing goals that you have been working towards?

I also think, if String_ext became a public, widely used interface it would be very difficult to extend. If you devised new algorithms that could be applied to the basic String interface, they would have to go in String_ext_ext. With non members you can just keep adding to your library of algorithms

String_contract seems a much better candidate for your Bolt-In technique. I guess two phase construction is the only way to go. Otherwise String_contract could only wrap classes that statisfied the String interface AND had exactly the same set of constructors as String_contract. Of course now String_contract can only support types that support the String interface and the Init interface.

cheers

Indranil

indranil banerjee

Posts: 38
Nickname: indranil
Registered: Nov, 2004

Re: Bolt-Ins for Contract and Extension Classes Posted: Apr 27, 2005 5:15 PM
Reply to this message Reply
BTW, are you aware of the approach Andrei Alexandescu has taken to produce a well factored policy based string class with flex_string?
http://www.moderncppdesign.com/code/main.html

Christopher Diggins

Posts: 1215
Nickname: cdiggins
Registered: Feb, 2004

Re: Bolt-Ins for Contract and Extension Classes Posted: Apr 27, 2005 6:32 PM
Reply to this message Reply
> BTW, are you aware of the approach Andrei Alexandescu has
> taken to produce a well factored policy based string class
> with flex_string?
> http://www.moderncppdesign.com/code/main.html

I was not aware of this, would you mind summarizing what the relevant portions of his work are?

Christopher Diggins

Posts: 1215
Nickname: cdiggins
Registered: Feb, 2004

Re: Bolt-Ins for Contract and Extension Classes Posted: Apr 27, 2005 6:49 PM
Reply to this message Reply
> Very interesting. I did need to see this second post
> before I felt able to comment.
>
> You know the String_ext class only depends on the public
> interface of the String and has no member data of its own.
> So I would strongly favour implementing those methods as
> non member functions.

Why?

> This is the approach Boost took with
> the string_algo library, std::string's interface is quite
> large enough!

I don't see how using non-members is a superior approach over extension classes. Nothing is saved by making the function non-member functions, they are just scattered throughout the namespace in a non-modular manner. It feels like I am stepping back in time to old fashioned C ;-)

> There are a couple of other advantages to non members. If
> someone has got a 'raw' String_Impl class or another class
> statisfying the String interface, they cant use the
> methods of String_ext without creating a new String_ext
> fom the String_Impl. Doesnt this defeat the dynamic typing
> goals that you have been working towards?

Not really, I don't view dynamic and static typing as mutually exclusive goals.

> I also think, if String_ext became a public, widely used
> interface it would be very difficult to extend.

That does seem like a possibility.

> If you
> devised new algorithms that could be applied to the basic
> String interface, they would have to go in String_ext_ext.

It doesn't seem so bad to me, but nothing is stopping these extra extensions from being overloaded non-member functions (or shims as Matthew calls them).

> With non members you can just keep adding to your library
> of algorithms

Until your namespace is overloaded ;-)

> String_contract seems a much better candidate for your
> Bolt-In technique. I guess two phase construction is the
> only way to go. Otherwise String_contract could only wrap
> classes that statisfied the String interface AND had
> exactly the same set of constructors as String_contract.

Or we can also use template constructors. It is a funky kettle of fish, but it can work.

> Of course now String_contract can only support types that
> support the String interface and the Init interface.

Yes, that is limiting. I wish I could figure out a way around this. It is still hopefully a useful technique. I used it with relative success when writing the Heron Standard Library, and hopefully it will work well for the OOTL.

Matthew Wilson

Posts: 145
Nickname: bigboy
Registered: Jun, 2004

Re: Bolt-Ins for Contract and Extension Classes Posted: Apr 27, 2005 9:51 PM
Reply to this message Reply
> > So I would strongly favour implementing those methods
> as
> > non member functions.
>
> Why?

> I don't see how using non-members is a superior approach
> over extension classes. Nothing is saved by making the
> function non-member functions, they are just scattered
> throughout the namespace in a non-modular manner. It feels
> like I am stepping back in time to old fashioned C ;-)

I say upfront that I've not read this for over a year, but I recall being in complete agreement at each read since its publication in 2000: http://www.cuj.com/documents/s=8042/cuj0002meyers/meyers.htm

[You might take some meta-persuasion on that very thing, since Scott Meyers and I are wont to disagree about a great many things. ;)]

<snipped the rest, as I'm not yet through your original post>

Matthew Wilson

Posts: 145
Nickname: bigboy
Registered: Jun, 2004

Re: Bolt-Ins for Contract and Extension Classes Posted: Apr 27, 2005 10:28 PM
Reply to this message Reply
> <h2>Bolt-Ins</h2>

[snip]

I'll address all that at a later stage

> <h2>Separation of Concerns</h2>
>
> The software engineering notion of "separation of
> concerns" refers to the fact that within a
> well-engineered
> piece of software, we should find code which is
> responsible for a specific concern located within a single
> module.
> <p>
> Following the principle of separation of concerns has the
> benefit of reduction of code coupling, and improvement of
> code cohesion.
> These are both well-known good software engineering
> practices. The advantages are that following these
> principles more naturally leads
> to more maintainable and reusable code.
> <p>
> Bolt-ins can be used to enforce separation of concerns,
> and to reduce code coupling.

Nice point.

> <h2>Programming with Contracts</h2>
>
> When it comes to the concern of verification of
> contractual conditions (i.e. preconditions, postconditions
> and invariants), this is
> commonly tangled with the implementation of the class
> itself. For instance consider the following class:
>
> <pre>
> class SimpleClass {
> public:
> Init() {
> // do initialization
> }
> void DoSomething() {
> // precondition: object must be initialized
> // do something
> }
> }
> </pre>
>
> There is here an implied precondition to calling
> <tt>DoSomething()</tt> which is that the member function
> <tt>Init()</tt> must have been previously
> called on a particular instance. A common and somewhat
> naive approach to assuring the precondition is done by
> introducing a member variable:
>
> <pre>
> class SimpleClass {
> public:
> SimpleClass() : mbInit(false) {
> }
> Init() {
> // do initialization
> mbInit = true;
> }
> void DoSomething() {
> assert(mbInit);
> // do something
> }
> private:
> bool mbInit;
> }
> </pre>
>
> Notice that <tt>mbInit</tt> is solely used for verifying
> that the class is used correctly. The problem is that it
> probably will only be
> needed during debug builds.

I dislike "debug vs release" language when talking about contract programming, and suggest it unwise of you to use it when writing about it. May I suggest that we talk about differentiating between builds with different aspects of contract programming set at different levels of activity? Naturally the most coarse grained and widely used is simply between debug (all on) and release (all off) but, IMO at least, real world systems lie somewhere in between (or they should do, anyway! <g>).

> The naive solution then would
> be:
>
> <pre>
> class SimpleClass {
> public:
> SimpleClass()
> #ifdef _DEBUG
> : mbInit(false)
> #endif
> { }
> Init() {
> #ifdef _DEBUG
> // do initialization
> mbInit = true;
> #endif
> }
> void DoSomething() {
> assert(mbInit);
> // do something
> }
> private:
> #ifdef _DEBUG
> bool mbInit;
> #endif
> }
> </pre>

Nit: use of _DEBUG is not advised, as it's not portable. Just as common an equivalent - on another platform ;) - is the absence of NDEBUG. It tend to discriminate an ACMELIB_DEBUG symbol taking into account compiler, platform and presence/absence of _DEBUG/NDEBUG, e.g.



>
> Needlessly to say that is a mess. The problem results from
> a failure to separate concerns modularly. One improved
> solution is to
> use inheritance:
>
> <pre>
> class SimpleClass_contract : SimpleClass_impl {
> public:
> void SimpleClass() : mbInit(false) {
> }
> void Init() {
> SimpleClass_impl::Init();
> mbInit = true;
> }
> void DoSomething() {
> assert(mbInit);
> SimpleClass_impl::DoSomething();
> }
> private:
> bool mbInit;
> };
>
> class SimpleClass_impl {
> public:
> void Init() {
> // do initialization
> }
> void DoSomething() {
> // do something
> }
> };
>
> #ifdef _DEBUG
> typedef SimpleClass_contract SimpleClass;
> #else
> typedef SimpleClass_impl SimpleClass;
> #endif
> </pre>
>
> This solution is good, but the contract verification class
> is not reusable because it is coupled with that particular
> implementation.
> This arises because we have only partially separated the
> concern of contract verification.
> Ideally I want something reusable, so I will use a class
> which inherits from its template parameters (a bolt-in).
>
> <pre>
> template<typename Impl_T>
> class Simple_contract : Impl_T {
> public:
> void Simple_contract() : mbInit(false) {
> }
> void Init() {
> Impl_T::Init();
> mbInit = true;
> }
> void DoSomething() {
> assert(mbInit);
> Impl_T::DoSomething();
> }
> private:
> bool mbInit;
> };
>
> class SimpleClass_impl {
> public:
> void Init() {
> // do initialization
> }
> void DoSomething() {
> // do something
> }
> };
>
> #ifdef _DEBUG
> struct SimpleClass :
> Simple_contract<SimpleClass>
> { };
> #else
> struct SimpleClass :
> SimpleClass_impl
> { };
> #endif
> </pre>

There's a mistake here, isn't there? Shouldn't it be


#ifdef _DEBUG
struct SimpleClass :
Simple_contract<SimpleClass_impl>
{ };
#else


- which isn't a bolt-in, then - or am I misunderstanding?


> This new contract verification class can now be used with
> other classes with similar interfaces:
>
> <pre>
> class AnotherSimpleClass_impl {
> public:
> void Init() {
> // do initialization
> }
> void DoSomething() {
> // do something
> }
> };
>
> #ifdef _DEBUG
> struct AnotherSimpleClass :
> Initializing_contract<AnotherSimpleClass>
> { };
> #else
> typedef AnotherSimpleClass_impl AnotherSimpleClass;
> #endif
> </pre>

Have I missed a step? Isn't Initializing_contract actually Simple_contract?

> This demonstrates how through the diligent practice of
> separation of concerns we can arrive at code which is more
> reusable.
>
> <h2>Extension Classes</h2>
>
> The public member functions of many classes can be divided
> up into two relatively distinct groups, core functions
> and derived functions. The derived functions can be
> thought of as syntactic sugar, that is functions which can
> be
> defined in terms of the core functions. These derived
> functions are a good candidate to use within a bolt-in.

Although I tend to favour the non-member approach, this last sentence makes a sound point, and is agreeable (to me anyway <g>).

> Consider
> the case of the following naive and minimalist string
> implementation:
>
> <pre>
> class MyString_impl {
> public:
> void SetCount(int n) {
> m.resize(n);
> }
> void SetChar(int n, char c) {
> m[n] = c;
> }
> char GetChar(int n) const {
> return m[n];
> }
> int Count() {
> return m.size();
> }
> private:
> std::vector<char> m;
> };
> </pre>
>
> This class is too minimalist to be of much use as-is,
> however with this basic implementation you can implement
> virtually every other
> concievable string member function. By writing the derived
> functions within a bolt-in, I can reuse the derived set of
> memember functions
> on any class which matches the code interface. For
> instance consider the following bolt-in:
>
> <pre>
> template<typename Impl_T>
> class String_ext : public Impl_T {
> public:
> void Concat(const Impl_T& x) {
> int n = Count();
> SetCount(n + x.Count());
> for (int i=n; < Count(); i++) {
> SetChar(i, x.GetChar(i-n));
> }
> }
> void Assign(const Impl_T& x) {
> SetCount(x.Count());
> for (int i=0; i < Count(); i++) {
> SetChar(i, x.GetChar());
> }
> }
> Impl_T SubString(int n, int cnt) const {
> Impl_T s;
> s.SetCount(cnt);
> for (int i=0; i < cnt; i++) {
> s.SetChar(i, GetChar(i + n));
> }
> return s;
> }
> // and so on and so forth
> };
> </pre>
>
> This extension class can be bolted on to any compatible
> class implementation as simply as:
>
> <pre>
> typedef String_ext<MyString_impl> MyString;
> </pre>

Yes! This is exactly Bolt-in-ness to a T. :-)

> The advantage of doing this is that the extension class
> can be reused on any class matching the
> core interface. This also reduces the coupling between
> extension functions and the implementation
> to a set of four functions. Refactoring and debugging the
> code as a result becomes much easier.
> <p>
> We can also define a contract verification class for
> MyString as follows:
>
> <pre>
> template<typename Impl_T>
> class String_contract : public Impl_T {
> public:
> void SetChar(int n, char c) {
> assert(n < Count());
> Impl_T::SetChar(n);
> assert(GetChar(n) == c);
> }
> char GetChar(int n) const {
> assert(n < Count());
> return Impl_T::GetChar(n);
> }
> }
> </pre>
>
> Tying everything together gives us:
>
> <pre>
> #ifdef _DEBUG
> typedef
> edef String_ext<String_contract<MyString_impl> >
> MyString;
> #else
> typedef String_ext<MyString_impl> MyString;
> #endif
> </pre>
>
> <h2>The Big Gotcha</h2>
>
> Hopefully I have demonstrated the theoretical advantages
> of separating classes into reusable components as
> bolt-ins.
> There is however a big problem with all of this,
> constructor inheritance. In C++ constructors are not
> inherited, which really
> puts a wrench in the gears (and makes me wish I was
> working with Heron instead of C++).
> <p>
> One solution I am aware of is to use template
> constructors. This is not a perfect solution though.
> <p>
> My currently preferred solution to this design conundrum
> is to forego initializing construction entirely within my
> libraries.
> A rather drastic and contentious measure, but perhaps one
> which may pay off, because it opens the door more widely
> to sophisticated usage
> of template parameter inheritance techniques such as
> bolt-ins, contract verificiation classes, extensions
> classes, and more.
> Some debate on the merits of this approach can be found in
> the comments of my earlier blog entry
> <a
> href="http://www.artima.com/weblogs/viewpost.jsp?thread=106
> 524">Two Stage Construction in C++ versus Initializing
> Constructors</a>

Ok, now I get where you're coming from.

I have a very prosaic response: all this effort in making a set of related classes as templates, rather than as plain classes, is only worth it if one can apply the bolt-in class templates to other components. Given the strong structural requirement constraints on the bolt-ee from the bolt-in - i.e. String_contract's bolt-ee needs to provide SetChar() and GetChar() - then one is writing a specific bolt-in in any case, so the exta effort in writing appropriate specific forwarding constructors is not prohibitive.

Since semantic checking is done when template are instantiated, it is perfectly safe to define a 'better' Bolt-in than is required for all the bolt-ee types, because only those ones used will be checked for validity by the compiler. Yes, there's an issue of maintenance in making your bolt-in class keep up with the bolt-ee classes, but that's not different than doing a more conventional approach. Indeed, it's still likely to be a big saving, since you only need to change the bolt-in once to reflect a new facility irrespective of how many bolt-ee types evolve to express that facility.

An example of this controlled complexity is shown in STLSoft's sequence_container_veneer. Here are all its forwarding constructors, enabling it to support the construction of types bolt-ed from all STL-sequence containers:


template< typename T
, typename F
>
class sequence_container_veneer
: public T
{
. . .

// Forwarding constructors

/// Constructs from a range
template <typename I>
sequence_container_veneer(I i1, I i2)
: parent_class_type(i1, i2)
{}
/// Constructs from a range with the given allocator
template <typename I>
sequence_container_veneer(I i1, I i2, allocator_type const &a)
: parent_class_type(i1, i2, a)
{}
/// Constructs with the given number of elements (initialised with the given value)
template <typename V>
sequence_container_veneer(size_type n, V v)
: parent_class_type(n, v)
{}
/// Constructs with the given number of elements (initialised with the given value) with the given allocator
template <typename V>
sequence_container_veneer(size_type n, V v, allocator_type const &a)
: parent_class_type(n, v, a)
{}


By contrast, the Synesis RefCounter bolt-in, which is a true generic bolt-in in all its scary ramifications, looks like the following:


template< typename T
, typename _S_
, typename _C_ >
class RefCounter
: public T
, public _S_
{
public:
typedef T ParentClass;
typedef _S_ SyncClass;
typedef _C_ CountClass;
typedef RefCounter<T, _S_, _C_> Class;
typedef _mP(ParentClass) ParentClassPtr;
typedef _mP(Class) ClassPtr;
typedef _mPC(Class) ClassConstPtr;
typedef _mR(Class) ClassRef;
typedef _mRC(Class) ClassConstRef;

// Construction
public:
RefCounter();
RefCounter(const Class &rhs);
virtual ~RefCounter() SyThrow_None();

#ifdef __SYNSOFT_DBS_CONSTRUCTOR_TEMPLATE_SUPPORT
// For compilers that support member templates, the following constructors
// are provided. Note that the first variant (one arg plus initial ref
// count) has to have a non-default ref-count argument so as to disambiguate
// it from the standard constructor above, and each variant following has the
// same constraint. Whilst less than optimal, this approach allows access to
// the ctors of the wrapped class.
template <typename N1>
RefCounter(N1 &n1)
: ParentClass(n1)
, m_cRefs(CountClass::Count)
{}
#if defined(__DMC__)
template <typename N1>
RefCounter(N1 *n1)
: ParentClass(n1)
, m_cRefs(CountClass::Count)
{}
#endif /* compiler */

template <typename N1, typename N2>
RefCounter(N1 &n1, N2 &n2)
: ParentClass(n1, n2)
, m_cRefs(CountClass::Count)
{}
template <typename N1, typename N2>
RefCounter(N1 &n1, N2 *n2)
: ParentClass(n1, n2)
, m_cRefs(CountClass::Count)
{}
template <typename N1, typename N2>
RefCounter(N1 *n1, N2 &n2)
: ParentClass(n1, n2)
, m_cRefs(CountClass::Count)
{}
template <typename N1, typename N2>
RefCounter(N1 *n1, N2 *n2)
: ParentClass(n1, n2)
, m_cRefs(CountClass::Count)
{}

template <typename N1, typename N2, typename N3>
RefCounter(N1 &n1, N2 &n2, N3 &n3)
: ParentClass(n1, n2, n3)
, m_cRefs(CountClass::Count)
{}
template <typename N1, typename N2, typename N3>
RefCounter(N1 &n1, N2 &n2, N3 *n3)
: ParentClass(n1, n2, n3)
, m_cRefs(CountClass::Count)
{}
template <typename N1, typename N2, typename N3>
RefCounter(N1 &n1, N2 *n2, N3 &n3)
: ParentClass(n1, n2, n3)
, m_cRefs(CountClass::Count)
{}
template <typename N1, typename N2, typename N3>
RefCounter(N1 &n1, N2 *n2, N3 *n3)
: ParentClass(n1, n2, n3)
, m_cRefs(CountClass::Count)
{}
template <typename N1, typename N2, typename N3>
RefCounter(N1 *n1, N2 &n2, N3 &n3)
: ParentClass(n1, n2, n3)
, m_cRefs(CountClass::Count)
{}
template <typename N1, typename N2, typename N3>
RefCounter(N1 *n1, N2 &n2, N3 *n3)
: ParentClass(n1, n2, n3)
, m_cRefs(CountClass::Count)
{}
template <typename N1, typename N2, typename N3>
RefCounter(N1 *n1, N2 *n2, N3 &n3)
: ParentClass(n1, n2, n3)
, m_cRefs(CountClass::Count)
{}
template <typename N1, typename N2, typename N3>
RefCounter(N1 *n1, N2 *n2, N3 *n3)
: ParentClass(n1, n2, n3)
, m_cRefs(CountClass::Count)
{}

template <typename N1, typename N2, typename N3, typename N4>
RefCounter(N1 &n1, N2 &n2, N3 &n3, N4 &n4)
: ParentClass(n1, n2, n3, n4)
, m_cRefs(CountClass::Count)
{}
template <typename N1, typename N2, typename N3, typename N4>
RefCounter(N1 &n1, N2 &n2, N3 &n3, N4 *n4)
: ParentClass(n1, n2, n3, n4)
, m_cRefs(CountClass::Count)
{}
template <typename N1, typename N2, typename N3, typename N4>
RefCounter(N1 &n1, N2 &n2, N3 *n3, N4 &n4)
: ParentClass(n1, n2, n3, n4)
, m_cRefs(CountClass::Count)
{}
template <typename N1, typename N2, typename N3, typename N4>
RefCounter(N1 &n1, N2 &n2, N3 *n3, N4 *n4)
: ParentClass(n1, n2, n3, n4)
, m_cRefs(CountClass::Count)
{}
template <typename N1, typename N2, typename N3, typename N4>
RefCounter(N1 &n1, N2 *n2, N3 &n3, N4 &n4)
: ParentClass(n1, n2, n3, n4)
, m_cRefs(CountClass::Count)
{}
template <typename N1, typename N2, typename N3, typename N4>
RefCounter(N1 &n1, N2 *n2, N3 &n3, N4 *n4)
: ParentClass(n1, n2, n3, n4)
, m_cRefs(CountClass::Count)
{}
template <typename N1, typename N2, typename N3, typename N4>
RefCounter(N1 &n1, N2 *n2, N3 *n3, N4 &n4)
: ParentClass(n1, n2, n3, n4)
, m_cRefs(CountClass::Count)
{}
template <typename N1, typename N2, typename N3, typename N4>
RefCounter(N1 &n1, N2 *n2, N3 *n3, N4 *n4)
: ParentClass(n1, n2, n3, n4)
, m_cRefs(CountClass::Count)
{}
template <typename N1, typename N2, typename N3, typename N4>
RefCounter(N1 *n1, N2 &n2, N3 &n3, N4 &n4)
: ParentClass(n1, n2, n3, n4)
, m_cRefs(CountClass::Count)
{}
template <typename N1, typename N2, typename N3, typename N4>
RefCounter(N1 *n1, N2 &n2, N3 &n3, N4 *n4)
: ParentClass(n1, n2, n3, n4)
, m_cRefs(CountClass::Count)
{}
template <typename N1, typename N2, typename N3, typename N4>
RefCounter(N1 *n1, N2 &n2, N3 *n3, N4 &n4)
: ParentClass(n1, n2, n3, n4)
, m_cRefs(CountClass::Count)
{}
template <typename N1, typename N2, typename N3, typename N4>
RefCounter(N1 *n1, N2 &n2, N3 *n3, N4 *n4)
: ParentClass(n1, n2, n3, n4)
, m_cRefs(CountClass::Count)
{}
template <typename N1, typename N2, typename N3, typename N4>
RefCounter(N1 *n1, N2 *n2, N3 &n3, N4 &n4)
: ParentClass(n1, n2, n3, n4)
, m_cRefs(CountClass::Count)
{}
template <typename N1, typename N2, typename N3, typename N4>
RefCounter(N1 *n1, N2 *n2, N3 &n3, N4 *n4)
: ParentClass(n1, n2, n3, n4)
, m_cRefs(CountClass::Count)
{}
template <typename N1, typename N2, typename N3, typename N4>
RefCounter(N1 *n1, N2 *n2, N3 *n3, N4 &n4)
: ParentClass(n1, n2, n3, n4)
, m_cRefs(CountClass::Count)
{}
template <typename N1, typename N2, typename N3, typename N4>
RefCounter(N1 *n1, N2 *n2, N3 *n3, N4 *n4)
: ParentClass(n1, n2, n3, n4)
, m_cRefs(CountClass::Count)
{}

. . . etc. etc. up to permutations for 8 params!!!!!!

};


I think this ably demonstrates the usefulness and elegance of tailored Bolt-ins, and the horror of fully generic ones.

;)

Matthew Wilson

Posts: 145
Nickname: bigboy
Registered: Jun, 2004

Re: Bolt-Ins for Contract and Extension Classes Posted: Apr 27, 2005 10:31 PM
Reply to this message Reply
> > The naive solution then would
> > be:
> >
> > <pre>
> > class SimpleClass {
> > public:
> > SimpleClass()
> > #ifdef _DEBUG
> > : mbInit(false)
> > #endif
> > { }
> > Init() {
> > #ifdef _DEBUG
> > // do initialization
> > mbInit = true;
> > #endif
> > }
> > void DoSomething() {
> > assert(mbInit);
> > // do something
> > }
> > private:
> > #ifdef _DEBUG
> > bool mbInit;
> > #endif
> > }
> > </pre>
>
> Nit: use of _DEBUG is not advised, as it's not portable.
> Just as common an equivalent - on another platform ;) - is
> the absence of NDEBUG. It tend to discriminate an
> ACMELIB_DEBUG symbol taking into account compiler,
> platform and presence/absence of _DEBUG/NDEBUG, e.g.

[Forgot my example <g>]

from the recls library:


/* /////////////////////////////////////////////////////////////////////////////
* Debugging?
*/

/** \def _RECLS_DEBUG If defined, it indicates a debug build */

#if !defined(NDEBUG)
# if defined(RECLS_PLATFORM_IS_UNIX)
# define _RECLS_DEBUG
# elif defined(RECLS_PLATFORM_IS_WIN32) && \
( defined(DEBUG) || \
defined(_DEBUG))
# define _RECLS_DEBUG
# endif /* platform */
#endif /* debug */

Christopher Diggins

Posts: 1215
Nickname: cdiggins
Registered: Feb, 2004

Re: Bolt-Ins for Contract and Extension Classes Posted: Apr 27, 2005 11:20 PM
Reply to this message Reply
> > > So I would strongly favour implementing those methods
> > as
> > > non member functions.
> >
> > Why?
>
> > I don't see how using non-members is a superior
> approach
> > over extension classes. Nothing is saved by making the
> > function non-member functions, they are just scattered
> > throughout the namespace in a non-modular manner. It
> feels
> > like I am stepping back in time to old fashioned C ;-)
>
> I say upfront that I've not read this for over a year, but
> I recall being in complete agreement at each read since
> its publication in 2000:
> http://www.cuj.com/documents/s=8042/cuj0002meyers/meyers.ht
> m
>
> [You might take some meta-persuasion on that very
> thing, since Scott Meyers and I are wont to disagree about
> a great many things. ;)]
>
> <snipped the rest, as I'm not yet through your original
> post>

Nothing wrong about what Scott Meyers says ( despite some arm-waving due the fact that he outright fails to define encapsulation ) nonetheless I see no argument there that non-member functions are more encapuslated than extension classes (i.e. bolt-ins).

Christopher Diggins

Posts: 1215
Nickname: cdiggins
Registered: Feb, 2004

Re: Bolt-Ins for Contract and Extension Classes Posted: Apr 27, 2005 11:51 PM
Reply to this message Reply
> I dislike "debug vs release" language when talking about
> contract programming, and suggest it unwise of you to use
> it when writing about it. May I suggest that we talk about
> differentiating between builds with different aspects of
> contract programming set at different levels of activity?

That is more accurate, thanks for correcting me.

> Naturally the most coarse grained and widely used is
> simply between debug (all on) and release (all off) but,
> IMO at least, real world systems lie somewhere in between
> (or they should do, anyway! <g>).

I agree.

> Nit: use of _DEBUG is not advised, as it's not portable.
> Just as common an equivalent - on another platform ;) - is
> the absence of NDEBUG. It tend to discriminate an
> ACMELIB_DEBUG symbol taking into account compiler,
> platform and presence/absence of _DEBUG/NDEBUG, e.g.

Worthwhile nit.

> There's a mistake here, isn't there? Shouldn't it be
>
>

> #ifdef _DEBUG
> struct SimpleClass :
> Simple_contract<SimpleClass_impl>
> { };
> #else
>


Yes I just corrected the blog thank you.

> - which isn't a bolt-in, then - or am I misunderstanding?

No that isn't a bolt-in.

> Have I missed a step? Isn't
> Initializing_contract actually
> Simple_contract?

My mistake, sorry.

> Although I tend to favour the non-member approach, this
> last sentence makes a sound point, and is agreeable (to me
> anyway <g>).

Thanks.

> Yes! This is exactly Bolt-in-ness to a T. :-)

Good!

> Ok, now I get where you're coming from.
>
> I have a very prosaic response: all this effort in making
> a set of related classes as templates, rather than as
> plain classes, is only worth it if one can apply the
> bolt-in class templates to other components. Given the
> strong structural requirement constraints on the bolt-ee
> from the bolt-in - i.e. String_contract's bolt-ee needs to
> provide SetChar() and GetChar() - then one is writing a
> specific bolt-in in any case, so the exta effort in
> writing appropriate specific forwarding constructors is
> not prohibitive.

I consider these to be weak structural requirements. The cost is not prohibitive in the short term, but it is cumulative when applying bolt-in to bolt-in to bolt-in, and when creating compositional bolt-ins (bolt-ins which inherit from bolt-ins).

> Since semantic checking is done when template are
> instantiated, it is perfectly safe to define a 'better'
> Bolt-in than is required for all the bolt-ee types,
> because only those ones used will be checked for validity
> by the compiler. Yes, there's an issue of maintenance in
> making your bolt-in class keep up with the bolt-ee
> classes, but that's not different than doing a more
> conventional approach.

I don't like conventional approaches neither :-(

> Indeed, it's still likely to be a
> big saving, since you only need to change the bolt-in once
> to reflect a new facility irrespective of how many bolt-ee
> types evolve to express that facility.

Good point.

> An example of this controlled complexity is shown in
> STLSoft's sequence_container_veneer. Here are all
> its forwarding constructors, enabling it to support the
> construction of types bolt-ed from all STL-sequence
> containers:
>
[snip]
>
> I think this ably demonstrates the usefulness and elegance
> of tailored Bolt-ins, and the horror of fully generic
> ones.

And the horror of C++!

Martin Odersky

Posts: 84
Nickname: modersky
Registered: Sep, 2003

Re: Bolt-Ins for Contract and Extension Classes Posted: Apr 28, 2005 4:03 AM
Reply to this message Reply
I agree that bolt-ins are very useful, but they are not new. They have been around for about 15 years under the name of mixin classes. See the paper "Mixin-Based Inheritance" by Bracha and Cook at OOPSLA 90, Bracha's thesis and the later work by Cartwright on MixGen. I think it would be better to use the original term, then other programmers know immediately what you are talking about.

Christopher Diggins

Posts: 1215
Nickname: cdiggins
Registered: Feb, 2004

Re: Bolt-Ins for Contract and Extension Classes Posted: Apr 28, 2005 9:49 AM
Reply to this message Reply
> I agree that bolt-ins are very useful, but they are not
> new. They have been around for about 15 years under the
> name of mixin classes. See the paper "Mixin-Based
> Inheritance" by Bracha and Cook at OOPSLA 90, Bracha's
> thesis and the later work by Cartwright on MixGen. I think
> it would be better to use the original term, then other
> programmers know immediately what you are talking about.

This is very interesting. The Bracha and Cook paper, provides the following definition of mixins:

A mixin is an abstract subclass that may be used to specialize the behavior of a variety of parent classes. It often does this by defining new methods that perform some actions and then call the corresponding parent methods.
- Bracha and Cook - Mixin-Based Inheritance

Thank you very much for bringing this to our attention!

indranil banerjee

Posts: 38
Nickname: indranil
Registered: Nov, 2004

Re: Bolt-Ins for Contract and Extension Classes Posted: Apr 28, 2005 5:22 PM
Reply to this message Reply
> > So I would strongly favour implementing those methods
> as
> > non member functions.
>
> Why?

Because they (the String_ext methods) dont need to be members.

They only operate on the public String interface, they dont need access to the protected parts of String_Impl.
They dont override any of String_Impl's members, they are just extensions.
They dont have any state of their own. So dont need to be in a class at all.

Making those algorithms members of a class and making that class derived from String only adds dependencies and increases coupling.

> I don't see how using non-members is a superior approach
> over extension classes. Nothing is saved by making the
> function non-member functions, they are just scattered
> throughout the namespace in a non-modular manner. It feels
> like I am stepping back in time to old fashioned C ;-)

Nothing has to be 'scattered', thats a bit of a loaded term. They can be grouped together in sub namespaces if you like. And you could be stepping sideways into the parallel universe of functional programming :-P

> > There are a couple of other advantages to non members.
> If
> > someone has got a 'raw' String_Impl class or another
> class
> > statisfying the String interface, they cant use the
> > methods of String_ext without creating a new String_ext
> > fom the String_Impl. Doesnt this defeat the dynamic
> typing
> > goals that you have been working towards?
>
> Not really, I don't view dynamic and static typing as
> mutually exclusive goals.

I agree about dynamic and static typing. Thats why I was so interested in your 'Proposed Boost' Interfaces Library. And that is also why I've got a strong dislike of this String_ext class.

Say I've got a class, YetAnotherString, which I'm using in lots of places. Say YetAnotherString statisfies your String interface. But I dont have a rich library of string algorithms like Concat(). If Concat was a nonmember function, I could just do this:-

YetAnotherString foo(YetAnotherString fred, YetAnotherString bob) {
OOTL::Concat(fred, bob);
return fred;
}

And it would just work.

With the String_ext approach I have to do this:


YetAnotherString foo(YetAnotherString& fred, YetAnotherString& bob) {
typedef OOTL::String_ext<YetAnotherString> YourString;
YourString fred2(fred); //string is copied
YourString bob2(bob); //string is copied
fred2.Concat(bob2);
return (YetAnotherString)fred2;
}


In fact I just wouldnt use it. By making these member functions you've massively limited the utility of your library.

Moral of the story. Dont use inheritance unless you need to, and I've given a checklist of when you might need to at the top of my post.

Flat View: This topic has 16 replies on 2 pages [ 1  2 | » ]
Topic: Definite proDuctivity aDvantages Previous Topic   Next Topic Topic: Jumping from the top of The Parachutes


Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2014 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use - Advertise with Us