Topic: A RFC for YAAC++ ( yet another addition to C++ )
Author: partha@hollywood.cinenet.net (N.Parthasarathy)
Date: 26 Dec 1994 22:21:21 -0800 Raw View
A RFC for YAAC++ ( yet another addition to C++ )
Introduction :
The note is organised as follows
Section I : Tutorial ( actually semi - tutorial )
1. Concept of paired functions.
2. The concept of dynamically resolved global scope.
3. User defined scopes
Section II :
A slightly larger example using most of the above features.
Section III :
Implementation
Section IV :
Miscellaneous
Section I
---------
1. Concept of paired functions.
Paired functions are two functions that are related to each other
in the sense of that whenever one is called the other is also
called subsequently. The function that is called first is named
as a CON function and that which is called later is shall be known
as a DES function. Further paired functions have the following
property. If f1 is paired to f1' and f2 is paired to f2` then
all calls of the form
f1();
f2();
is followed by
f2'();
f1'();
That is all f1, f2, .. calls are like a push and f2', f1 ..
correspond to a pop. The order cannot be changed.
The constructor and destructor are both paired functions with
respect to each other. note that the mapping between a CON function
and a DES function is many to one.
eg.
free, malloc - can be paired
fopen, fclose - can be paired
For paired functions we can implement a mechanism similar to
automatic destruction of objects.
we introduce a new keyword paired.
Syntax :
paired template is a definition of the DES code to generate from the CON
call.
paired template
[ var_name = ] func_proto_CON( .. ) : func_proto_DES ( .. );
eg.
paired template fp = fopen ( n, a ) : fclose ( fp );
A paired call is an actual invocation of the template.
paired var_name = func_call_CON( ... );
eg.
int funcion( char *name1, char *name2 )
{
FILE *fp1, *fp2;
paired fp1 = fopen ( name1, "rt" );
paired fp1 = fopen ( name1, "rt" );
if ( fp1 != 0 && fp2 != 0 )
{
do_something_here
}
}
The compiler translates this to
int funcion( char *name1, char *name2 )
{
FILE *fp1, *fp2;
fp1 = fopen ( name1, "rt" );
FILE *ftmp1 = fp1;
fp1 = fopen ( name1, "rt" );
FILE *ftmp2 = fp2;
if ( fp1 != 0 && fp2 != 0 )
{
do_something_here
}
fclose ( ftmp2 );
fclose ( ftmp1 );
}
just like it would generate a destructor.
This is a general mechanism which can be used for any paired calls
for eg. database Connect and Disconnect.
Also new and delete are implicitly defined to be a paired function set.
this means that objects constructed using new can be freed
automatically.
eg.
void function1 ( int n1, int n2 )
{
char *cp1;
char *cp2;
paired cp1 = new [ n1 ];
paired cp2 = new [ n2 ];
if ( cp1 != 0 && cp2 != 0 )
{
do_something
}
}
is generated as
void function1 ( int n1, int n2 )
{
char *cp1;
char *cp2;
cp1 = new [ n1 ];
char *tmpcp1 = cp1;
cp2 = new [ n2 ];
char *tmpcp2 = cp2;
if ( cp1 != 0 && cp2 != 0 )
{
do_something
}
delete [] tmpcp2;
delete [] tmpcp1;
}
2. The concept of dynamically resolved global scope.
scope is used to determine automatically when to apply a desctructor
for objects. This is very simple for global objects and local objects
which are allocated on the stack. But it is not always possible to
allocate global objects for a variety of reasons. These are usually
defined as pointers and new. Now the compiler cannot determine when
the scope ends so that it can call a corresponding delete.
Thus there are two concepts of scope - one static which can be determined
by looking at the source and one dynamic that which might vary from one
execution to another.
There are cases where even if the global scope is not dynamic it cannot be
defined as static definitios of the object itself. Usually a pointer is
defined and later the object is new'ed. This can happen when the objects
to be constructed need parameters that cannot be readily expressed at
compile time. It is not possible for the compiler to determine when to
delete the new'ed object.
The following overloaded use of paired can help here
Syntax :
paired static var_name = paired_func_CON ( ... );
delete static [ size_t ];
Helper function.
size_t get_static_subscope ( void );
Example :
char *cp1;
char *cp2;
int init ( int n1, int n2 )
{
paired cp1 = new [ n1 ];
paired cp2 = new [ n1 ];
return ( cp1 != 0 && cp2 != 0 );
}
int main ( )
{
size_t my_scope = get_static_subscope ( );
if ( init ( 5, 10 ) )
{
do_processing;
}
delete static my_scope;
}
using plain
delete static;
would destroy all global paired objects.
the compiler generates an implicit
delete static just after exiting main.
so actually the call is redundant - this is similar to the way
globally declared objects work.
Deletes only objects constructed after sub_scope - this mechanism
is somewhat like that of the variables on a stack being destructed
when the function call returns.
Another usage is for correct construction and destruction of
standard librarie like iostream. This uses the mechanism
similar to having an accessory class a static copy of which
is declared in the header file.
int ___iostream_builder ( void );
static int __iostream_dummy = ___iostream_builder ( );
In the definition file :
static iostream_internals *ii;
int ___iostream_builder ( void )
{
if ( ii == 0 )
paired static ii = new iostream_internals;
return 0;
}
note that the class gets automatically destroyed at the "right" time.
3. User defined scopes
Though dynamic scope is suitable for some class of applications
it is useful to have the ability to arbitrarily define scopes.
Syntax :
size_t paired [ size_t ( numeric_expression ) ]
paired size_t var_name = func_call_CON( ... );
delete size_t [ size_t ];
size_t get_subscope ( size_t scope );
eg.
size_t my_scope = paired 100;
What this means is return a handle to a user defined scope. this scope
should be capable of holding 100 obejcts right from the beginning.
paired my_scope fp = fopen ( name, "rt" );
The second size_t in delete is used optionally a subscope for the
user defined scope.
Summary of Syntax :
1. Concept of paired functions.
paired template
[ var_name = ] func_proto_CON( .. ) : func_proto_DES ( .. );
paired var_name = func_call_CON( ... );
2. The concept of dynamically resolved global scope.
paired static var_name = paired_func_CON ( ... );
delete static [ size_t ];
size_t get_static_subscope ( void );
3. User defined scopes
size_t paired [ size_t ( numeric_expression ) ]
paired size_t var_name = func_call_CON( ... );
delete size_t [ size_t ];
size_t get_subscope ( size_t scope );
Section III :
Example :
#include <stdio.h>
paired template fp = fopen ( n, a ) : fclose ( fp );
const buf_size = 512;
char *buf;
FILE *fpsrc,
FILE *fpdst;
int init ( int scope, char *src, char *dst )
{
paired scope buf = new ( buf_size );
paired scope fpsrc = fopen ( src, "rb" );
paired scope fpdst = fopen ( dst, "wb" );
return !( cp1 != 0 && cp2 != 0 && fp1 != 0 && fp2 != 0 );
}
int copy_file ( char *src, char *dst )
{
int scope = paired 3;
int rc = 0;
if ( rc = init ( scope, src, dst ) )
{
copy file over
}
delete scope;
return rc;
}
Section IV :
There exists no implementation. If anybody interested in trying out an
implementation please send me mail. I would be more than happy to share
my thoughts. It is possible to kludge the just the concept
of dynamic scopes in a very very implementation dependent manner.
However for implementing all of these things we need to write atleast
a preprocessor.
The implementation i have in mind seems to satisfy the following
condition.
Users who don't use this mechanism don't pay for it in execution
time at all. Unless there is a special compiler flag to turn it
on ( or off ) there will be a small penalty ( constant size given
an implentations ) in space.
My email address is
partha@cinenet.com
Author: milor001@maroon.tc.umn.edu (R A Milowski)
Date: Tue, 27 Dec 1994 15:40:32 GMT Raw View
In article <3dobp1$sfv@hollywood.cinenet.net>,
N.Parthasarathy <partha@hollywood.cinenet.net> wrote:
>
>A RFC for YAAC++ ( yet another addition to C++ )
>Introduction :
>
>The note is organised as follows
> Section I : Tutorial ( actually semi - tutorial )
> 1. Concept of paired functions.
Well, I don't quite see why this is *needed*. Obviously, it can be done it
plan C (not event using C++). So, my question is, what do you gain by using
this?
I the case of an application that uses, say, file processing or database access,
the "guts" inside of the paired function might change greatly. It would seem
to me there would be more problems of a database connection being closed
when you don't want it then the databases connection being forgotten to be
closed. Thus, it might cause more problems.
> 2. The concept of dynamically resolved global scope.
I don't like globals... and so I don't really like these suggestions (no
offense intended). I you need globals, you might look to see if you can
re-design your application/library/whatever.
> 3. User defined scopes
...same as above...
So, in general, my question is more of a task: Give me a *really good*
example where this would solve more problems than it creates.
R. Alexander Milowski
Microcom Inc. (612) 825 - 4132 "An SGML Solutions Company"
Author: tob@world.std.com (Tom O Breton)
Date: Tue, 27 Dec 1994 20:32:17 GMT Raw View
partha@hollywood.cinenet.net (N.Parthasarathy) writes: [About "paired
functions"]
Pardon me, but that is redundant to CTORs and DTORs, and less versatile.
Usually when you want paired resource acquisition/ release, you use a
CTOR/DTOR pair.
IMO the only thing this has over CTOR/DTOR is that you don't need the
extra source code (no runtime cost) to put FILE* (or whatever) into a
class and then access functions to bring it back out of the class. And
IMO this has nothing to do with any weakness in the CTOR/DTOR area, but
rather it's because C++ disallows certain inheritance tactics like
inheriting from pointers and from built-in types(*).
I don't understand what you're trying to do for global objects -- their
scope doesn't need modification. There is a well-known problem with not
controlling the order of construction of globals, but I don't think
that's what you were talking about.
As for user-defined scopes, they seem redundant to '{' and '}'.
I do like your acronym YAAC++, though. }:)
Tom
(*) So one must write a lot of source to fake it. No runtime cost
(assuming the access functions are inline and the use of pointers/
references doesn't confuse the optimizer), but programmer time and
compile time cost. Templates can help, to a point.
--
tob@world.std.com
TomBreton@delphi.com: Author of The Burning Tower
Author: beman@dawes.win.net (Beman Dawes)
Date: Tue, 27 Dec 1994 21:19:46 GMT Raw View
In article <3dobp1$sfv@hollywood.cinenet.net>, N.Parthasarathy (partha@hollywood.cinenet.net) writes:
>
>A RFC for YAAC++ ( yet another addition to C++ )
>...
>1. Concept of paired functions.
> Paired functions are two functions that are related to each other
> in the sense of that whenever one is called the other is also
> called subsequently...
If I understand the thrust of your proposal, can't much the same
effect can be achieved by using an "grim reaper" class?
For example, here is a portion of Greg Colvin's
proposal ANSI X3J16/94-0202R1, ISO WG21/N0589R1 currently being
considered by the committee:
----------------------------------------------------------------
The auto_ptr template provides a semantics of strict ownership. An
auto_ptr owns the object it holds a pointer to, and deletes that object
when it itself is destroyed. An object may be safely pointed to by only
one auto_ptr, so copying an auto_ptr copies the pointer and transfers
ownership to the destination.
Interface
template<class X> class auto_ptr {
X* px; // exposition only
public:
explicit auto_ptr(X* p=0);
template<class U> auto_ptr(const auto_ptr<U>& r);
~auto_ptr();
template<class U> auto_ptr& operator=(const auto_ptr<U>& r);
X& operator*() const;
X* operator->() const;
X* get() const;
X* release();
void reset(X* p=0);
};
Semantics
An auto_ptr<X> object holds a pointer to an object of class X, presented
here as X* px. In the following table: p and px are pointers to an
object of class X or a class derived from X for which delete(X*) is
defined and accessible, or else null; d is an auto_ptr<D> where D is X
or a class derived from X; a is an auto_ptr<X>; and m is a member of X.
Expression Value Effect
---------------- -------------- ---------------------
auto_ptr<X>a(p) a.px = p
auto_ptr<X>a(d) a.px = d.release()
a.~auto_ptr<X>() delete a.px
a = d reference to a a.reset(d.release())
*a *a.px
a->m a.px->m
a.get() a.px
a.release() a.px a.px = 0
a.reset(p) delete a.px, a.px = p
-- Beman (beman@dawes.win.net)