Topic: Intelligent C++ Macros


Author: john1257@gmi.edu (Odie Redbeard)
Date: 14 Jul 1994 14:34:49 GMT
Raw View
Ben Jones (jones@cais.cais.com) wrote:
: One thing that is really lacking in C++ is an intelligent macro facility.

: To illustrate why one would be useful, consider the following problem:

: You have a class hierarchy which you would like to make persistent.  In
: order to do so, you need to provide the streaming code needed to save and
: restore the members of each class and the bases for each class.  Here is
: a trivial one:

--- Demonstration clipped

: Not only that, but serialization is only one of several things you
: might want to do with these classes.  You might want to create dialog
: boxes to access the data in the object.  You might want to use
: multiple inheritance, which MFC 2.0 does not support (OWL 2.0 supports
: multiple inheritance in its persistent streams library but the
: required support code is even uglier).

: In any case, the more support code you have to write, the more you
: have to modify every time you add a member to a class.  It gets even
: worse if you have shared pointers or arrays to worry about.

: Wouldn't it be nice if you (the user of a class library) could just
: say simply:

:     class X: public CObject
:     {
:       public:
:         int a,b,c;
:     };

:     class Y: public X
:     {
:       public:
:         int d;
:     };

: and be done with it?

: You can't do it in C++.  There is no way to encapsulate the formula
: you use by hand to generate the support code for all the different
: things you want to do with members of classes.

Well... unless I'm mistaken you CAN do somesuch, or at least close, using a
feature from the proposed ANSI standard called templates.  They make doing
the same thing with many classes easy.  For instance, Borland implements its
container class library (BIDS) using templates.  What this means is that
to create a dynamically allocated, automatically sorted array of, say, class
Foo, all you use the statement (I forget the exact template names... but
this should be close)
 template TSArrayVector<Foo> thisIsMyArray
and all the operators are implented for Foo.  Of course Foo has to support a
few things (like having == and < overloaded) but beyond that it is simple.

I believe that templates, as I understand them, could be used to implement
what you are suggesting.

By the way, apparently Microsoft doesn't think templates are useful because
they still haven't implemented them, while Borland has had templates since
(I believe) 2.0.

I will however, still check out ARC++, to see what it does.

I hope this has been helpful

Michael
--
   /| /||\     | o-<  A life with love  @-}---  >-o
  / |/ || \    | o-<  Will have some thorns     >-o Michael D Johnson
 /  |  || /    | o-<  But a life without love   >-o john1257@nova.gmi.edu
/   |  ||/ \__/  o-<  Will have no roses  -???  >-o




Author: jones@cais.cais.com (Ben Jones)
Date: 18 Jul 1994 12:00:27 GMT
Raw View
Odie Redbeard (john1257@gmi.edu) wrote:
: Ben Jones (jones@cais.cais.com) wrote:
: : One thing that is really lacking in C++ is an intelligent macro facility.

: : To illustrate why one would be useful, consider the following problem:

: : You have a class hierarchy which you would like to make persistent.  In
: : order to do so, you need to provide the streaming code needed to save and
: : restore the members of each class and the bases for each class.  Here is
: : a trivial one:

: --- Demonstration clipped

: : Not only that, but serialization is only one of several things you
: : might want to do with these classes.  You might want to create dialog
: : boxes to access the data in the object.  You might want to use
: : multiple inheritance, which MFC 2.0 does not support (OWL 2.0 supports
: : multiple inheritance in its persistent streams library but the
: : required support code is even uglier).

: : In any case, the more support code you have to write, the more you
: : have to modify every time you add a member to a class.  It gets even
: : worse if you have shared pointers or arrays to worry about.

: : Wouldn't it be nice if you (the user of a class library) could just
: : say simply:

: :     class X: public CObject
: :     {
: :       public:
: :         int a,b,c;
: :     };

: :     class Y: public X
: :     {
: :       public:
: :         int d;
: :     };

: : and be done with it?

: : You can't do it in C++.  There is no way to encapsulate the formula
: : you use by hand to generate the support code for all the different
: : things you want to do with members of classes.

: Well... unless I'm mistaken you CAN do somesuch, or at least close, using a
: feature from the proposed ANSI standard called templates.  They make doing
: the same thing with many classes easy.  For instance, Borland implements its
: container class library (BIDS) using templates.  What this means is that
: to create a dynamically allocated, automatically sorted array of, say, class
: Foo, all you use the statement (I forget the exact template names... but
: this should be close)
:  template TSArrayVector<Foo> thisIsMyArray
: and all the operators are implented for Foo.  Of course Foo has to support a
: few things (like having == and < overloaded) but beyond that it is simple.

