Topic: finally statement


Author: jpotter@falcon.lhup.edu (John E. Potter)
Date: 1996/09/30
Raw View
Hoshi Takanori (hoshi@sra.co.jp) wrote:
: In article <KANZE.96Sep11124753@slsvijt.lts.sel.alcatel.de>
:      kanze@lts.sel.alcatel.de (James Kanze US/ESC 60/3/141 #40763) writes:
: > IMHO, it is not as interesting as it first sounds.  Typically, in a
: > complex function, the cleanup actions will depend on how far you got
: > into the function before the finally clause was invoked.
: Agreed.
: > A more useful variant (IMHO) would be some sort of "implicit" object of
: > an unnamed class type.  The syntax might be:
: >         int*                  p = new int[ n ] ;
: >         cleanup { delete [] p ; }
[snip]
: In the meantime, I can write an ugly macro:
:         #define CLEANUP(TYPE, VAR, DTOR) \
:         class cleanup_##VAR##_class { \
:         public: \
:             cleanup_##VAR##_class(TYPE &_##VAR) : VAR(_##VAR) {} \
:             ~cleanup_##VAR##_class() DTOR \
:         private: \
:             TYPE &VAR; \
:         } cleanup_##VAR(VAR);
: and use it as:
:         int *p = new int[n];
:         CLEANUP(int *, p, { delete[] p; })

auto_ptr would take care of the single cases and this one is covered by
          vector<int> v(n);

: or:
:         void foo(T &obj)        // T has Lock() and Unlock().
:         {
:             obj.Lock();
:             CLEANUP(T, obj, { obj.Unlock(); })
:             // do the job on obj...
:         }

This one should be doable with a not so _ugly_ template

template <class T>
class Cleanup {
    public :
        typedef void (T::*MF)();
        Cleanup (T& t, MF f) : obj(t), mf(f) { }
        ~Cleanup () { (obj.*mf)(); }
    private :
        T& obj;
        MF mf;
    };

        void foo (Lockable &obj)        // T has Lock() and Unlock().
        {
            obj.Lock();
            Cleanup<Lockable> objClean(obj, Lockable::Unlock);
            // do the job on obj...
        }

Perhaps something along these lines with much more thought than I have
given will get into the library the next time around.

John
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: hoshi@sra.co.jp (Hoshi Takanori)
Date: 1996/09/26
Raw View
In article <KANZE.96Sep11124753@slsvijt.lts.sel.alcatel.de> kanze@lts.sel.alcatel.de (James Kanze US/ESC 60/3/141 #40763) writes:

> IMHO, it is not as interesting as it first sounds.  Typically, in a
> complex function, the cleanup actions will depend on how far you got
> into the function before the finally clause was invoked.

Agreed.

> A more useful variant (IMHO) would be some sort of "implicit" object of
> an unnamed class type.  The syntax might be:
>
>         int*                  p = new int[ n ] ;
>         cleanup { delete [] p ; }
>
> In this case, the keyword "cleanup" defines an unnamed class which
> contains (private) references to all locally visible variables, and a
> destructor with the code given.  It also defines a local unnamed
> instance of this class.  (Of course, the compiler would be free under
> the as if rule to not generate the references at all, but simply the
> direct access to the variables involved.)

Excellent idea.  It's far better than finally because:

1. you don't have to check if the operation is actually done.

2. as the cleanup code immediately follows the operation code,
   you don't forget to add or delete it when you add or delete
   an operation which needs some cleanup code.

I hope the standard to have it, but I think the committee don't
like to add such an extension.  GNU C++ may have it someday...

In the meantime, I can write an ugly macro:

        #define CLEANUP(TYPE, VAR, DTOR) \
        class cleanup_##VAR##_class { \
        public: \
            cleanup_##VAR##_class(TYPE &_##VAR) : VAR(_##VAR) {} \
            ~cleanup_##VAR##_class() DTOR \
        private: \
            TYPE &VAR; \
        } cleanup_##VAR(VAR);

and use it as:

        int *p = new int[n];
        CLEANUP(int *, p, { delete[] p; })

or:

        void foo(T &obj)        // T has Lock() and Unlock().
        {
            obj.Lock();
            CLEANUP(T, obj, { obj.Unlock(); })

            // do the job on obj...
        }

hoshi