: I believe that templates, as I understand them, could be used to implement
: what you are suggesting.

: By the way, apparently Microsoft doesn't think templates are useful because
: they still haven't implemented them, while Borland has had templates since
: (I believe) 2.0.

: I will however, still check out ARC++, to see what it does.

: I hope this has been helpful

Templates don't handle this kind of problem.  Templates are glorified
C-preprocessor macros.  They have no looping and branching capabilities
and no ability to analyze the parse tree and symbol table.  ARC++ macros
can do all these things and more.

Ben Jones
jones@arsoftware.arclch.com





Author: jones@cais.cais.com (Ben Jones)
Date: 7 Jul 1994 19:55:03 GMT
Raw View
One thing that is really lacking in C++ is an intelligent macro facility.

To illustrate why one would be useful, consider the following problem:

You have a class hierarchy which you would like to make persistent.  In
order to do so, you need to provide the streaming code needed to save and
restore the members of each class and the bases for each class.  Here is
a trivial one:

    class X
    {
      public:
        int a,b,c;
    };

    class Y: public X
    {
      public:
        int d;
    };

If you are using Microsoft Foundation Classes, you can use "serialization"
to make objects of these classes persist.  However, you will have to declare
them as follows:

    #include <afx.h>

    class X: public CObject
    {
      public:
        int a,b,c;
      private:
        DECLARE_SERIAL(X);
    };

    IMPLEMENT_SERIAL(X,CObject,1)

    void X::Serialize(CArchive& ar)
    {
        if (ar.IsStoring())
            ar << a << b << c;
        else
            ar >> a >> b >> c;
    }

    class Y: public X
    {
      public:
        int d;
      private:
        DECLARE_SERIAL(Y);
    };

    IMPLEMENT_SERIAL(Y,X,1);

    void Y::Serialize(CArchive& ar)
    {
        if (ar.IsStoring())
            ar << d;
        else
            ar >> d;
    }

Just imagine what this mess will look like for something non-trivial!

Not only that, but serialization is only one of several things you
might want to do with these classes.  You might want to create dialog
boxes to access the data in the object.  You might want to use
multiple inheritance, which MFC 2.0 does not support (OWL 2.0 supports
multiple inheritance in its persistent streams library but the
required support code is even uglier).

In any case, the more support code you have to write, the more you
have to modify every time you add a member to a class.  It gets even
worse if you have shared pointers or arrays to worry about.

Wouldn't it be nice if you (the user of a class library) could just
say simply:

    class X: public CObject
    {
      public:
        int a,b,c;
    };

    class Y: public X
    {
      public:
        int d;
    };

and be done with it?

You can't do it in C++.  There is no way to encapsulate the formula
you use by hand to generate the support code for all the different
things you want to do with members of classes.

What is needed is something that could generate the support code by
looking at the class hierarchy.  Perhaps you could use a CASE tool, or
a "schema"-driven code generator.  Some people have experimented with
pointer swizzling or other behind the scenes mechanisms.  All of these
alternatives represent a failure of the language.  We can encapsulate
data and functionality but we can't encapsulate the capability we need.

Suppose, however, that C++ had an intelligent macro facility:

*   Let macros be defined as members of classes.

*   Let macros have looping, branching, and error reporting capabilities.

*   Let macros have access to the parse tree and symbol table.

*   Let macros insert or replace code in the parse tree.

*   Let "callback" macro names be invoked automatically at key points in
    the compilation process (such as whenever a class is defined).

*   Let multiple inheritance of classes which define such "callback"
    macros cause multiple analyses to take place.

Then, you could define a base class, such as "CObject" in the example
above which would cause each and every derived class to be automatically
analyzed to generate whatever additional code was necessary to support
persistence, dialog boxes, or whatever you can possibly conceive of.

You could actually write class libraries which don't require a Master's
Degree to understand how to use.

The capability described above is implemented in a preprocessor called
ARC++ which piggybacks onto your C++ compiler.

If you are interested in experimenting with this idea, you may download
ARC++ from the anonymous FTP:

    arsoftware.arclch.com:  /pub/arsoftware/arc++

Get "arc.READ_ME" for downloading instructions for various platforms.

Ben Jones
jones@arsoftware.arclch.com