[ 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: kanze@lts.sel.alcatel.de (James Kanze US/ESC 60/3/141 #40763)
Date: 1996/09/27
Raw View
In article <HOSHI.96Sep26125400@ext221.sra.co.jp> hoshi@sra.co.jp (Hoshi
Takanori) writes:

|> In article <KANZE.96Sep11124753@slsvijt.lts.sel.alcatel.de> kanze@lts.sel.alcatel.de (James Kanze US/ESC 60/3/141 #40763) writes:

|> > A more useful variant (IMHO) would be some sort of "implicit" object of
|> > an unnamed class type.  The syntax might be:
|> >
|> >         int*                  p = new int[ n ] ;
|> >         cleanup { delete [] p ; }
|> >
|> > In this case, the keyword "cleanup" defines an unnamed class which
|> > contains (private) references to all locally visible variables, and a
|> > destructor with the code given.  It also defines a local unnamed
|> > instance of this class.  (Of course, the compiler would be free under
|> > the as if rule to not generate the references at all, but simply the
|> > direct access to the variables involved.)

|> Excellent idea.  It's far better than finally because:

|> 1. you don't have to check if the operation is actually done.

|> 2. as the cleanup code immediately follows the operation code,
|>    you don't forget to add or delete it when you add or delete
|>    an operation which needs some cleanup code.

|> I hope the standard to have it, but I think the committee don't
|> like to add such an extension.  GNU C++ may have it someday...

I don't think that the committee would be a priori against it, as such.
However:

1. We want a standard this millenium.  If I understand things correctly,
the committee has made an explicit decision NOT to accept any more
extensions, a decision with which I very strongly concur.  Even when the
above was aired the first time in this forum, it was too late for
extensions in the basic language.  (I wish I had noted who actually
first mentioned the idea, so I could give him credit.)

2. The committee cannot really act on some vague discussion in this
forum.  They need some sort of formal proposal, with a careful analysis
of the impact of the change.  (An obvious impact of the above, for
example, is that it introduces a new keyword, which will break some
code.  There are probably others that we haven't considered.)

3. All ISO standards are reviewed on a regular basis.  Thus, we have
Fortran 66, Fortran 77, Fortran 90 ...  This will also be the case with
C++.  If someone is interested in doing the work of writing up a formal
proposal at that time, I'm sure that the committee would give it its
full consideration.  A proof of concept implementation in the meantime
(say using g++) would not hurt, either.
--
James Kanze         Tel.: (+33) 88 14 49 00        email: kanze@gabi-soft.fr
GABI Software, Sarl., 8 rue des Francs-Bourgeois, F-67000 Strasbourg, France
Conseils,    tudes et r   alisations en logiciel orient    objet --
                -- A la recherche d'une activit    dans une region francophone
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: "Bradd W. Szonye" <bradds@ix.netcom.com>
Date: 1996/09/23
Raw View
Philip Gruebele <philipg@bayarea.net> wrote in article
<01bba72a$89c673d0$7f40dbcd@philipg>...
> I am finally convinced that the finally statement really is not
necessary.
> Ironically, my compiler (Visual C++ 4.2) does not accept the above
> construct.  It complains "functions cannot be defined in local classes"
> when I place any function into a local class.  This must be a
compatibility
> bug with VC++, I imagine....

I think you're right there. Local classes are not allowed to have static
member data (or functions? I can't remember), but they're pretty useless if
they can't have member functions at all. The functions *do* need an inline,
in-the-class definition.

> I would think that you could take the above construct one step further
and
> do the following:
>
> void foo()
> {
>  char *array = new char[50];
>  struct cleanup {
>   ~cleanup {delete[] array};
>  } bar
> }
>
> This should work, right?  Of course my compiler won't accept it for
reason
> stated above.

Unfortunately, no. Local classes may only access static data: static
function variable, class statics, and globals. Allowing access to function
locals would require a frame pointer, which implies a fully
block-functional language like Pascal. C and C++ are not block-functional,
they only go halfway on that issue. Still, non-static inline local class
functions should be legal.

The main problem with local classes as finally is that they're a little
verbose, and they must go somewhere near the beginning of the block to be
useful. Not quite as pretty as "finally" but it's there already, and it's
not horribly inelegant.
--
Bradd W. Szonye
bradds@ix.netcom.com
http://www.geocities.com/SouthBeach/2447
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: Alan Griffiths <aGriffiths@ma.ccngroup.com>
Date: 1996/09/23
Raw View
In article: <01bba72a$89c673d0$7f40dbcd@philipg>  "Philip Gruebele" <philipg@bayarea.net> writes:
>
> >
> > void foo()
> > {
> >  // manage two streams and an array
> >  struct cleanup {

                cleanup() : c(0) {}   // ;)

> >   ~cleanup() {delete[] c;}
> >   ifstream a;
> >   ofstream b;
> >   char* c;
> >  } bar;
> >
> >  bar.a.open("foobar.txt");
> >  bar.b.open("report.txt");
> >  bar.c = new char[256];
> >
> >  // a, b closed automatically
> >  // c deleted automatically by destructor
> > }
> >
>
> I am finally convinced that the finally statement really is not necessary.
> Ironically, my compiler (Visual C++ 4.2) does not accept the above
> construct.  It complains "functions cannot be defined in local classes"
> when I place any function into a local class.  This must be a compatibility
> bug with VC++, I imagine....
>

The following works (even with MSVC4.x  - provided you don't use the MS STL
implementation from 4.2)

void foo()
{
    using namespace std;
    vector<char> array(50);

    ...
}

__
Alan Griffiths               | Also editor of: The ISDF Newsletter
Senior Systems Consultant,   | (An Association of C and C++ Users publication)
CCN Group Limited.           | (ISDF editor  : isdf@octopull.demon.co.uk)
(agriffiths@ma.ccngroup.com) | (For ACCU see : http://bach.cis.temple.edu/accu)
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: "Bradd W. Szonye" <bradds@ix.netcom.com>
Date: 1996/09/20
Raw View
Philip Gruebele <philipg@bayarea.net> wrote in article
<01bba505$57627370$7f40dbcd@philipg>...
> It is true that if I repeatedly want to create temporary file that
> automatically delete themselves, creating a little container class and
> reusing it in the future would be good.  However, my whole point is that
> the semantics of the problem could be such that you cannot simply wrap
the
> problem into a class.  Maybe under some circumstances I would want to
> delete the file, and under other circumstances I would not.

The finally block has the same problem. You still need to make the decision
somewhere. Finally blocks, according to their usually definition, get
executed no matter what, just as destructors do.

>  It may not
> make sense to filter out all the functionality of this temporary file
into
> another object, because it's semantics might really _belong_ into the
> function - and because it will never get used again.

The "typing" (as in keystrokes) overhead involved in writing a class and
the need for keeping the code local to the function are the best arguments
I've heard yet for finally blocks. However, if you really want the code in
the function, then put the class in the function--a local class
declaration.

void foo()
{
 // manage two streams and an array
 struct cleanup {
  ~cleanup() {delete[] c;}
  ifstream a;
  ofstream b;
  char* c;
 } bar;

 bar.a.open("foobar.txt");
 bar.b.open("report.txt");
 bar.c = new char[256];

 // a, b closed automatically
 // c deleted automatically by destructor
}

If you want to initialize the variables, give cleanup a constructor too.

You know, I've always wondered what the purpose of local classes was...
apparently it makes a *very* good finally{} substitute. Even the
amount-of-typing overhead is practically nothing compared to finally{}. The
major difference is that the finally-code is close to the end of the block
(where the cleanup happens) and the local-class code is close to the
initialization.

There's two ways of looking at it:
finally{} says, "when you reach this point, do this cleanup"
local-class says, "initialize this code, and whenever you need to, here's
how you release it."
--
Bradd W. Szonye
bradds@ix.netcom.com
http://www.geocities.com/SouthBeach/2447


[ 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: "Philip Gruebele" <philipg@bayarea.net>
Date: 1996/09/20
Raw View
>
> void foo()
> {
>  // manage two streams and an array
>  struct cleanup {
>   ~cleanup() {delete[] c;}
>   ifstream a;
>   ofstream b;
>   char* c;
>  } bar;
>
>  bar.a.open("foobar.txt");
>  bar.b.open("report.txt");
>  bar.c = new char[256];
>
>  // a, b closed automatically
>  // c deleted automatically by destructor
> }
>

I am finally convinced that the finally statement really is not necessary.
Ironically, my compiler (Visual C++ 4.2) does not accept the above
construct.  It complains "functions cannot be defined in local classes"
when I place any function into a local class.  This must be a compatibility
bug with VC++, I imagine....

I would think that you could take the above construct one step further and
do the following:

void foo()
{
 char *array = new char[50];

 struct cleanup {
  ~cleanup {delete[] array};
 } bar

 ....
}


This should work, right?  Of course my compiler won't accept it for reason
stated above.


Regards

Philip Gruebele




[ 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: Pete Becker <pbecker@oec.com>
Date: 1996/09/20
Raw View
Philip Gruebele wrote:
>
> >
> > void foo()
> > {
> >       // manage two streams and an array
> >       struct cleanup {
> >               ~cleanup() {delete[] c;}
> >               ifstream a;
> >               ofstream b;
> >               char* c;
> >       } bar;
> >
> >       bar.a.open("foobar.txt");
> >       bar.b.open("report.txt");
> >       bar.c = new char[256];
> >
> >       // a, b closed automatically
> >       // c deleted automatically by destructor
> > }
> >
>
> I am finally convinced that the finally statement really is not necessary.
> Ironically, my compiler (Visual C++ 4.2) does not accept the above
> construct.  It complains "functions cannot be defined in local classes"
> when I place any function into a local class.  This must be a compatibility
> bug with VC++, I imagine....
>
> I would think that you could take the above construct one step further and
> do the following:
>
> void foo()
> {
>         char    *array = new char[50];
>
>         struct cleanup {
>                 ~cleanup {delete[] array};
>         } bar
>
>         ....
> }
>
> This should work, right?  Of course my compiler won't accept it for reason
> stated above.

No, this shouldn't work. Local classes do not have any special access to
local variables.
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: Pete Becker <pbecker@oec.com>
Date: 1996/09/20
Raw View
Reposting article removed by rogue canceller.

Philip Gruebele wrote:
>
> >
> > void foo()
> > {
> >       // manage two streams and an array
> >       struct cleanup {
> >               ~cleanup() {delete[] c;}
> >               ifstream a;
> >               ofstream b;
> >               char* c;
> >       } bar;
> >
> >       bar.a.open("foobar.txt");
> >       bar.b.open("report.txt");
> >       bar.c = new char[256];
> >
> >       // a, b closed automatically
> >       // c deleted automatically by destructor
> > }
> >
>
> I am finally convinced that the finally statement really is not necessary.
> Ironically, my compiler (Visual C++ 4.2) does not accept the above
> construct.  It complains "functions cannot be defined in local classes"
> when I place any function into a local class.  This must be a compatibility
> bug with VC++, I imagine....
>
> I would think that you could take the above construct one step further and
> do the following:
>
> void foo()
> {
>         char    *array = new char[50];
>
>         struct cleanup {
>                 ~cleanup {delete[] array};
>         } bar
>
>         ....
> }
>
> This should work, right?  Of course my compiler won't accept it for reason
> stated above.

No, this shouldn't work. Local classes do not have any special access to
local variables.
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: kanze@lts.sel.alcatel.de (James Kanze US/ESC 60/3/141 #40763)
Date: 1996/09/16
Raw View
In article <51ar4n$5ot@tools.bbnplanet.com> Barry Margolin
<barmar@bbnplanet.com> writes:

|> In article <KANZE.96Sep11124753@slsvijt.lts.sel.alcatel.de>,
|> James Kanze US/ESC 60/3/141 #40763 <kanze@lts.sel.alcatel.de> wrote:
|> >IMHO, it is not as interesting as it first sounds.  Typically, in a
|> >complex function, the cleanup actions will depend on how far you got
|> >into the function before the finally clause was invoked.

|> There are two ways to deal with that:

|> 1) finally clauses should be associated with blocks, not functions.  Thus,
|> as you get further into a function you can nest blocks, e.g.

|> int foo(...) {
|>    ...
|>    {
|>       ...
|>       {
|>          ...
|>          finally { ... }
|>       }
|>       ...
|>       finally { ... }
|>    }
|>    ...
|>    finally { ... }
|> }

I presume that in this case, the finally will be invoked whenever
leaving the associated block.  What is the scope it sees?  (Presumably
members of the block, plus all enclosing blocks, but..  A block can
contain a definition (with perhaps an initialization); what happens if
the finally clause is executed before the definition.  (For a concrete
example of what I mean, see below.)

|> 2) Use a single finally clause for the function, but have the code in it
|> look at variables that contain the things that need to be cleaned up.
|> These variables would be initialized to 0, null pointers, etc., and
|> assigned as the function progresses.  The finally clause would look like:

|> finally {
|>   if (p) delete p;
|>   if (f != -1) close(f);
|>   ...
|> }

In this case, the tests aren't even necessary, since you can delete a
null pointer or close an inexistant file without problems.  (You will
get an error status from the close, but since you are ignoring it...)

Let's look at this in detail:

 void
 func( char const* filename )
 {
  T*              p = new T ;
  int             f = ::open( filename , O_RDONLY ) ;
                                  //  Posix headers supposed.
  //  ...
  finally
  {
   delete p ;
   close( f ) ;
  }
 }

I presume that this is what you were talking about.

Now suppose that the constructor for T throws an exception, and we
execute the finally.  At this point:

1. p is not yet initialized, and may contain random data.  (I don't
think this is a good idea.)

2. f doesn't even exist yet, so what on earth is the parameter to close.

I know that you can avoid this by first defining the variables to be
initialized with inoculous values (null, -1) which cannot throw an
exception.  I wouldn't like to have to find the standardese which makes
the above program undefined (because it accesses f before the variable
is declared)!

Note that neither Modula-3 nor Java have this problem (I think).  In
both cases, you have to declare all variables at the top of the block
(not sure about Java), and in both cases, the only types which can throw
an exception are `objects', and the declaration of an object actually
only defines a "reference" which is initialized to null.  To get an
actual object, you have to do new, later.  (Not sure about Modula-3,
this time.)

The advantages (that I see, at least) in the implicit class are:

1. It gets the full scope from the point of declaration, which is at the
point where the cleanup becomes necessary, and

2. Purely subjunctive, but it seems more C++'ish.

All of this is just my humble opinion of course, and should not in any
way be interpreted as a criticism of Java or Modula-3, but different
languages have different philosophies, which means that a solution for
one language may not be appropriate for the other.
--
James Kanze         Tel.: (+33) 88 14 49 00        email: kanze@gabi-soft.fr
GABI Software, Sarl., 8 rue des Francs-Bourgeois, F-67000 Strasbourg, France
Conseils,    tudes et r   alisations en logiciel orient    objet --
                -- A la recherche d'une activit    dans une region francophone



[ 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: Bjorn Fahller <ebcbear@ebc.ericsson.se>
Date: 1996/09/16
Raw View
Philip Gruebele wrote:
>
> The problem is that C++ is still also a procedural language.  There are
> many things which cannot be wrapped into an object and forgotten.  Let's
> say we have a function which creates a temporary file for storage.  This
> ile is wrtten to using some stream object.  At the end of the function we
> want to delete the file.  The stream objects, however, do not automatically
> delete their files upon destruction.  Sure, you could subclass the stream
> object to make a "temporary stream" object which does this, but it involved
> more overhead and complexity thatn needed.   After all a main goal of OOD
> is to minimize complexity.  IMHO it would just be clearer and better and
> clearer to have a finally statement.

Although you're right, the problem can be solved without too much
work. Concider the following construct, using a simple one-time
"remover" class:

class remover
{
public:
  remover(fstream& fs) : f(fs) {};
  ~remover(void) {unlink(...);};
private:
  fstream& f;
};

A X::y(B)
{
  fstream tempfile(...);
  remover finally(tempfile);

  // your working code that might throw exceptions here!!
}

The destructor of the "remover" class is the finally statement.
Overhead for this (run-time overhead, that is,) should lie between
minimal and none.
   _
/Bjorn.
--
Bjorn Fahller                  Tel: +46 8 4220898 /
NA/EBC/FNM/T                   -------------------
Ericsson Business Networks AB /     "Don't you try to outweird me"
S-131 89 Stockholm/SWEDEN    /            -- Zaphod Beeblebrox


[ 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: stew@datalytics.com (Rob Stewart)
Date: 1996/09/17
Raw View
"Philip Gruebele" <philipg@bayarea.net> wrote:
>The problem is that C++ is still also a procedural language.  There are
>many things which cannot be wrapped into an object and forgotten.  Let's
>say we have a function which creates a temporary file for storage.  This
>ile is wrtten to using some stream object.  At the end of the function we
>want to delete the file.  The stream objects, however, do not automatically
>delete their files upon destruction.  Sure, you could subclass the stream
>object to make a "temporary stream" object which does this, but it involved
>more overhead and complexity thatn needed.   After all a main goal of OOD
>is to minimize complexity.  IMHO it would just be clearer and better and
>clearer to have a finally statement.

But in the true sense of OO, you'd want to create such a derived
"temporary stream" class because you'll want such objects now and
in the future. Thus, you'd capture--once--the new behavior which
you could invoke automatically by creating an object of that new
type. That is the way to reduce complexity--admittedly at the
cost of increased complexity when you first realize your need
for the new class.

The value of OO is less in the current project you're working on
than in the reuse and extension of previous work and the
projection of current work to the future. In other words, don't
just focus on the current function or problem. Consider what you
will need in the future too.

Robert Stewart  | My opinions are usually my own.
Datalytics, Inc. | stew@datalytics.com
   | http://www.datalytics.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: Bart Samwel <bsamwel@WI.leidenuniv.nl>
Date: 1996/09/17
Raw View
Barry Margolin wrote:

> int foo(...) {
>    ...
>    {
>       ...
>       {
>          ...
>          finally { ... }
>       }
>       ...
>       finally { ... }
>    }
>    ...
>    finally { ... }
> }

Then I'd prefer a construct where the finally keyword is not inside the
block, like a catch clause:

int foo (...) {
   {
     {
     } finally {
     }
   } finally {
   }
} finally {
}

This might introduce a problem because anything outside the block
hasn't got access to the block's local variables. This could require
a scope kludge making the finally clause part of the block scope.

On the other hand, the syntax you suggested also introduces some
problems. The syntax implies that the finally keyword can be placed
anywhere in the block and that it can be used more than once in every
block. (In the syntax I suggested, it could also be used more than once,
because the block following 'finally' can of course also have a finally
clause, and this clause would also include the complete scope of the
primary finally clause, including the scope of the original block.)
IMO It wouldn't be very C++-like only to allow only one finally clause
but still keep it inside the block (remember C's variable declaration
placement restrictions?). And a compiler wouldn't be able to check
whether the finally clause was _really_ at the end of the block until
it had processed the whole finally clause.

A solution would be to use your syntax and allow multiple finally
clauses anywhere inside the block. In that case, you would only
execute the finally clauses the block has 'executed' already when it
exits (probably in reverse order). But I think this would cause a large
amount of overhead, and probably an unacceptable amount.

   Bart
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: "Philip Gruebele" <philipg@bayarea.net>
Date: 1996/09/17
Raw View
Bjorn Fahller <ebcbear@ebc.ericsson.se> wrote in article
<323CFB41.4795@ebc.ericsson.se>...
> Although you're right, the problem can be solved without too much
> work. Concider the following construct, using a simple one-time
> "remover" class:
>
> class remover
> .....

This is a simple way of solving this specific problem.  However, it is not
practical to do this for every special case: memory allocation, file
deletion, signalling, messaging etc.   Also, you are hiding the part of
your code which should actually be visible in your function. In most cases
it is better to hide functionality in classes.  Function cleanup code,
however, is better left as part of the visible code because it is part of
the samantics of the function itself...  Now the process of checking and
making sure that all "volatile" items get cleanup up becomes error prone.

Regards

Philip Gruebele
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: Chelly Green <chelly@eden.com>
Date: 1996/09/17
Raw View
Philip Gruebele wrote:
>
> The problem is that C++ is still also a procedural language.

The language supports multiple decomposition techniques, such as
procedural and object-oriented.

> There are
> many things which cannot be wrapped into an object and forgotten. Let's
> say we have a function which creates a temporary file for storage.

OK.

    class TemporaryFile // ...
        TemporaryFile() { // create file

> This file is wrtten to using some stream object.  At the end of the function
> we want to delete the file.

Done.

        ~TemporaryFile() { // delete file

> The stream objects, however, do not automatically
> delete their files upon destruction.

The temporary file class does.

> Sure, you could subclass the stream
> object to make a "temporary stream" object which does this, but it involved
> more overhead and complexity thatn needed.

It gets the job done, is reusable, cleans up things properly, and can be
made much more general than files.

    void f()
    {
        TempFile temp_file( "foo", file::create );

        // ...
    }

> After all a main goal of OOD is to minimize complexity.

So wrap this complexity in a class that has a more restricted, simpler
interface, as I did for TempFile.

> IMHO it would just be clearer and better and
> clearer to have a finally statement.

OK, if you really want

    void f()
    {
        File temp_file( "foo", File::create );
        bool exception = true;
        try
        {
            // ...

            exception = false;
            throw 0;
        }
        catch ( ... ) // finally { ... }
        {
            temp_file.close();
            filesystem_delete_from_disk( temp_file.pathname() );
            if ( exception ) throw;
        }
    }

>
> Regards
>
> Philip Gruebele
> ---
>

--
Chelly Green | mailto:chelly@eden.com | http://www.eden.com/~chelly
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: "Philip Gruebele" <philipg@bayarea.net>
Date: 1996/09/18
Raw View
> But in the true sense of OO, you'd want to create such a derived
> "temporary stream" class because you'll want such objects now and
> in the future. Thus, you'd capture--once--the new behavior which
> you could invoke automatically by creating an object of that new
> type. That is the way to reduce complexity--admittedly at the
> cost of increased complexity when you first realize your need
> for the new class.
>
> The value of OO is less in the current project you're working on
> than in the reuse and extension of previous work and the
> projection of current work to the future. In other words, don't
> just focus on the current function or problem. Consider what you
> will need in the future too.
>
> Robert Stewart  | My opinions are usually my own.
> Datalytics, Inc. | stew@datalytics.com
>    | http://www.datalytics.com

It is true that if I repeatedly want to create temporary file that
automatically delete themselves, creating a little container class and
reusing it in the future would be good.  However, my whole point is that
the semantics of the problem could be such that you cannot simply wrap the
problem into a class.  Maybe under some circumstances I would want to
delete the file, and under other circumstances I would not.   It may not
make sense to filter out all the functionality of this temporary file into
another object, because it's semantics might really _belong_ into the
function - and because it will never get used again.  You cannot make every
line of code you write reusable.  There has to be some glue code which
brings everything together and which cannot be practically reused...

I guess temporary files were a bad example.


Regards

Philip Gruebele

:-)
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: "Philip Gruebele" <philipg@bayarea.net>
Date: 1996/09/09
Raw View
Is someting of the following nature being added to the c++ standard?

A "finally" statement which contains a block of code which always gets
executed when a function returns - regardless of wether the function exists
via "return" or "throw".  This would look something like this:

func()
{
 int* x = NULL;

 try
 {
  x = new ....;
  ....
  throw
 }
 catch (int)
 {
 }
 catch (...)
 {
  throw;
 }

 return;

 finally  // this code gets executed when control leaves this function
(through return or throw).
 {
  if (x)
   delete x;
 }
}

The problem is that without a finally statement,  if one wants to rethrow
an exception in tge "catch" block, one has to duplicate the cleanup code
from the end of the function in the catch block.  The lack of this type of
construct has been my main frustration with c++ exfeption handling.
Cleaning up allocations can be error prone and redundant...


Philip Gruebele
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: clamage@taumet.eng.sun.com (Steve Clamage)
Date: 1996/09/09
Raw View
In article 7f40dbcd@philipg, "Philip Gruebele" <philipg@bayarea.net> writes:
>Is someting of the following nature being added to the c++ standard?
>
>A "finally" statement which contains a block of code which always gets
>executed when a function returns - regardless of wether the function exists
>via "return" or "throw".

Java (for example) has such a facility, and it was informally proposed
as an addition to C++. Partly because we are trying to wrap up the
standard so it can be voted on, and partly because the "finally"
clause doesn't add much power to C++, the subject was
not pursued. It might be considered for the next round of
standardization. (We have a little list ... )

---
Steve Clamage, stephen.clamage@eng.sun.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: Barry Margolin <barmar@bbnplanet.com>
Date: 1996/09/10
Raw View
In article <01bb9d52$85953d50$7f40dbcd@philipg>,
Philip Gruebele <philipg@bayarea.net> wrote:
>A "finally" statement which contains a block of code which always gets
>executed when a function returns - regardless of wether the function exists
>via "return" or "throw".  This would look something like this:

This is normally implemented by defining a class whose destructor contains
the "finally" code.  The function has an automatic variable of this type,
and when the function exits its destructur will be run.
--
Barry Margolin
BBN Planet, Cambridge, MA
barmar@bbnplanet.com -  Phone (617) 873-3126 - Fax (617) 873-6351
(BBN customers, please call (800) 632-7638 option 1 for support)


[ 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: dtaylor@pifus.com (David Taylor)
Date: 1996/09/11
Raw View
>The problem is that without a finally statement,  if one wants to rethrow
>an exception in tge "catch" block, one has to duplicate the cleanup code
>from the end of the function in the catch block.  The lack of this type of
>construct has been my main frustration with c++ exfeption handling.
>Cleaning up allocations can be error prone and redundant...
>

This is why you should always make sure all resources are wrapped in local
variables, which will automatically release the resource when they go out of
scope.  Then simply leave it to the compiler to clean up the mess.  For
memory and dynamic objects, use auto_ptr<> or something similar.  For
resources such as files, use a file class which automatically cleans up
itself.

Dave
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: kanze@lts.sel.alcatel.de (James Kanze US/ESC 60/3/141 #40763)
Date: 1996/09/11
Raw View
In article <199609092044.NAA05626@taumet.eng.sun.com>
clamage@taumet.eng.sun.com (Steve Clamage) writes:

|> In article 7f40dbcd@philipg, "Philip Gruebele" <philipg@bayarea.net> writes:
|> >Is someting of the following nature being added to the c++ standard?
|> >
|> >A "finally" statement which contains a block of code which always gets
|> >executed when a function returns - regardless of wether the function exists
|> >via "return" or "throw".

|> Java (for example) has such a facility, and it was informally proposed
|> as an addition to C++. Partly because we are trying to wrap up the
|> standard so it can be voted on, and partly because the "finally"
|> clause doesn't add much power to C++, the subject was
|> not pursued. It might be considered for the next round of
|> standardization. (We have a little list ... )

Modula-3 also has such a facility.

IMHO, it is not as interesting as it first sounds.  Typically, in a
complex function, the cleanup actions will depend on how far you got
into the function before the finally clause was invoked.

A more useful variant (IMHO) would be some sort of "implicit" object of
an unnamed class type.  The syntax might be:

 int*   p = new int[ n ] ;
 cleanup { delete [] p ; }

In this case, the keyword "cleanup" defines an unnamed class which
contains (private) references to all locally visible variables, and a
destructor with the code given.  It also defines a local unnamed
instance of this class.  (Of course, the compiler would be free under
the as if rule to not generate the references at all, but simply the
direct access to the variables involved.)

This is, of course, just a convenience feature; even today, I can always
use a local class:

 class PtrToIntArray
 {
 public :
  PtrToIntArray( int n ) : myPtr( new int[ n ] ) {}
  ~PtrToIntArray() { delete [] myPtr }
  operator int*() { return myPtr ; }
 private :
  int*            myPtr ;
 }    p( n ) ;

I find the addition syntax, proposed above, nice, and preferrable to the
explicit local class.  But the advantages are certainly not great enough
to even think about delaying the standard for them.
--
James Kanze         Tel.: (+33) 88 14 49 00        email: kanze@gabi-soft.fr
GABI Software, Sarl., 8 rue des Francs-Bourgeois, F-67000 Strasbourg, France
Conseils,    tudes et r   alisations en logiciel orient    objet --
                -- A la recherche d'une activit    dans une region francophone
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: jodle@bix.com (jodle)
Date: 1996/09/12
Raw View
Steve Clamage (clamage@taumet.eng.sun.com) wrote:
: >A "finally" statement which contains a block of code which always gets
: >executed when a function returns - regardless of wether the function exists
: >via "return" or "throw".

: Java (for example) has such a facility, and it was informally proposed
: as an addition to C++. Partly because we are trying to wrap up the
: standard so it can be voted on, and partly because the "finally"
: clause doesn't add much power to C++, the subject was
: not pursued. It might be considered for the next round of
: standardization. (We have a little list ... )

Also, the amount of code that must be duplicated can be minimized by
application of functional decomposition.  Since the states of any variables
whose value depends on the code in the block of code in question are
themselves in question, the number of parameters needed to pass to such a
function will tend to be small.


[ 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: Barry Margolin <barmar@bbnplanet.com>
Date: 1996/09/13
Raw View
In article <KANZE.96Sep11124753@slsvijt.lts.sel.alcatel.de>,
James Kanze US/ESC 60/3/141 #40763 <kanze@lts.sel.alcatel.de> wrote:
>IMHO, it is not as interesting as it first sounds.  Typically, in a
>complex function, the cleanup actions will depend on how far you got
>into the function before the finally clause was invoked.

There are two ways to deal with that:

1) finally clauses should be associated with blocks, not functions.  Thus,
as you get further into a function you can nest blocks, e.g.

int foo(...) {
   ...
   {
      ...
      {
         ...
         finally { ... }
      }
      ...
      finally { ... }
   }
   ...
   finally { ... }
}

2) Use a single finally clause for the function, but have the code in it
look at variables that contain the things that need to be cleaned up.
These variables would be initialized to 0, null pointers, etc., and
assigned as the function progresses.  The finally clause would look like:

finally {
  if (p) delete p;
  if (f != -1) close(f);
  ...
}
--
Barry Margolin
BBN Planet, Cambridge, MA
barmar@bbnplanet.com -  Phone (617) 873-3126 - Fax (617) 873-6351
(BBN customers, please call (800) 632-7638 option 1 for support)


[ 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: "Philip Gruebele" <philipg@bayarea.net>
Date: 1996/09/14
Raw View
The problem is that C++ is still also a procedural language.  There are
many things which cannot be wrapped into an object and forgotten.  Let's
say we have a function which creates a temporary file for storage.  This
ile is wrtten to using some stream object.  At the end of the function we
want to delete the file.  The stream objects, however, do not automatically
delete their files upon destruction.  Sure, you could subclass the stream
object to make a "temporary stream" object which does this, but it involved
more overhead and complexity thatn needed.   After all a main goal of OOD
is to minimize complexity.  IMHO it would just be clearer and better and
clearer to have a finally statement.

Regards

Philip Gruebele
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]