Topic: ANSI C++ public comments


Author: franklin@millenium.texas.net (Frank Schmidt)
Date: 1995/04/13
Raw View
Ulf Schuenemann (schuenem@Informatik.TU-Muenchen.DE) wrote:

: In article <3m2s2q$ic6@empire.texas.net>, franklin@millenium.texas.net (Frank Schmidt) writes:
: [..]
: |> --------------------function templates-------------------------------
: |>
: |> Function templates should not resolve automaticly (except for implicit
: |> conversions, discussed later).  Explicit specification of template
: |> arguments should be required.  This would prevent C++ from being too
: |> automatic and to give the programmer more explicit control over how fn
: |> calls get resolved.  If I have
: |>
: |>     template <class T> T min(T t1,T t2) {
: |>         return t1 < t2 ? t1 : t2;
: |>     }
: |>
: |> Then I should only able to use this with
: |>
: |>     min<int>(3,4);
: |>
: [..]

: Hey, couldn't this be another usage for the "explicit" keyword? Let's
: make it worth to introduce a new keyword by given it more then just such a
: limited usage that "explicit" has now. :-)

: How about:

:  template <explicit class T> T min(T t1,T t2) {
:   return t1 < t2 ? t1 : t2;
:  }

: for all the programmers that want the behavior that Frank Schmidt describes?


Sounds ok for users, but compiler writers still have the headaches
involved with implicit resolution of function templates.  And one of my
main motivations for this suggestion was to simplify C++.






Author: johnw@jove.acs.unt.edu (Jove Super-User Account)
Date: 1995/04/13
Raw View
Frank Schmidt (franklin@millenium.texas.net) wrote:
> Ulf Schuenemann (schuenem@Informatik.TU-Muenchen.DE) wrote:

> : In article <3m2s2q$ic6@empire.texas.net>, franklin@millenium.texas.net (Frank Schmidt) writes:
> : [..]
> : |> --------------------function templates-------------------------------
> : |>
> : |> Function templates should not resolve automaticly (except for implicit
> : |> conversions, discussed later).  Explicit specification of template
> : |> arguments should be required.  This would prevent C++ from being too
> : |> automatic and to give the programmer more explicit control over how fn
> : |> calls get resolved.  If I have
> : |>
> : |>     template <class T> T min(T t1,T t2) {
> : |>         return t1 < t2 ? t1 : t2;
> : |>     }
> : |>
> : |> Then I should only able to use this with
> : |>
> : |>     min<int>(3,4);
> : |>
> : [..]

> : Hey, couldn't this be another usage for the "explicit" keyword? Let's
> : make it worth to introduce a new keyword by given it more then just such a
> : limited usage that "explicit" has now. :-)

> : How about:

> :  template <explicit class T> T min(T t1,T t2) {
> :   return t1 < t2 ? t1 : t2;
> :  }

> : for all the programmers that want the behavior that Frank Schmidt describes?


> Sounds ok for users, but compiler writers still have the headaches
> involved with implicit resolution of function templates.  And one of my
> main motivations for this suggestion was to simplify C++.

This is one of the suggestions that I originally hated, but it seems to
be getting better. However, to avoid enourmous complexity, the whole
template should be make explicit:
  explicit template<class T, class U> PairOfThings
instead of
  template<explicity class T, explicit class U> PairOfThings

John Williams
johnw@jove.acs.unt.edu
"Life is case sensitive."






Author: andrewfg@dai.ed.ac.uk (Andrew Fitzgibbon)
Date: 1995/04/14
Raw View
> Frank Schmidt (franklin@millenium.texas.net) wrote:
FS>     template <class T> T min(T t1,T t2)
FS>
FS> [should only able to be used as] min<int>(3,4);

> Ulf Schuenemann (schuenem@Informatik.TU-Muenchen.DE) wrote:
US> Hey, couldn't this be another usage for the "explicit" keyword?

Jove Super-User Account (johnw@jove.acs.unt.edu) wrote:
JW> This is one of the suggestions that I originally hated, but it seems to
JW> be getting better. However, to avoid enourmous complexity, the whole
JW> template should be make explicit:
JW>   explicit template<class T, class U> PairOfThings
JW> instead of
JW>   template<explicit class T, explicit class U> PairOfThings

Correct, and a nice idea.

(I suppose one could argue for 'default parameters' on the template,
 so that I could say
      template<explicit class T, class U> void P(T,U);
 and then use either
      P<int,float>(1,1.0)
      P<int>(1,1.0)
 but that would be getting very silly :-)

A.

--
Andrew Fitzgibbon (Research Associate),                     andrewfg@ed.ac.uk
Artificial Intelligence, Edinburgh University.               +44 031 650 4504
<a href=http://www.dai.ed.ac.uk/staff/personal_pages/andrewfg> Home Page </a>
              "I have seven friends, they taught me all I know.
        Their names are who, why, what, where, which, when, and how."





Author: gadget@coho.halcyon.com (Timothy Sharpe)
Date: 1995/04/14
Raw View
>>: : : : Default assignment operators are another unwanted favor C++ does for
>>: : : : programmers.  They should be generated for simple structs only, for C
>>: : : : compatibility.  For other classes, a "default_copy()" fn should be
>>: : : : generated if refered to so that the programmer can easily write his
>>: : : : own fns as in
>
> That is actually not a bad idea, but it is too late.
>C++ assignment is known to be broken. The problem in the
>presence of virtual bases is solved by again ensuring they
>have no members to assign.

Is it possible for the compiler to generate a text dump of the functions
it "adds for me" so I can see if its really what I want, or just include
them in the source for clarity?

Tim





Author: schuenem@Informatik.TU-Muenchen.DE (Ulf Schuenemann)
Date: 1995/04/12
Raw View
In article <3m2s2q$ic6@empire.texas.net>, franklin@millenium.texas.net (Frank Schmidt) writes:
[..]
|> --------------------function templates-------------------------------
|>
|> Function templates should not resolve automaticly (except for implicit
|> conversions, discussed later).  Explicit specification of template
|> arguments should be required.  This would prevent C++ from being too
|> automatic and to give the programmer more explicit control over how fn
|> calls get resolved.  If I have
|>
|>     template <class T> T min(T t1,T t2) {
|>         return t1 < t2 ? t1 : t2;
|>     }
|>
|> Then I should only able to use this with
|>
|>     min<int>(3,4);
|>
[..]

Hey, couldn't this be another usage for the "explicit" keyword? Let's
make it worth to introduce a new keyword by given it more then just such a
limited usage that "explicit" has now. :-)

How about:

 template <explicit class T> T min(T t1,T t2) {
  return t1 < t2 ? t1 : t2;
 }

for all the programmers that want the behavior that Frank Schmidt describes?


Ulf Schuenemann

--------------------------------------------------------------------
Ulf Sch   nemann
Fakult   t f   r Informatik, Technische Universit   t M   nchen, Germany.
email: schuenem@informatik.tu-muenchen.de





Author: milod@netcom.com (John DiCamillo)
Date: 1995/04/12
Raw View
In article <D6wq9M.Drp@syacus.acus.oz.au>, ian@syacus.acus.oz.au says...

>We need to
>develop complex software now.  We can't wait for another 10 years before
>the C++ ANSI committee gets its act together.

That's not entirely fair.  The ANSI/ISO committee seems to
be taking about as much time as is normal for a language
standard body.  (Using Ada95 as a single point of reference.)

>That is why I say there
>are now alternatives available to C++, in C++'s own domain, let alone in
>domains where C++ is being promoted that it doesn't really fit.

>Whether or not I do a very good job of criticising C++, I recommend that
>people think twice about it before jumping in, and analyze the
>alternatives.

>I think that even Bjarne would agree on that, as I have said him say
>many sensible things like this.  I think that they will also find that
>the alternatives are not promoted by sleazy salesmen, but have been
>developed with the overall goal of software quality in mind.  It is no
>wonder they find C++ deficient, and are prepared to point that out.

Yes, I think we all agree that one should evaluate the
alternatives before commiting to a development tool.  I
have never heard anyone in this group say (seriously)
"Trust me.  Use C++.  Everything else is crap.  I've done
this before, and I know the way."  I have heard people
say "Well, given your platform, your OS, your current
staff, your performance requirements, and the expected
lifetime of the system, C++ may still be a good choice
for you."

Right now, no one sells Eiffel, or Sather, or Java, or
SML on my platforms.  My choices are Smalltalk, C++, and
(very recently) Ada95.  This project started two years
ago, so there goes Ada95.  I want static typing, so there
goes Smalltalk.  Gee, what's left?

--
    ciao,
    milo
================================================================
    John DiCamillo                         Fiery the Angels Fell
    milod@netcom.com       Deep thunder rode around their shores





Author: ronb@marble.sentex.net (Ron Bakowski)
Date: 1995/04/11
Raw View
It's interesting how many of these issues are covered in "C++ Style"
documents that I've seen as well as a few documented "patterns".  In fact,
all of the style/programming standards documents (Pascal, APL and FORTRAN)
I've worked with intended to
 1. help programmers avoid programming language *pitfalls*
 2. help make the code readable & maintainable
 3. "extend" the language (insofar as it could be extended)

Let's try not to restrict C++'s extensibility by forcing style-related
issues.

My US1.45 cents worth.
--
Ron Bakowski
ronb@sentex.net
rbakowski@mercer.ca






Author: osinski@valis.cs.nyu.edu (Ed Osinski)
Date: 1995/04/11
Raw View
In article <3m4hah$nca@empire.texas.net>, franklin@millenium.texas.net (Frank Schmidt) writes:
|>
|> In article <3m3aci$2r8@silver.jba.co.uk> someone wrote:

   [ lots of stuff omitted ]

|> : : 2.  void fn(const T&);
|> : :
|> : : This does serve a useful purpose.  The idea here is that conceptually
|> : : you want to pass a structure by value, but you don't want the compiler
|> : : to bother making a copy of it.  So you use a reference.  But I think it
|> : : is reasonable to say that the compiler should pass by reference if you
|> : : have "void fn(const T)", and this solves this problem.
|>
|> : Which is nothing more than pass-by-reference without the explicit "this is
|> : a reference, boys" syntax.  I'd say that that was *less* clear, not more.
|>
|> My point is that in the case of fn(const T), it makes no difference to the
|> programmer if this is handled by value or reference.  It is just a compiler
|> implementation detail.  Therefor, it does not reduce the clarity of what
|> happens from the programmers point of view.  But it does simplify the
|> language.

Actually, it *does* make a difference in the semantics because of possible
aliasing.  For example:

 T x = orig_val;  // global

 void fn (const T v)
 {
    x = new_val;
    ... v ...
 }

 fn(x);

If x was passed by value, the value of v within fn (after the assignment
to x) is orig_val, but it would be new_val if x were passed by reference.

 [ lots more stuff skipped ]

--
---------------------------------------------------------------------
 Ed Osinski
 Computer Science Department, New York University
 E-mail:  osinski@cs.nyu.edu
---------------------------------------------------------------------
In the early years of the 16th century, to combat the rising tide of
religious unorthodoxy, the Pope gave Cardinal Ximinez of Spain leave
to move without let or hindrance throughout the land, in a reign of
violence, terror and torture that makes a smashing film.  This was
the Spanish Inquisition...
    -- Monty Python's Flying Circus





Author: jim.fleming@bytes.com (Jim Fleming)
Date: 1995/04/12
Raw View
In article <3m735n$t53@empire.texas.net>, franklin@millenium.texas.net
says...
>

[snip]

>The C++ philosophy seems to be: if the C++ designers like
>a feature, they add it to C++ and make it automatic, thereby making it
>painful for programmers to avoid the feature.  And if the C++ designers
>don't like a feature, they just ban it.

Who are the "C++ designers" considered to be at this point?

Is it a small number? Do they know anything about language design?

Are all of the members of the ANSI Standards activity considered to
 be designers?

Are the ANSI committee members "designing" or just "clarifying" and
 trying to develop a common language definition that matches
 all of their compilers?

Was C++ designed...or did it evolve?

Since the standard is supposedly done, are there any compilers
 that compile programs that conform to the standard?

Is someone or some company (maybe in Hawaii) going to "certify"
 that compiler vendors conform to the standard? If so,
 how much will this certification process cost?

How much in additional cost is an ANSI standard conforming C++
 compiler expected to cost?

How are companies going to recover the reported $60,000,000 spent
 on developing the C++ standard?

--
Jim Fleming            /|\      Unir Corporation       Unir Technology, Inc.
%Techno Cat I        /  | \     One Naperville Plaza   184 Shuman Blvd. #100
Penn's Landing      /   |  \    Naperville, IL 60563   Naperville, IL 60563
East End, Tortola  |____|___\   1-708-505-5801         1-800-222-UNIR(8647)
British Virgin Islands__|______ 1-708-305-3277 (FAX)   1-708-305-0600
                 \__/-------\__/       e-mail: jim.fleming@bytes.com
Smooth Sailing on Cruising C+@amarans  ftp: 199.3.34.12 <-----stargate----+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\____to the end of the OuterNet_|






Author: franklin@millenium.texas.net (Frank Schmidt)
Date: 1995/04/12
Raw View
Ed Osinski (osinski@valis.cs.nyu.edu) wrote:
: In article <3m4hah$nca@empire.texas.net>, franklin@millenium.texas.net (Frank Schmidt) writes:
: |>
: |> In article <3m3aci$2r8@silver.jba.co.uk> someone wrote:

:    [ lots of stuff omitted ]

: |> : : 2.  void fn(const T&);
: |> : :
: |> : : This does serve a useful purpose.  The idea here is that conceptually
: |> : : you want to pass a structure by value, but you don't want the compiler
: |> : : to bother making a copy of it.  So you use a reference.  But I think it
: |> : : is reasonable to say that the compiler should pass by reference if you
: |> : : have "void fn(const T)", and this solves this problem.
: |>
: |> : Which is nothing more than pass-by-reference without the explicit "this is
: |> : a reference, boys" syntax.  I'd say that that was *less* clear, not more.
: |>
: |> My point is that in the case of fn(const T), it makes no difference to the
: |> programmer if this is handled by value or reference.  It is just a compiler
: |> implementation detail.  Therefor, it does not reduce the clarity of what
: |> happens from the programmers point of view.  But it does simplify the
: |> language.

: Actually, it *does* make a difference in the semantics because of possible
: aliasing.  For example:

:  T x = orig_val;  // global

:  void fn (const T v)
:  {
:     x = new_val;
:     ... v ...
:  }

:  fn(x);

: If x was passed by value, the value of v within fn (after the assignment
: to x) is orig_val, but it would be new_val if x were passed by reference.

Quite right.  I addressed this question in my original post.






Author: maxtal@Physics.usyd.edu.au (John Max Skaller)
Date: 1995/04/09
Raw View
Various people wrote:

>: Frank:
>: : : : Construction of virtual base classes now happens at the highest class.
>: : : : This is a mistake.

>To repeat from above: "The rule should be that the virtual base class should
>be constructed at the lowest level that contains all references to the
>virtual base class."  Does this answer your question?

 No. C++ is fine as it is. Assume it is correct and _deduce_
that you are using it incorrectly. What exactly are you doing wrong?
Answer: providing virtual bases with constructors and data members
is usually a bad idea. They should be usually abstract and data-less.
The almost always should have ONLY a default constructor and copy
constructor, no other constructors.

 [There are exceptions, especially tricks which leverage
off the seemingly peculiar rules.]
>
>: : : : Default assignment operators are another unwanted favor C++ does for
>: : : : programmers.  They should be generated for simple structs only, for C
>: : : : compatibility.  For other classes, a "default_copy()" fn should be
>: : : : generated if refered to so that the programmer can easily write his
>: : : : own fns as in

 That is actually not a bad idea, but it is too late.
C++ assignment is known to be broken. The problem in the
presence of virtual bases is solved by again ensuring they
have no members to assign.


--
        JOHN (MAX) SKALLER,         INTERNET:maxtal@suphys.physics.su.oz.au
 Maxtal Pty Ltd,
        81A Glebe Point Rd, GLEBE   Mem: SA IT/9/22,SC22/WG21
        NSW 2037, AUSTRALIA     Phone: 61-2-566-2189





Author: johnw@jove.acs.unt.edu (John Robert Williams)
Date: 1995/04/09
Raw View
Frank Schmidt (franklin@millenium.texas.net) wrote:
> I thought I would post this file of my criticisms of C++ that I have
> acumulated.

I don't feel like making a whole lot of comments at the moment, so I'll
just point out that even though a few of your suggestions are good, many
will break a lot of code, and some will SEVERELY break ALL code (even the
standard stuff like stream I/O). I would go so far as to say that the
language you want to create is not C++, just C-derived.

John Williams
johnw@jove.acs.unt.edu
"Life is case sensitive."






Author: franklin@millenium.texas.net (Frank Schmidt)
Date: 1995/04/09
Raw View
John Max Skaller (maxtal@Physics.usyd.edu.au) wrote:
: Various people wrote:

: >: Frank:
: >: : : : Construction of virtual base classes now happens at the highest class.
: >: : : : This is a mistake.

: >To repeat from above: "The rule should be that the virtual base class should
: >be constructed at the lowest level that contains all references to the
: >virtual base class."  Does this answer your question?

:  No. C++ is fine as it is. Assume it is correct and _deduce_
: that you are using it incorrectly. What exactly are you doing wrong?
: Answer: providing virtual bases with constructors and data members
: is usually a bad idea. They should be usually abstract and data-less.
: The almost always should have ONLY a default constructor and copy
: constructor, no other constructors.

Are you saying that C++ intentionally made it difficult to handle
constructors taking arguments in virtual base classes to discourage
this kind of thing?  Then why make it possible at all?  If you are
going to make it possible, then why not do it right, as I suggested?






Author: franklin@millenium.texas.net (Frank Schmidt)
Date: 1995/04/09
Raw View
John Robert Williams (johnw@jove.acs.unt.edu) wrote:
: Frank Schmidt (franklin@millenium.texas.net) wrote:
: > I thought I would post this file of my criticisms of C++ that I have
: > acumulated.

: I don't feel like making a whole lot of comments at the moment, so I'll
: just point out that even though a few of your suggestions are good, many
: will break a lot of code, and some will SEVERELY break ALL code (even the
: standard stuff like stream I/O). I would go so far as to say that the
: language you want to create is not C++, just C-derived.

I agree with you.  As Stroustrup points out, the way to handle big
changes is to implement them while allowing old style code.  The old
style code should generate warnings.  Eventually people will adapt to
the changes, and the old stuff can be dropped.  C++ itself breaks most
C code because of minor incompatibilities.






Author: JdeBP@jba.co.uk (Jonathan de Boyne Pollard)
Date: 1995/04/10
Raw View
Frank Schmidt (franklin@millenium.texas.net) wrote:
: Paul J. Ste. Marie (pstemari@erinet.com) wrote:
: : In article <3m2s2q$ic6@empire.texas.net>,
: :    franklin@millenium.texas.net (Frank Schmidt) wrote:

: : :The problem is that C++ resolves B* to void* rather than A*.
: : :Converions of not fully declared types should be banned.

: : This is frequently the desired behavior, esp with template
: : containers of pointers.

: : :I would also suggest allowing declarations like:
: : :
: : :    class B : public A;
: : :
: : :This could be useful for classes that refer to each other.

: : And solves nothing, since this does not give the compiler enough
: : information to convert a A* to a B* or vice-versa.  That conversion
: : cannot be performed without knowledge of the complete layout of B
: : and A.  Clue--derived/base pointer conversion involve applying an
: : offset.

: The structure of A is needed, but the structure of B is not since
: base classes always come before members in the layout.
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Since when was an implementation detail like this part of the language
specfication ?

Although I too would like the ability to include base class specifiers in
forward declarations (but for an entirely different reason than you), Paul
does have a point.





Author: "Ronald F. Guilmette" <rfg@rahul.net>
Date: 1995/04/10
Raw View
In article <3m3aci$2r8@silver.jba.co.uk>,
Jonathan de Boyne Pollard <JdeBP%utopium@jba.co.uk> wrote:
>
>And cue another long language war.

Sure.  Why not?  (We haven't had a good one for awhile.)

>Be very careful of falling into the trap of blaming the language for the
>fact that you don't understand it.
>
>I don't understand Cyrillic, but that's not a valid criticism of Russian.

Be very careful.

I don't understand Kanji or Katakana (sp?), and that _is_ a valid criticism
of those immense alphabets.

--

-- Ron Guilmette, Sunnyvale, CA ---------- RG Consulting -------------------
---- E-mail: rfg@segfault.us.com ----------- Purveyors of Compiler Test ----
-------------------------------------------- Suites and Bullet-Proof Shoes -





Author: beman@dawes.win.net (Beman Dawes)
Date: 1995/04/07
Raw View
In article <3m3aci$2r8@silver.jba.co.uk>, Jonathan de Boyne Pollard (JdeBP@jba.co.uk) writes:

>And if any of the standards people have read this far, count this
>as one compiler user's voice against the change to the semantics of
>operator new. Bring back NULL return values !

The committee accepted a proposal last month in Austin to provide an
operator new function which always returns a null pointer on
failure.  The signature is:

        class nothrow{};
        void* operator new( size_t, const nothrow& ) throw();

The appropriate operator delete, operator new[], and operator
delete[] changes were part of the proposal accepted.


-- Beman    (beman@dawes.win.net)





Author: franklin@millenium.texas.net (Frank Schmidt)
Date: 1995/04/07
Raw View
In article <3m3aci$2r8@silver.jba.co.uk> you wrote:
: And cue another long language war.

I thought I was giving constructive criticism.

: : 2.  simple to implement - Is it easy for the compiler writers to
: : implement?  This affects the user in 2 ways; fewer compiler bugs and
: : faster compile times.

: Of course, the reverse side of this criterion is that simple to implement
: languages are often simple languages.  i.e. not powerful/elaborate/rich.

: It's also often surprising that the hard to implement languages are the
: ones that attempt to hide the most from the user.  If you compare the size
: of the average BASIC runtime to the size of the average Standard C Library,
: and consider what each are doing, then you'll see what I'm getting at.

C is a good language precisely because it offloads complexity to libraries.
C itself is simple.  I can't say the same for C++.

: : Function templates should not resolve automaticly (except for implicit
: : conversions, discussed later).

: Why not ?  That effectively knobbles the entire concept.

As I said:  "This would prevent C++ from being too automatic and to give the
programmer more explicit control over how fn calls get resolved."

: If you don't want automatic instantiation of templates, the answer is very
: easy : don't use templates and write plain overloaded functions instead.

The concept of fn templates is to eliminate the need for doing the same sort
of thing with macros.  The idea is good, but I disagree with the way it was
done.


: : Most uses of references should be removed from C++.  They are an
: : unnecessary complication.

: Is this the "they're only pointers under the covers anyway" argument, or
: the "we don't need anything other than pass-by-value/return-by-value"
: argument ?

: : 1.  void fn(T&);
: :
: : This construct adds nothing but confusion to the language.  In C, if I
: : had:
: :
: :     int i = 8;
: :     fn(i);
: :
: : I could be sure that the variable i was still 8.  But not with C++.

: Ah, the "we don't need anything other than pass-by-value" argument.

: The situation here is simply  that the contract between the function user
: and the function implementor is extended to allow pass-by-modifiable-reference
: semantics in addition to the old pass-by-value semantics.  Quite why allowing
: such contracts to be formed is a *failing* of the language is beyond me.

: In fact, people who suggest this should really be ashamed of their
: blinkered viewpoint, because many other programmers writing in other
: languages often have pass-by-modifiable-reference as the *default* for
: function arguments and are quite able to use them.

: Consider the number of MS BASIC programmers who have pass-by-modifiable-
: -reference semantics by default, and yet seem to be perfectly happy about
: that, and seem to be writing reasonably productive programs.

: It's merely a question of learning the forms of contract that function
: implementor and function user can form.  There's nothing inherently *wrong*
: about pass-by-modifiable-reference.

: In other words, Know Your Language.

I haven't followed this news group much, so I don't know if this has been
covered before.  Anyway, I don't have anything in principle against
references.  In fact, if I were designing a new language, I would just
support references.  I have 2 criticisms of C++'s references.  First, they
are half-baked.  Like most things C++ has added to C, the design of
references was poorly done.  If you are interested, I can describe how
references should have been designed.  But, my other criticism is that C
already has pointers, and pointers and references are 2 answers to the same
problem.  Supporting both adds nothing but complexity to the language, so
even adding well designed references to C would be a mistake.


: : 2.  void fn(const T&);
: :
: : This does serve a useful purpose.  The idea here is that conceptually
: : you want to pass a structure by value, but you don't want the compiler
: : to bother making a copy of it.  So you use a reference.  But I think it
: : is reasonable to say that the compiler should pass by reference if you
: : have "void fn(const T)", and this solves this problem.

: Which is nothing more than pass-by-reference without the explicit "this is
: a reference, boys" syntax.  I'd say that that was *less* clear, not more.

My point is that in the case of fn(const T), it makes no difference to the
programmer if this is handled by value or reference.  It is just a compiler
implementation detail.  Therefor, it does not reduce the clarity of what
happens from the programmers point of view.  But it does simplify the
language.

: : One other use of references is as a pointer guaranteed not to be null.
: : A much better solution to this is to add a key word like "not_null" for
: : pointers as in:
: :
: :     char *not_null s;
: :     char * s2;
: :     s2 = s;                 // ok
: :     s = s2;                 // compiler error
: :     s = not_null_cast(s2)   // ok

: Bollocks to that!  When I want the compiler fooling around trying to second
: guess what I really mean, and whether I could *possibly* (but not
: necessarily, given the above example) have a NULL pointer, *I'll* code it,
: thank you very much.

: This sort of nannying by the compiler makes the language *much* less
: controllable.

So I suppose you're against "const" too.  It is the same kind of idea.

: : Note that a pointer declared "A * const not_null p;" differs from a
: : reference only in terms of syntax.

: Exactly!  So why bother ?  Just keep references.

References are more complex.  not_null is more general.


: : Construction of virtual base classes now happens at the highest class.
: : This is a mistake.  It is particularly annoying when developing a class
: : library where I want to use virtual base classes to implement the
: : library, but I don't want to bother users of the library.  The rule
: : should be that the virtual base class should be constructed at the
: : lowest level that contains all references to the virtual base class.

: Except that once someone comes along and uses your class library you are in
: big trouble with this rule.

: Consider the case when they inherit from one of your leaf classes.  In your
: library, those classes are "the lowest level that contains all references
: to the virtual base class".  But when someone multiply inherits from two of
: your leaf classes they suddenly aren't any more, and by your rule the
: person implementing the new, inherited, class should now call the virtual
: base class constructors.

: Except that your library classes are also calling the virtual base class
: constructors as well ...

: Nightmare city.

Exactly the same problem arises with C++ as it is currently designed.  If a
library contains a concrete class derived from a virtual base class, it must
construct that virtual base class.  If a user derives a class from this
concrete class, he too must construct the virtual base class.  The solution
is that the compiler just picks which constructor of the virtual base class
to use for each class.  C++ now does this one way, and I suggested another
way.  Please "Know Your Language" ;)


: : One should be able to say that a virtual base class is no longer virtual
: : as follow:

: No comment, except to ask :

: Have you ever encountered a *real world* use for such a thing ?

Yes.  The explanation is involved.  I will provide it if you insist.


: : C made a minor mistake in not differenciating between a pointer to a
: : type and a pointer to an array of a type.  This has become a major
: : mistake in C++.  Consider the following:
: :
: :     class A { int i1; };
: :     class B : public A { int i2; };
: :     B b[9];
: :     A* p = b;
: :     A* p2 = p + 2;
: :
: : This non-sensical code is legal.  It shouldn't be.

: It makes perfect sense to someone who understands the language. This is my
: "Know Your Language" point from before.

Gee?  Are you saying the above code looks good to you?  If yes, then, thank
you, you are proving my point that C++ code is not predictable.  The above
code does not do what you would expect.  The variable p2 is set to
(A*)&b[1], not (A*)&b[2] as you might expect.  Think about it.


: : It would be nice if one could use any type, not just classes, as the
: : base for a class.  For example:
: :
: :     template <class T> class handle : public T*

: Is this a case of your confusing IS-A with HAS-A ?

No.  A handle is a kind of pointer.  It is only implemented by having a
pointer because of the limitiations of C++.


: : What does this declaration mean:
: :
: :     a b(c);
: :
: : "a" must be a class.  "b" can be either a function or an object
: : depending on "c".  If "c" is a class, "b" is a function.  If "c" is an
: : object, "b" is an object.  To fix this kind of ambiguity, C++ should
: : require the word "extern" for function declarations.

: Hmmmm.  Removing the "implicitness" of linkage specifiers would be a good
: idea (see above), but I suspect that simply adding "extern" won't fix all
: of the ambiguities between declarations and definitions.

Example please.


: :      A a;
: :      void (.*spmf)() = &a.fn;
: :      // Implemented as a two-element struct containing a pointer to `a'
: :      // and a pointer to `fn'. Note that it is not necessary to handle
: :      // virtual functions in a special way.
: :
: : This would be useful for all sorts of callback functions, as well as
: : allowing the programmer to check for vtable corruption.

: Good idea, except ...

: ... it isn't useful in the vast majory of callback functions, because (in
: my experience anyway) callbacks are mainly used by the O/S API, which doesn't
: expect funny <object,pointer> tuples for callbacks, but simple function
: addresses.  A much better way around this is to use reflector functions
: instead, anyway.

What is a reflector function?  My (Bill Daly's) suggestion does make
callbacks much easier under C++ and is consistent in philososphy with C's fn
pointers.  I personally don't like callbacks, but I don't see why other
people shouldn't be able to use them.

: ... checking for vtable corruption is properly the remit of the C++ run-time,
: and *not* that of the programmer.  Who is to say that there *is* a vtable
: in the first place anyway ?  You shouldn't assume that the implementation
: works that way.

Memory corruption is a fact of life, and one should have a reasonable way to
check for corruption anywhere in data space.  As things are now, I am forced
to figure out exactly how the compiler implements vtables and then analyze
these vtables in the debugger.  My suggestion allows one to check for
corruption in a implementation independant way as in:

     struct A {
          virtual void fn();
     };
     A a;
     void (.*spmf)() = &a.fn;
     // some stuff that may corrupt memory
     assert( &a.fn == spmf );   // check if vtable was corrupted


: : Consider the following program:
: :
: :     #include <iostream.h>
: :     class A {};
: :     inline void fn(void*)   { cout << "not ok\n"; }
: :     inline void fn(A*)      { cout << "ok\n"; }
: :     class B;
: :     inline void fn2(B* p)   { fn(p); }
: :     class B : public A {};
: :
: :     main() {
: :         B b;
: :         fn2(&b);
: :         return 0;
: :     }
: :
: : This program will print out "not ok".  The problem is that C++ resolves
: : B* to void* rather than A*.  Converions of not fully declared types
: : should be banned.

: Hmmmm.  I see an additional problem.  What if the compiler chooses to
: inline the function ?  Would the behaviour be different, considering that
: the inlining would occur at a point where the conversion from B to A would
: be known ?

: ( There are probably "as if" rules to cover this, I suppose. )

What?


: : "new" is like "malloc", and "delete" is like "free", but what is like
: : "realloc"?  I suggest "renew".

: Hmmmm.  So you are willing to sort out the semantics of deep-copy classes,
: virtual inheritance, and uncopyable (private copy constructor) classes are
: you ?

Virtual inheritance is not a problem.  Deep copying and uncopyable classes
would be handled by the programmer using my suggested reconstructor.  I am
not trying to add complexity here, just consistancy.  The need for a "renew"
idea does arise, and the current soltions are quite messy.


: : Ctors don't inherit.  This is usually what is wanted, but not always.
: : Once again, C++ fails to give the programmer control.  The obvious way
: : to give the programmer control would be with the namespace features as
: : in:
: :
: :     class A {
: :     public:
: :         A(int);
: :         A& operator=(const A&);
: :     };
: :
: :     class B : public A {
: :     public:
: :         using A::A;             // inherit constructor
: :         using A::operator=;
: :         // ...
: :     };

: Hehehehehehe.  What you want are

:    #pragma On(SOMObject_constructors)
:    #pragma On(SOM_Object_Assignment_Operators)

: <smug look>

I guess so.  Is this part of ANSI C++?  Do you have a smug look because your
compiler gives you something that ANSI C++ fails to provide?


: : A related problem is when you don't want to inherit a class fn.  I
: : would like a key word like "dont_inherit" to handle this.  Here is an
: : example:
: :
: :     class A {
: :     public:
: :         dont_inherit virtual int Size()    { return sizeof(*this); }
: :     };
: :
: : When Size() gets inherited, it returns the wrong result.  Fns declared
: : "dont_inherit virtual" would force its redefinition in each derived
: : class.

: The pure virtual mechanism does most of what you want already.

How?  Please explan using my Size() example.


: :              Another example is an Init fn.  I don't like constructors for
: : various reasons, so I often use an Init() fn instead.  I would prefer
: : if this weren't inherited.

: Sheesh!  Make it private (or protected) then!

See my criticism of C++ ctors to see why I want Init to be public.


: : I would also give the programmer more control over the order of
: : construction by allowing one "order" statement in a class to specify
: : the order of construction as in
: :
: :     class B : public A {
: :     public:
: :         order c, B;
: :         B() : c(), A(&c) {} // ok
: :         C c;
: :     };

: Such ordering is implicit in the semantics of base class and member
: construction already, though.

That's the problem.  It should be able to be explicitly controlled be the
programmer.  The above example shows why.


: : C++ constructors are poorly designed.  They are unnecesarily complex
: : and confusing.
: :                     [.......]
: :                                              Instead of using the
: : current complex syntax of ctors to do this which looks like
: :
: :     myFrame::myFrame
: :         : upper_left(...)
: :         , upper_right(...)
: :         , lower_left(...)
: :         , lower_right(...)
: :     {}
: :
: : a simple "construct" statement, to be used only in constructors would
: : have sufficed.  So in the above example, the ctor for myFrame would
: : look like
: :
: :     myFrame::myFrame {
: :         construct frame();
: :         const int dx = 100;
: :         const int dy = 30;
: :         int x1 = (Dx()/2 - dx)/2;
: :         int x2 = Dx()/2 + x1;
: :         int y1 = (Dy()/2 - dy)/2;
: :         int y2 = Dy()/2 + y1;
: :         construct upper_left("upper left",x1,y1,dx,dy);
: :         construct upper_right("upper right",x2,y1,dx,dy);
: :         construct lower_left("lower left",x1,y2,dx,dy);
: :         construct lower_right("lower right",x2,y2,dx,dy);
: :     }

: Aesthetics, pure and simple.

: You *can* call class function members in the initialisers for class data
: members, by the way.  It's only the initialisers for base class
: constructors where you have a potential problem (but such wasn't your
: example anyway).

Did I fail to express myself clearly?  I consider this my most important
criticism of C++.  Please re-read it and re-consider it.  If you still don't
see the point, I will try re-state it.


: : Default assignment operators are another unwanted favor C++ does for
: : programmers.  They should be generated for simple structs only, for C
: : compatibility.  For other classes, a "default_copy()" fn should be
: : generated if refered to so that the programmer can easily write his
: : own fns as in

: Let me get this straight.  You don't like the compiler generating a default
: shallow copy version of operator= for you, and yet you want it to generate
: a default shallow copy function for you, just with a different name ?

: Why ?  If you don't like shallow copy, you implement your own operator=.
: If you do like shallow copy (and are lazy), you let the compiler implement
: it for you.  What's the problem ?

The problem isn't that the compiler generates the code for the shallow copy,
it is that it also applies this code automaticly.  I want to explicitly
control when the compiler generates this code and when it gets applied.


: And if any of the standards people have read this far, count this as one
: compiler user's voice against the change to the semantics of operator new.
: Bring back NULL return values !

I agree.  Those who prefer having the new operator throw an exception can
always use:

    template <class T> T* safe_new() {
        T* p = new T;
        if( !p )  throw "new failed";
        return p;
    }


: Anyway.  I'll now stand back and watch the war ... (-:

You mean "constructive discussion", right?





Author: franklin@millenium.texas.net (Frank Schmidt)
Date: 1995/04/07
Raw View
I thought I would post this file of my criticisms of C++ that I have
acumulated.


I use C++ because it is the best language available.  I do not like C++,
I just dislike the alternatives more.  The basic idea of C++, to add
classes to C doing as much as possible at compile time, was right.  But
the way that this was accomplished was poorly designed.

In my opinion a good programming language (or any other technology) has
the following characteristics:

    1.  simple to use
    2.  simple to implement
    3.  predictable
    4.  controllable
    5.  powerful

These are explained below.

1.  simple to use - Is it easy for you, the user, to use?

2.  simple to implement - Is it easy for the compiler writers to
implement?  This affects the user in 2 ways; fewer compiler bugs and
faster compile times.

3.  predictable - Looking at a piece of code, how easy is it for you to
predict what it will do?  Languages that are too automatic and
ambiguous, and try to guess what the programmer means get low marks
here.  This also includes the idea of safety, meaning that dangerous and
unintended actions are flagged by the compiler because such actions may
have unpredictable results.

4.  controllable - Can you make the language/compiler do exactly what
you want?  Do you feel in control?  Is the language flexible.  A related
concept to "control" is "freedom".  Type casting is a good example of
how C gives you control.

5.  powerful - This is self explanatory, but could probably best be
defined as meaning/char.  This is what feature happy languages stress at
the expense of the previous 4 goals.

Here is is how I would rate some languages (from 0 to 10):

C
    1.  simple to use               7
    2.  simple to implement         7
    3.  predictable                 8
    4.  controllable                9
    5.  powerful                    5

C++
    1.  simple to use               5
    2.  simple to implement         3
    3.  predictable                 4
    4.  controllable                5
    5.  powerful                    9

assembler
    1.  simple to use               2
    2.  simple to implement         10
    3.  predictable                 2
    4.  controllable                10
    5.  powerful                    0

pascal
    1.  simple to use               8
    2.  simple to implement         8
    3.  predictable                 9
    4.  controllable                2
    5.  powerful                    4


Below are my detailed criticisms.  I will also explain how I would
correct the fault.  I will rate my suggestion against C++ for these 5
goals on a scale of -10 to +10.

    function templates
    remove references
    not_null pointer
    overload
    constructor for virtual base classes
    ~virtual
    : [private] A
    array vs pointer
    user defined conversions
    non-member conversions
    base types
    require extern
    address of object function
    bad type resolution
    initializing arrays
    renew
    inherit (use) non-inherited fns
    dont_inherit
    order of construction
    constructor design
    no default assignment operator
    template parameters and base class members
    exceptions
    preprocessing


--------------------function templates-------------------------------

Function templates should not resolve automaticly (except for implicit
conversions, discussed later).  Explicit specification of template
arguments should be required.  This would prevent C++ from being too
automatic and to give the programmer more explicit control over how fn
calls get resolved.  If I have

    template <class T> T min(T t1,T t2) {
        return t1 < t2 ? t1 : t2;
    }

Then I should only able to use this with

    min<int>(3,4);

or alternatively, I could do

    inline int min(int i1,int i2)  { return min<int>(i1,i2); }

    min(3,4);

I can't think of any big benefit to automatic fn template resolution,
but I can think of plenty of negatives; complexity, ambiguity, and lack
of control.

Here is how this suggestion compares to the current C++ rules:

    1.  simple to use               0
    2.  simple to implement         +7
    3.  predictable                 +7
    4.  controllable                +4
    5.  powerful                    -1

--------------------------remove references--------------------------

Most uses of references should be removed from C++.  They are an
unnecessary complication.  As far as I can tell, there is only one valid
reason to have references, and this is for functions that need to be
able to return by reference.

One reason often mentioned for references is passing by reference.  The
reference can be non-const or const, so I will consider the possibilites
in turn.

1.  void fn(T&);

This construct adds nothing but confusion to the language.  In C, if I
had:

    int i = 8;
    fn(i);

I could be sure that the variable i was still 8.  But not with C++.
There is simply nothing that fn(T&) can do that fn(T*) can't do better
and more clearly.

2.  void fn(const T&);

This does serve a useful purpose.  The idea here is that conceptually
you want to pass a structure by value, but you don't want the compiler
to bother making a copy of it.  So you use a reference.  But I think it
is reasonable to say that the compiler should pass by reference if you
have "void fn(const T)", and this solves this problem.  So explicit
references aren't needed here.  I can see 2 objections;  One is that if
the compiler passes by reference for "void fn(const T)", something
inside fn() may, as a side effect, change the variable while it is being
used in fn().  I think this would be extremely rare, and if the
programmer wants to be sure he has a copy, he can do:

    void fn(const T tRef) {
        T t = tRef; // use t instead of tRef
    }

The second problem is that currently "void fn(const T)" isn't allowed if
T is an abstract type.  But there would be no reason for this
restriction if my suggestion was followed.

In summary, references add a lot of complexity and confusion to C++.  My
suggestion is much simpler and would makes C++ easier to use and to
implement.

    1.  simple to use               +10
    2.  simple to implement         +7
    3.  predictable                 +4
    4.  controllable                0
    5.  powerful                    -1

----------------------not_null pointer-----------------------------

One other use of references is as a pointer guaranteed not to be null.
A much better solution to this is to add a key word like "not_null" for
pointers as in:

    char *not_null s;
    char * s2;
    s2 = s;                 // ok
    s = s2;                 // compiler error
    s = not_null_cast(s2)   // ok

One other advantage of this over references is that you could do things
like:

    char not_null* not_null* argv;

Note that a pointer declared "A * const not_null p;" differs from a
reference only in terms of syntax.

    1.  simple to use               -1
    2.  simple to implement         -2
    3.  predictable                 +4
    4.  controllable                0
    5.  powerful                    +1

--------------------------overload-------------------------------

It was a mistake for C++ to get rid of the "overload" key word.  Here is
an example of how not having overload can cause subtle bugs:

    //  t1.h
    #include <iostream.h>
    inline void fn1(long)   { cout << "ok\n"; }
    inline void fn2()       { fn1(0); }

    //  t2.h
    #include <iostream.h>
    inline void fn1(int)    { cout << "error\n"; }

    //  t.cpp
    #include "t1.h"
    #include "t2.h"
    main() {
        fn2();
        return 0;
    }

This prints out "ok".  If you reverse the order of the include files,
you will get "error".  This is very hard to track down.  The rule should
be that an overload declaration is needed before the first use of an
overloaded fn in a namespace.  This eliminates all ambiguities and order
dependancies.  It also should prevent the old complaints about
"overload" because the user doesn't have to worry about overloaded fns
that are only declared but never used.

    1.  simple to use               -1
    2.  simple to implement         -2
    3.  predictable                 +8
    4.  controllable                0
    5.  powerful                    -1

------------constructor for virtual base classes---------------------

Construction of virtual base classes now happens at the highest class.
This is a mistake.  It is particularly annoying when developing a class
library where I want to use virtual base classes to implement the
library, but I don't want to bother users of the library.  The rule
should be that the virtual base class should be constructed at the
lowest level that contains all references to the virtual base class.  So
with

    class A {
    public:
        A(int) {}
    };
    class B : public virtual A {};
    class C : public B, public virtual A {};
    class D : public C {};

    main() {
        D d;
        return 0;
    }

the constructor of A should be in C, not D.

    1.  simple to use               +3
    2.  simple to implement         -1
    3.  predictable                 0
    4.  controllable                0
    5.  powerful                    +3

-------------------------~virtual------------------------------------

One should be able to say that a virtual base class is no longer virtual
as follow:

    class A {};
    class B : public virtual A {};
    class C : public virtual A {};
    class D : public B, public C, public ~virtual A {};

Now if I have

    class E : public B, public C, public D {};

The class E will contain 2 A's, one for B and C, and one for D.  Further
more, a cast from A* to D* is now ok because A is not a virtual base
class of D, it is a regular base class.

    1.  simple to use               -1
    2.  simple to implement         -1
    3.  predictable                 0
    4.  controllable                +7
    5.  powerful                    +2

---------------------: [private] A----------------------------------

In C++ "class B : A {};" is interpreted as "class B : private A {};".
This is usually an unwanted favor.  The user should be required to
explicitly state his preference, "private", "protected", or "public".

    1.  simple to use               0
    2.  simple to implement         +1
    3.  predictable                 +3
    4.  controllable                0
    5.  powerful                    -0.01

-------------------array vs pointer---------------------------------

C made a minor mistake in not differenciating between a pointer to a
type and a pointer to an array of a type.  This has become a major
mistake in C++.  Consider the following:

    class A {
        int i1;
    };

    class B : public A {
        int i2;
    };

    B b[9];
    A* p = b;
    A* p2 = p + 2;

This non-sensical code is legal.  It shouldn't be.  A pointer to a type
and to an array should be different types.  A pointer to an array should
implicitly convert to a pointer to an element, but not the other way
around.  How should we declare a pointer to an array?  C and C++
declarations are quite ugly, but it is too late to do much about that
now.  We need something new, so I suggest "[*]" as in

    A [*]       // a pointer to an array of A's

When we have a variable, I am not sure if this should go before or
after.  I will pick before, so

    A [*]p;     // p points to an array of A's
    A [*3]p;    // p points to an array of 3 A's

So now consider these examples:

class A {};
class B : public A {};

    A a[3];
    A[*] p1 = a;        // ok
    A* p2 = a;          // ok, implicit conversion
    p1 = p2;            // error
    p2 = p1;            // ok
    p1 += 2;            // ok
    p2 += 2;            // warning, obsolete
    B b[3];
    p1 = b;             // error, can't convert from B[*] to A[*]
    p2 = b;             // ok, B[*] -> B* -> A*

This change will cause a lot of warnings to be generated for C code, but
I think it is worth while.  It will produce safer and more readable
code.

Someone may argue that this change is not needed because C++ lets you
implement arrays as templates.  In fact, the opposite is true.  This
change makes array templates truely safe by allowing the array template
to return the address of an object, on which the user will not be able
to perform pointer arithmetic.

One other point worth noting is that one can now dispense with the
unpleasant "delete[]" notation.  "delete" will now know whether or not
it is deleting an array based on type.

    1.  simple to use               -1
    2.  simple to implement         -2
    3.  predictable                 +7
    4.  controllable                0
    5.  powerful                    0

-------------------user defined conversions------------------------

Another area of abiguity and lack of control is with implicit type
conversions.  C++ rules for user defined type conversions are too
automatic and arbitrary.  The user should have control over whether the
conversions he defines are implicit or explicit (requiring a cast or
contructor).  This could be done with a key word like "implicit".  So,
for example:

    class A {
    public:
        A(char*);
        implicit A(int);
        implicit operator char*() const;
        operator int() const;
    };

    void fn1(const A);
    void fn2(char*);
    void fn3(int);

    fn1("abc");     // compiler error
    fn1(9);         // ok
    fn2(A("z"));    // ok
    fn3(A("z"));    // compiler error

I should add that if an implicit conversion is a template function, the
function should be instantiated automaticly (as now in C++).

    1.  simple to use               -1
    2.  simple to implement         -1 (but compiles will be faster)
    3.  predictable                 +8
    4.  controllable                +9
    5.  powerful                    0

-----------------non-member conversions----------------------------

C++ requires conversion operators to be member functions.  This is
another pointless restriction.  Non-member conversion functions should
be allowed.  An example of where this may be useful would be to allow
the conversion between 2 string types from different libraries as in:

    // lib1
    class String1;

    // lib2
    class String2;

    // my code
    implicit operator String1(String2 s) { return String1((char*)s); }
    implicit operator String2(String1 s) { return String2((char*)s); }

    1.  simple to use               0
    2.  simple to implement         -1
    3.  predictable                 0
    4.  controllable                +3
    5.  powerful                    +3

-------------------------base types--------------------------------

It would be nice if one could use any type, not just classes, as the
base for a class.  For example:

    template <class T> class handle : public T* {
        handle(T* p=NULL) : T*(p) {
            if( *this )  ++(*this)->count;
        }
        ~handle() {
            if( *this && --(*this)->count == 0 )
                delete *this;
        }
    }

    1.  simple to use               -1
    2.  simple to implement         -3
    3.  predictable                 0
    4.  controllable                0
    5.  powerful                    +6

-----------------------require extern--------------------------------

What does this declaration mean:

    a b(c);

"a" must be a class.  "b" can be either a function or an object
depending on "c".  If "c" is a class, "b" is a function.  If "c" is an
object, "b" is an object.  To fix this kind of ambiguity, C++ should
require the word "extern" for function declarations.  So the above must
be an object.  To declare "b" a function, do:

    extern a b(c);

This also means that "a b();" would be a declaration for an object, not
a function.

    1.  simple to use               0
    2.  simple to implement         +3
    3.  predictable                 +4
    4.  controllable                0
    5.  powerful                    -0.01

------------------------address of object function----------------

This idea comes from Bill Daly:

It would be useful to have a "specific pointer to member function" in
C++.  This could be done with something like:

     struct A {
          //...
          void fn();
          //...
     };

     A a;
     void (.*spmf)() = &a.fn;
     // Implemented as a two-element struct containing a pointer to a
     // and a pointer to fn. Note that it is not necessary to handle
     // virtual functions in a special way.

     spmf();
     // Calls a.fn(). Note that it is not necessary to know the type
     // of a in order to do this.

This would be useful for all sorts of callback functions, as well as
allowing the programmer to check for vtable corruption.

    1.  simple to use               -1
    2.  simple to implement         -2
    3.  predictable                 0
    4.  controllable                +4
    5.  powerful                    +5

-------------------bad type resolution------------------------------

Consider the following program:

    #include <iostream.h>
    class A {};
    inline void fn(void*)   { cout << "not ok\n"; }
    inline void fn(A*)      { cout << "ok\n"; }
    class B;
    inline void fn2(B* p)   { fn(p); }
    class B : public A {};

    main() {
        B b;
        fn2(&b);
        return 0;
    }

This program will print out "not ok".  The problem is that C++ resolves
B* to void* rather than A*.  Converions of not fully declared types
should be banned.

    1.  simple to use               0
    2.  simple to implement         0
    3.  predictable                 +3
    4.  controllable                0
    5.  powerful                    0

I would also suggest allowing declarations like:

    class B : public A;

This could be useful for classes that refer to each other.

    1.  simple to use               0
    2.  simple to implement         -1
    3.  predictable                 0
    4.  controllable                +2
    5.  powerful                    +1

--------------------initializing arrays-----------------------------

It would be nice to be able to initialize arrays:

    struct C {
        C(int,int) {}
    };

    C a1[3] = { C(1,1), C(2,2), C(3,3) };   // works with IBM C++
    C a2[3](1,2);   // construct all elements with (1,2)
    new C[3](1,2);  // construct all elements with (1,2)

    1.  simple to use               -1
    2.  simple to implement         -1
    3.  predictable                 0
    4.  controllable                +5
    5.  powerful                    +3

----------------------------renew-----------------------------------

"new" is like "malloc", and "delete" is like "free", but what is like
"realloc"?  I suggest "renew".

    A[*] p = new A[3];
    p = renew p[5];     // reconstruct first 3, construct 2 more
    p = renew p[2];     // reconstruct first 2, destruct last 3

The relevent operators are:

    struct A {
        ~~A(const A* old) {}    // reconstructor
        void* operator renew[](size_t,void*);
    };

The reconstructor is a lot like a copy constructor.

    1.  simple to use               -1
    2.  simple to implement         -1
    3.  predictable                 0
    4.  controllable                +4
    5.  powerful                    +4

---------------inherit (use) non-inherited fns---------------------

Ctors don't inherit.  This is usually what is wanted, but not always.
Once again, C++ fails to give the programmer control.  The obvious way
to give the programmer control would be with the namespace features as
in:

    class A {
    public:
        A(int);
        A& operator=(const A&);
    };

    class B : public A {
    public:
        using A::A;             // inherit constructor
        using A::operator=;
        // ...
    };

Obviously, an error should be generated where this can't work, like if
B has a member that needs to be constructed with parameters.  I will
now give an example where the above feature is essential:

Suppose you wanted to add a certain function to lots of classes.  You
might do it with a template as follows:

    template <class T> AddDup : public T {
    public:
        T* Dup()    { return new AddDup<T>(*this); }
    };

The idea is that now you could easily add the Dup fn to many classes
by using this template.  But there is a problem; the constructor and
operator= are not inherited.  And I can't see any way around this
problem, so this technique simply won't work.  You will have to add
the Dup fn to each class by explicit derivation.  But with the above
suggestion, this problem could be solved with:

    template <class T> AddDup : public T {
    public:
        T* Dup()    { return new AddDup<T>(*this); }
        using T::T;             // inherit constructor
        using T::operator=;
    };

    1.  simple to use               -1
    2.  simple to implement         -1
    3.  predictable                 0
    4.  controllable                +6
    5.  powerful                    +2

---------------------------dont_inherit-----------------------------

A related problem is when you don't want to inherit a class fn.  I
would like a key word like "dont_inherit" to handle this.  Here is an
example:

    class A {
    public:
        dont_inherit virtual int Size()    { return sizeof(*this); }
    };

When Size() gets inherited, it returns the wrong result.  Fns declared
"dont_inherit virtual" would force its redefinition in each derived
class.  Another example is an Init fn.  I don't like constructors for
various reasons, so I often use an Init() fn instead.  I would prefer
if this weren't inherited.  See the following example

    class A {
    public:
        dont_inherit void Init(int i) { iA = i; }
    protected:
        int iA;
    };

    class B : public A {
        // oops, forgot Init for B
        // should be:
        // dont_inherit void Init(int i1,int i2) { A::Init(i1); iB = i2; }
    protected:
        int iB;
    };

I don't want Init() inherited for the same reasons I usually don't
want constructors inherited.  Since Init() isn't virtual, a
re-definition shouldn't be forced, but an object of type B shouldn't
have access to Init().

This is a good example of where C++ fails to give the programmer
control.  C++ arbritrarily decided that some fns should inherit and
others not.  This control should be given to the programmer.

    1.  simple to use               -1
    2.  simple to implement         -1
    3.  predictable                 +3
    4.  controllable                +7
    5.  powerful                    +2

---------------------order of construction-------------------------

Here as another example of C++ being too automatic and not giving the
programmer enough control.  Consider this

    class C {};

    class A {
    public:
        A(C* p);
    };

    class B : public A {
    public:
        B() : c(), A(&c) {} // trouble
        C c;
    };

This is valid C++ code.  I think that listing constructors in a
different order from the real order of construction should generate an
error.  Forcing programmers to list things in the right order makes
the code clearer and less error prone.

I would also give the programmer more control over the order of
construction by allowing one "order" statement in a class to specify
the order of construction as in

    class B : public A {
    public:
        order c, B;
        B() : c(), A(&c) {} // ok
        C c;
    };

    1.  simple to use               -1
    2.  simple to implement         -1
    3.  predictable                 +4
    4.  controllable                +7
    5.  powerful                    +2

-------------------------constructor design------------------------

C++ constructors are poorly designed.  They are unnecesarily complex
and confusing.  In particular, it is annoying that one can't do
calculations before construction.  Here is an example of this problem.
Suppose I have a GUI class library which has the following

    class frame {
    public:
        frame();
        int Dx();       // width
        int Dy();       // height
    };

    class button {
    public:
        button( char *text, int x, int y, int dx, int dy );
    }

Now I want to make a screen containing 4 buttons, one in each
quadrant.  In all the sample code I have seen, this kind of thing is
done as follow:

    class myFrame : public frame {
    public:
        myFrame();
        ~myFrame();
    protected:
        button *upper_left;
        button *upper_right;
        button *lower_left;
        button *lower_right;
    };

    myFrame::myFrame {
        const int dx = 100;
        const int dy = 30;
        int x1 = (Dx()/2 - dx)/2;
        int x2 = Dx()/2 + x1;
        int y1 = (Dy()/2 - dy)/2;
        int y2 = Dy()/2 + y1;
        upper_left = new button("upper left",x1,y1,dx,dy);
        upper_right = new button("upper right",x2,y1,dx,dy);
        lower_left = new button("lower left",x1,y2,dx,dy);
        lower_right = new button("lower right",x2,y2,dx,dy);
    }

    myFrame::~myFrame() {
        delete upper_left;
        delete upper_right;
        delete lower_right;
        delete lower_right;
    }

This ugly code is standard practice today.  It is ugly for several
reasons.  First, if an exception occurs in the constructor, there is
no reasonable way to unravel things (delete the right things).  And
second, these buttons really do belong to this frame, so conceptually
they should be members.  In other words, the declaration should be

    class myFrame : public frame {
    public:
        myFrame();
    protected:
        button upper_left;
        button upper_right;
        button lower_left;
        button lower_right;
    };

This is much cleaner, more efficient, and less error prone.  So why
don't people do it this way?  The reason, whether they are aware of it
or not, is that people are avoiding the use of C++ constructors.  Go
ahead and try to write the constructor for myFrame in the above
example, and you will see what I mean.

You are probably so used to C++ constructors that you take their
syntax for granted.  But if you step back and think about C++
constructors in comparison to other C constructs, I think you will
find that they are quite ugly and inconsistent with the rest of C.
They should have been designed to be as consistent as possible with
other C++ fns.  There is really only one significant thing that is
special about constructors, and that is that they are responsible for
constructing their base classes and members.  Instead of using the
current complex syntax of ctors to do this which looks like

    myFrame::myFrame
        : upper_left(...)
        , upper_right(...)
        , lower_left(...)
        , lower_right(...)
    {}

a simple "construct" statement, to be used only in constructors would
have sufficed.  So in the above example, the ctor for myFrame would
look like

    myFrame::myFrame {
        construct frame();
        const int dx = 100;
        const int dy = 30;
        int x1 = (Dx()/2 - dx)/2;
        int x2 = Dx()/2 + x1;
        int y1 = (Dy()/2 - dy)/2;
        int y2 = Dy()/2 + y1;
        construct upper_left("upper left",x1,y1,dx,dy);
        construct upper_right("upper right",x2,y1,dx,dy);
        construct lower_left("lower left",x1,y2,dx,dy);
        construct lower_right("lower right",x2,y2,dx,dy);
    }

Just like now, construction would be once per object.  And the order
of construction should be enforced as discussed above in the section
"order of contruction".

Yes I know, this is a big change for C++.  But constructors are a big
problem.  Please consider the example I presented here carefully and
think about the alternatives.  This is not esoteric stuff.  This is
every day programming.  It must be fixed.  If not, all code will look
like the first myFrame() ctor listed here.  I suggest offering the new
syntax and then gradually phasing out the old syntax.  This is the way
to make big changes acceptable.

    1.  simple to use               +1
    2.  simple to implement         +1
    3.  predictable                 0
    4.  controllable                +9
    5.  powerful                    +2

--------------no default assignment operator------------------------

Default assignment operators are another unwanted favor C++ does for
programmers.  They should be generated for simple structs only, for C
compatibility.  For other classes, a "default_copy()" fn should be
generated if refered to so that the programmer can easily write his
own fns as in

    class A {
    public:
    //  A& default_copy(const A&);  compiler generated
        A& operator=(const A& a)    { return default_copy(a); }
    };

    1.  simple to use               0
    2.  simple to implement         0
    3.  predictable                 +5
    4.  controllable                0
    5.  powerful                    -1

----------template parameters and base class members----------------

The following program fails to compile:

    class A {
        int i;  // private
    };

    template <int i> class B : public A {
        int fn()    { return i; }
    };

    void main() {
        B<3> b;
        b.fn();
    }

This is because the C++ standard says:

    A name from a base class can hide the name of a template-parameter.

Bad decision.  The template parameter should take precedence.

    1.  simple to use               0
    2.  simple to implement         0
    3.  predictable                 +3
    4.  controllable                +1
    5.  powerful                    0

----------------------exceptions-------------------------------------

I have read Stroustrup's books, but I still fail to see the point of
exceptions.  Until someone can enighten me, I think exceptions should be
eliminated.  I think code written that makes wide use of exceptions will
be a nightmare, totally unreliable.

    1.  simple to use               +1
    2.  simple to implement         +4
    3.  predictable                 +3
    4.  controllable                -2
    5.  powerful                    -4

------------------------preprocessing-------------------------------

Everyone agrees that the preprocessor, Cpp, has got to go.  The question
is what to replace it with.  I think the C++ designers would like to see
macros eliminated.  I strongly disagree with this.  I would like to see
macros incorporated directly into C++.  The macros would not operate
directly on text, as they do now in the preprocesser.  Rather, they
would be handled during parsing, operating on the post lexical analysis
stream that the parser sees.  Furthermore, macros should respect scope
rules.  A macro should be able to be local to a namespace, fn, class, or
any other scope.  The rules for macros could also use various minor
improvements to avoid unintentional results.  This could make macros
quite safe and very useful.  To those opposed to macros, I ask for the
name of one major commercial class library that doesn't use them.

Other Cpp features like #ifdef and #include should also be included in
C++.  #ifdef could also be made more useful if it were able to ask
questions about what is defined within a particular scope.  #include
should do exactly what it does now, not modified as suggested by
Stroustrup in The Design and Evolution of C++.  Stroustrup's 3
suggestions lead to various problems, mostly relating to taking away
control from the programmer.  His goals can be accomplished much more
cleanly with my above suggestion about macros respecting scope.  If a
programmer designs his include files in a reasonable way, putting
#defines in namespaces, then the compiler should be able to precompile
headers effectively, cutting compile times dramaticly.

Here are a few examples:

    void fn(int* ptr) {
        #define ref (*ptr)
        ref += 9;
    }

    template <class T> class Ptr {
    public:
        #ifdef T::operator->
            T* operator->() const { return op; }
        #endif
        // ...
    protected:
        T* p;
    };

    template <class T1,class T2> class C {};
    #define static_type_info(T) T::info()
    static_type_info(C<int,int>)    // ok, comma is no problem

I used Cpp syntax, but this syntax could obviously be improved.

    1.  simple to use               0
    2.  simple to implement         -5
    3.  predictable                 +7
    4.  controllable                +7
    5.  powerful                    +5

----------------------------the end--------------------------------






Author: franklin@millenium.texas.net (Frank Schmidt)
Date: 1995/04/08
Raw View
Robert Coie (rac@intrigue.com) wrote:
: In article <3m4hah$nca@empire.texas.net>, franklin@millenium.texas.net
: (Frank Schmidt) wrote in response to article <3m3aci$2r8@silver.jba.co.uk>
: by JdeBP%utopium@jba.co.uk:

: Frank:
: : : : Construction of virtual base classes now happens at the highest class.
: : : : This is a mistake.  It is particularly annoying when developing a class
: : : : library where I want to use virtual base classes to implement the
: : : : library, but I don't want to bother users of the library.  The rule
: : : : should be that the virtual base class should be constructed at the
: : : : lowest level that contains all references to the virtual base class.

: JdeBP:
: : : Except that once someone comes along and uses your class library you are in
: : : big trouble with this rule.
: :
: : : Consider the case when they inherit from one of your leaf classes.  In your
: : : library, those classes are "the lowest level that contains all references
: : : to the virtual base class".  But when someone multiply inherits from two of
: : : your leaf classes they suddenly aren't any more, and by your rule the
: : : person implementing the new, inherited, class should now call the virtual
: : : base class constructors.
: :
: : : Except that your library classes are also calling the virtual base class
: : : constructors as well ...
: :
: : : Nightmare city.

: Frank:
: : Exactly the same problem arises with C++ as it is currently designed.  If a
: : library contains a concrete class derived from a virtual base class, it must
: : construct that virtual base class.  If a user derives a class from this
: : concrete class, he too must construct the virtual base class.  The solution
: : is that the compiler just picks which constructor of the virtual base class
: : to use for each class.  C++ now does this one way, and I suggested another
: : way.  Please "Know Your Language" ;)

: There's a big difference.  When a user derives a class from two leafs that
: share a common virtual base, that user _can_ construct the virtual base,
: and therefore insure that it only gets constructed once.  How does your
: solution guarantee that the constructor for the virtual base does not get
: called multiple times without having to modify the source for the leaf
: classes?

To repeat from above: "The rule should be that the virtual base class should
be constructed at the lowest level that contains all references to the
virtual base class."  Does this answer your question?


: : : : Default assignment operators are another unwanted favor C++ does for
: : : : programmers.  They should be generated for simple structs only, for C
: : : : compatibility.  For other classes, a "default_copy()" fn should be
: : : : generated if refered to so that the programmer can easily write his
: : : : own fns as in
: :
: : : Let me get this straight.  You don't like the compiler generating a default
: : : shallow copy version of operator= for you, and yet you want it to generate
: : : a default shallow copy function for you, just with a different name ?
: :
: : : Why ?  If you don't like shallow copy, you implement your own operator=.
: : : If you do like shallow copy (and are lazy), you let the compiler implement
: : : it for you.  What's the problem ?
: :
: : The problem isn't that the compiler generates the code for the shallow copy,
: : it is that it also applies this code automaticly.  I want to explicitly
: : control when the compiler generates this code and when it gets applied.

: Wouldn't supplying an operator= for each class you make solve this
: problem?  I am unclear how muddying the syntax gives you any additional
: control.

Yes it would.  Do you think it is good language design to force the
programmer to add code to prevent unwanted favors?





Author: pstemari@erinet.com (Paul J. Ste. Marie)
Date: 1995/04/08
Raw View
In article <3m2s2q$ic6@empire.texas.net>,
   franklin@millenium.texas.net (Frank Schmidt) wrote:

 [...]

:In my opinion a good programming language (or any other
:technology) has the following characteristics:
:
:    1.  simple to use
:    2.  simple to implement
:    3.  predictable
:    4.  controllable
:    5.  powerful

 [...]

:-----------------function templates-------------------------------
:
:Function templates should not resolve automaticly (except for
:implicit conversions, discussed later).  Explicit specification of
:template arguments should be required.  This would prevent C++
:from being too automatic and to give the programmer more explicit
:control over how fn calls get resolved.

Except this makes the templates harder to use, not simpler, and it
prevents writing generic algorithms in terms of other lower-level
algorithms.  For example,

template <class T>
T FFT2D(const T& array)
{
 // stuff involving FFT(array[n])
}

would need to know the type of the contents of T, as well as T
itself.

 [...]

:--------------------------remove references-----------------------
:
:Most uses of references should be removed from C++.  They are an
:unnecessary complication.  As far as I can tell, there is only one
:valid reason to have references, and this is for functions that
:need to be able to return by reference.

And yet you think Pascal is more usable?  Pascal has var parameters
and pointers, which is exactly the syntax you object to here.
Really the only reason to pass by pointer instead of by reference
is if you are either going to save the pointer for future use, or
perform arithmetic on it.

 [...]

:------------constructor for virtual base classes------------------
:
:Construction of virtual base classes now happens at the highest
:class.  This is a mistake.  It is particularly annoying when
:developing a class library where I want to use virtual base
:classes to implement the library, but I don't want to bother users
:of the library.  The rule should be that the virtual base class
:should be constructed at the lowest level that contains all
:references to the virtual base class.

This might actually be doable.  I assume what's meant here that in
the hierarchy

class A;
class B : public virtual A;
class C : public virtual A;
class D : public B, public C;
class E : public D;
class F : public E;

that E and F wouldn't have to construct A, but rather D's
construction would be used.  Since the compiler has to figure out
to bypass B and C's construction of A already, I don't see where
this would be a big deal,

:-------------------------~virtual---------------------------------
:
:One should be able to say that a virtual base class is no longer
:virtual as follow:
:
:    class A {};
:    class B : public virtual A {};
:    class C : public virtual A {};
:    class D : public B, public C, public ~virtual A {};
:
:Now if I have
:
:    class E : public B, public C, public D {};
:
:The class E will contain 2 A's, one for B and C, and one for D.

This is already available--make the declaration for D look like:

    class D : public B, public C, public A {};

and you get the behavior you want.

:Further more, a cast from A* to D* is now ok because A is not a
:virtual base class of D, it is a regular base class.

It's NOT ok, since D contains two instances of A.  Is the A you're
casting from the one used in A & B, or the second one used only in
D?

 [...]

:-------------------array vs pointer------------------------------
:
:C made a minor mistake in not differenciating between a pointer to
:a type and a pointer to an array of a type.  This has become a
:major mistake in C++.  Consider the following:

 [...]

The headache here is that the type A[] decays into A* via &A[0].
What you're describing is not a pointer to array, which already
exists (A (*foo)[]), but removing the decay functionality.  This
fixes some stuff, but breaks much more.

:-------------------user defined conversions----------------------
:
:Another area of abiguity and lack of control is with implicit type
:conversions.  C++ rules for user defined type conversions are too
:automatic and arbitrary.  The user should have control over
:whether the conversions he defines are implicit or explicit
:(requiring a cast or contructor).  This could be done with a key
:word like "implicit".  So, for example:

I believe the committee accepted the explicit keyword for ctor
conversions, requiring explicit use of the ctor.  Aside from that,
if you want a conversion to X to be explicit, use a regular member
function.  There is nothing "arbitrary" going on here, the language
simply allows you to specify the behavior you want.

The only "arbitrary" restriction is disallowing > 1 implicit
user-defined conversion, but IMHO that's a good thing.

 [...]

:-----------------non-member conversions--------------------------
:
:C++ requires conversion operators to be member functions.  This is
:another pointless restriction.  Non-member conversion functions
:should be allowed.  An example of where this may be useful would
:be to allow the conversion between 2 string types from different
:libraries as in:

 [...]

It also allows for some heavy redefinition of what the semantics of
a class are on the fly.  I don't see how you can dislike
overloading and like this idea at the same time.  This can be done
quite easily enough using explicit conversion functions.

:-------------------------base types-----------------------------
:
:It would be nice if one could use any type, not just classes, as
:the base for a class.  For example:

 [...]

This opens a major can o' worms, once you consider the interaction
between this and all the automatic conversions that take place
between int's and unsigned's and long's and char's and float's and
double's.  It's a pain in the neck to make a class that behaves
like a base type, for example an int, but at least you've got
control over exactly how it does so, and a lot of the conversions
are highly dependent on the class involved.  For example,

class MyInt : int {};

MyInt m = 5;
int i = 1;

what's the type of (m + i)?  Does i promote to a MyInt?  Does MyInt
demote to an int?  I've seen examples that should go either way.

:-----------------------require extern-----------------------------
:
:What does this declaration mean:
:
:    a b(c);
:
:"a" must be a class.  "b" can be either a function or an object
:depending on "c".  If "c" is a class, "b" is a function.  If "c"
:is an object, "b" is an object.  To fix this kind of ambiguity,
:C++ should require the word "extern" for function declarations.
:So the above must be an object.  To declare "b" a function, do:

Syntax sugar.  Most of us can distinguish a list of declarations
from a list of expressions without much difficulty.  In the real
world, you'd see:

Type func(Type2 arg);
Type obj(expr);

Those are not hard to distinguish.

:------------------------address of object function-------------
:
:This idea comes from Bill Daly:
:
:It would be useful to have a "specific pointer to member function"
:in C++.  This could be done with something like:

 [...]

This isn't a pointer to member function, but rather a bound pointer
to member function.  You can get what you want quite easily with a
template:

template <class obj, class returntype>
class BoundMemberFunctionPointer  {
    public:
        BoundMemberFunctionPointer(obj *o,
  returntype (obj::*mfp)()) : object(o),
       function(mfp) {}
 returntype operator()() {return (object->*function)();}
    private:
        obj *object;
 returntype (obj::*function)();
    };

This is not interchangable with a pointer to a function, but
anything that was would by necessity involve some sort of garbage
collector or a change in the sizeof a function pointer.

:-------------------bad type resolution---------------------------
:
 [...]

:The problem is that C++ resolves B* to void* rather than A*.
:Converions of not fully declared types should be banned.

This is frequently the desired behavior, esp with template
containers of pointers.

:I would also suggest allowing declarations like:
:
:    class B : public A;
:
:This could be useful for classes that refer to each other.

And solves nothing, since this does not give the compiler enough
information to convert a A* to a B* or vice-versa.  That conversion
cannot be performed without knowledge of the complete layout of B
and A.  Clue--derived/base pointer conversion involve applying an
offset.

:---------------inherit (use) non-inherited fns------------------
:
:Ctors don't inherit.  This is usually what is wanted, but not
:always.  Once again, C++ fails to give the programmer control.
:The obvious way to give the programmer control would be with the
:namespace features as in:

This is pure BS.  You've got the control--what you asking for is
more automation.  The problem with inheriting a ctor is that it
leaves all the other base classes uninitialized, and worse yet
fundemental types in your class are left as random values.  The
default ctor and op=, which is what you were asking for in the
example I clipped, are made automatically by composition, which is
quite different than inheritance.

Arguably ctors with signatures other than the standard copy ctor
and default ctor could be automatically constructed by composition,
but this would be taking control out of the hands of the programmer
and introduce all sorts of weird bugs.

:---------------------------dont_inherit---------------------------
:
:A related problem is when you don't want to inherit a class fn.  I
:would like a key word like "dont_inherit" to handle this.

This violates the substitution principle for inheritence.  Your
derived class can no longer be used where a base class is expected.
Using a pure virtual is one alternative, but this doesn't always
work.

 [good example removed--but I see the point]

:Another example is an Init fn.  I don't like constructors for
:various reasons, so I often use an Init() fn instead.  I would
:prefer if this weren't inherited. [...]
:
:I don't want Init() inherited for the same reasons I usually don't
:want constructors inherited.  Since Init() isn't virtual, a
:re-definition shouldn't be forced, but an object of type B
:shouldn't have access to Init().

So make Init private.  This sound like you should be using ctors.

:This is a good example of where C++ fails to give the programmer
:control.  C++ arbritrarily decided that some fns should inherit
:and others not.  This control should be given to the programmer.

No, it is a good example of why you should use ctors for
construction.

:---------------------order of construction-----------------------
[...]

:This is valid C++ code.  I think that listing constructors in a
:different order from the real order of construction should
:generate an error.  Forcing programmers to list things in the
:right order makes the code clearer and less error prone.

This just pure nuisance syntax sugar.

:I would also give the programmer more control over the order of
:construction by allowing one "order" statement in a class to
:specify the order of construction as in

[...]

Why?  This has already been stated once, by the order that the
elements and base classes appear in.

:-------------------------constructor design----------------------
:
:C++ constructors are poorly designed.  They are unnecesarily
:complex and confusing.  In particular, it is annoying that one
:can't do calculations before construction.

 [...]

:Instead of using the current complex syntax of ctors to do this
:which looks like
:
:    myFrame::myFrame
:        : upper_left(...)
:        , upper_right(...)
:        , lower_left(...)
:        , lower_right(...)
:    {}
:
:a simple "construct" statement, to be used only in constructors
:would have sufficed.  So in the above example, the ctor for
:myFrame would look like
:
:    myFrame::myFrame {
:        construct frame();
:        const int dx = 100;
:        const int dy = 30;
:        int x1 = (Dx()/2 - dx)/2;
:        int x2 = Dx()/2 + x1;
:        int y1 = (Dy()/2 - dy)/2;
:        int y2 = Dy()/2 + y1;
:        construct upper_left("upper left",x1,y1,dx,dy);
:        construct upper_right("upper right",x2,y1,dx,dy);
:        construct lower_left("lower left",x1,y2,dx,dy);
:        construct lower_right("lower right",x2,y2,dx,dy);
:    }

You have not come even close to making a case for this.  The
importance of the ctor syntax is that it guarantees that everything
is constructed before use.  The compiler would have to perform
special syntax checks in the code above to ensure that no use of a
member was made before the construct member statement.  The
arithmetic you are perfoming above could exist either in a base
class or just be placed as expressions in the various ctor calls.

:--------------no default assignment operator----------------------
:
:Default assignment operators are another unwanted favor C++ does
:for programmers.  They should be generated for simple structs
:only, for C compatibility.  For other classes, a "default_copy()"
:fn should be generated if refered to so that the programmer can
:easily write his own fns as in [...]

This is easy enough to suppress in those cases where it is a
problem, and the savings of the default_copy member function are
quite minimal.  In those cases where a default_copy was used and
you did want a op=, you would have to go through and undo the
effects of the default_copy anyway.

:----------template parameters and base class members--------------
:
 [example trimmed]

:This is because the C++ standard says:
:
:    A name from a base class can hide the name of a
:template-parameter.
:
:Bad decision.  The template parameter should take precedence.

Why?  Your example does't discuss this.

:----------------------exceptions----------------------------------
:
:I have read Stroustrup's books, but I still fail to see the point
:of exceptions.  Until someone can enighten me, I think exceptions
:should be eliminated.

Exceptions should be eliminated until you are enlightened?  Did
someone die and elect you Bill Gates?  ;)

:I think code written that makes wide use of exceptions will be a
:nightmare, totally unreliable.

Then don't use them.  In the meantime, exceptions provide a means
for software to fail gracefully, without having to pass and check
error codes through vast amounts of code that can do nothing about
the errors.

:------------------------preprocessing-----------------------------
:
:Everyone agrees that the preprocessor, Cpp, has got to go.

That's why the ANSI preprocessor is in place.  In general, however,
I agree with a lot of what you say about the preprocessor, although
I don't think that having it operate on the post lexical analysis
phase is desireable.  A big part of what makes the ANSI
preprocessor more useful is the ability to perform operations such
as stringizing and token pasting.

 [...]
:#include should do exactly what it does now, not modified as
:suggested by Stroustrup in The Design and Evolution of C++.
:Stroustrup's 3 suggestions lead to various problems, mostly
:relating to taking away control from the programmer.

A seperate import specification, unrelated to #include, would be
highly useful.  Using #include for getting at class definitions has
a number of well-recognized problems.

 [...]

:----------------------------the end-----------------------------

Finally <grin>










 --Paul J. Ste. Marie, pstemari@well.sf.ca.us, pstemari@erinet.com

The Financial Crimes Enforcement Network claims that they capture every
public posting that has their name ("FinCEN") in it.  I wish them good hunting.





Author: franklin@millenium.texas.net (Frank Schmidt)
Date: 1995/04/08
Raw View
Paul J. Ste. Marie (pstemari@erinet.com) wrote:
: In article <3m2s2q$ic6@empire.texas.net>,
:    franklin@millenium.texas.net (Frank Schmidt) wrote:

: :-----------------function templates-------------------------------
: :
: :Function templates should not resolve automaticly (except for
: :implicit conversions, discussed later).  Explicit specification of
: :template arguments should be required.  This would prevent C++
: :from being too automatic and to give the programmer more explicit
: :control over how fn calls get resolved.

: Except this makes the templates harder to use, not simpler, and it
: prevents writing generic algorithms in terms of other lower-level
: algorithms.  For example,

: template <class T>
: T FFT2D(const T& array)
: {
:  // stuff involving FFT(array[n])
: }

: would need to know the type of the contents of T, as well as T
: itself.

Sorry, I don't understand.  You would just use FFT<T>(array[n]), right?


: :--------------------------remove references-----------------------
: :
: :Most uses of references should be removed from C++.  They are an
: :unnecessary complication.  As far as I can tell, there is only one
: :valid reason to have references, and this is for functions that
: :need to be able to return by reference.

: And yet you think Pascal is more usable?  Pascal has var parameters
: and pointers, which is exactly the syntax you object to here.
: Really the only reason to pass by pointer instead of by reference
: is if you are either going to save the pointer for future use, or
: perform arithmetic on it.

I didn't give Pascal a 10 for this reason.  It is a big advantage to be
able to determine whether an argument passed to a fn may be modified,
and passing by pointers allows this.  A modified form of reference
passing could also make this possible.


: :------------constructor for virtual base classes------------------
: :
: :Construction of virtual base classes now happens at the highest
: :class.  This is a mistake.  It is particularly annoying when
: :developing a class library where I want to use virtual base
: :classes to implement the library, but I don't want to bother users
: :of the library.  The rule should be that the virtual base class
: :should be constructed at the lowest level that contains all
: :references to the virtual base class.

: This might actually be doable.  I assume what's meant here that in
: the hierarchy

: class A;
: class B : public virtual A;
: class C : public virtual A;
: class D : public B, public C;
: class E : public D;
: class F : public E;

: that E and F wouldn't have to construct A, but rather D's
: construction would be used.  Since the compiler has to figure out
: to bypass B and C's construction of A already, I don't see where
: this would be a big deal,

The big deal isn't for the compiler, but for the user of a commercial
class library.  Imagine that classes A through E are part of a library
and class F is defined by a user of that library.  Why should he have
to worry about constructing A?


: :-------------------------~virtual---------------------------------
: :
: :One should be able to say that a virtual base class is no longer
: :virtual as follow:
: :
: :    class A {};
: :    class B : public virtual A {};
: :    class C : public virtual A {};
: :    class D : public B, public C, public ~virtual A {};
: :
: :Now if I have
: :
: :    class E : public B, public C, public D {};
: :
: :The class E will contain 2 A's, one for B and C, and one for D.

: This is already available--make the declaration for D look like:

:     class D : public B, public C, public A {};

: and you get the behavior you want.

: :Further more, a cast from A* to D* is now ok because A is not a
: :virtual base class of D, it is a regular base class.

: It's NOT ok, since D contains two instances of A.  Is the A you're
: casting from the one used in A & B, or the second one used only in
: D?

My declaration "public ~virtual A" means that A is no longer a virtual
base class.  It does not mean to add an A to D as it does in your
example.  So there is only one A in D, and by saying that it is no
longer virtual, one can be confident in its relative offset to D,
making the cast possible.


: :-------------------array vs pointer------------------------------
: :
: :C made a minor mistake in not differenciating between a pointer to
: :a type and a pointer to an array of a type.  This has become a
: :major mistake in C++.  Consider the following:

:  [...]

: The headache here is that the type A[] decays into A* via &A[0].
: What you're describing is not a pointer to array, which already
: exists (A (*foo)[]), but removing the decay functionality.  This
: fixes some stuff, but breaks much more.

What?  (A (*foo)[]) is really a pointer to an array in a 2D array.  It
is not a simple pointer to an array.  (Try making a regular array and
assigning its address to foo.  You can't.)  And I do allow A[] to
"decay" into A*, I just don't let A* "decay" into A[].


: :-------------------user defined conversions----------------------
: :
: :Another area of abiguity and lack of control is with implicit type
: :conversions.  C++ rules for user defined type conversions are too
: :automatic and arbitrary.  The user should have control over
: :whether the conversions he defines are implicit or explicit
: :(requiring a cast or contructor).  This could be done with a key
: :word like "implicit".  So, for example:

: I believe the committee accepted the explicit keyword for ctor
: conversions, requiring explicit use of the ctor.  Aside from that,
: if you want a conversion to X to be explicit, use a regular member
: function.  There is nothing "arbitrary" going on here, the language
: simply allows you to specify the behavior you want.

I had heard something about this.  This is progress, but still, it is a
pity that the default is unwanted automatic behavior.  If "explicit"
cannot be used in member conversion fns, then C++ is effectively
preventing the overloading of cast, a senseless restriction.

: The only "arbitrary" restriction is disallowing > 1 implicit
: user-defined conversion, but IMHO that's a good thing.

Why?


: :-----------------non-member conversions--------------------------
: :
: :C++ requires conversion operators to be member functions.  This is
: :another pointless restriction.  Non-member conversion functions
: :should be allowed.  An example of where this may be useful would
: :be to allow the conversion between 2 string types from different
: :libraries as in:

:  [...]

: It also allows for some heavy redefinition of what the semantics of
: a class are on the fly.  I don't see how you can dislike
: overloading and like this idea at the same time.  This can be done
: quite easily enough using explicit conversion functions.

I don't dislike overloading.  I just dislike overloading that is done
without my prior approval.  In my example above with 2 string type from
different libraries, writing explicit conversion fns is the current
solution, but it is a pointless hassle.  And I view this restriction as
a typical example of the arbritrary restrictions C++ puts on
programmers.  The C++ philosophy seems to be: if the C++ designers like
a feature, they add it to C++ and make it automatic, thereby making it
painful for programmers to avoid the feature.  And if the C++ designers
don't like a feature, they just ban it.


: :-------------------------base types-----------------------------
: :
: :It would be nice if one could use any type, not just classes, as
: :the base for a class.  For example:

:  [...]

: This opens a major can o' worms, once you consider the interaction
: between this and all the automatic conversions that take place
: between int's and unsigned's and long's and char's and float's and
: double's.  It's a pain in the neck to make a class that behaves
: like a base type, for example an int, but at least you've got
: control over exactly how it does so, and a lot of the conversions
: are highly dependent on the class involved.  For example,

: class MyInt : int {};

: MyInt m = 5;
: int i = 1;

: what's the type of (m + i)?  Does i promote to a MyInt?  Does MyInt
: demote to an int?  I've seen examples that should go either way.

Obviously MyInt demotes to an int.  The current (reasonable) rule is
that demotions are automatic but promotions are not.


: :-----------------------require extern-----------------------------
: :
: :What does this declaration mean:
: :
: :    a b(c);
: :
: :"a" must be a class.  "b" can be either a function or an object
: :depending on "c".  If "c" is a class, "b" is a function.  If "c"
: :is an object, "b" is an object.  To fix this kind of ambiguity,
: :C++ should require the word "extern" for function declarations.
: :So the above must be an object.  To declare "b" a function, do:

: Syntax sugar.  Most of us can distinguish a list of declarations
: from a list of expressions without much difficulty.  In the real
: world, you'd see:

: Type func(Type2 arg);
: Type obj(expr);

: Those are not hard to distinguish.

I would call this "syntax fiber".  The syntactical rules of a language
should remove ambiguity where-ever possible.


: :------------------------address of object function-------------
: :
: :This idea comes from Bill Daly:
: :
: :It would be useful to have a "specific pointer to member function"
: :in C++.  This could be done with something like:

:  [...]

: This isn't a pointer to member function, but rather a bound pointer
: to member function.  You can get what you want quite easily with a
: template:

: template <class obj, class returntype>
: class BoundMemberFunctionPointer  {
:     public:
:         BoundMemberFunctionPointer(obj *o,
:   returntype (obj::*mfp)()) : object(o),
:        function(mfp) {}
:  returntype operator()() {return (object->*function)();}
:     private:
:         obj *object;
:  returntype (obj::*function)();
:     };

: This is not interchangable with a pointer to a function, but
: anything that was would by necessity involve some sort of garbage
: collector or a change in the sizeof a function pointer.

The problem here is that your example will not work with callbacks
since each BoundMemberFunctionPointer will be a different type.
Pointers to member functions don't have to be interchangable with
pointers to regular functions, but they should be interchangable with
other pointers to member functions regardless of the class to which the
function is a member.


: :-------------------bad type resolution---------------------------
: :
:  [...]

: :The problem is that C++ resolves B* to void* rather than A*.
: :Converions of not fully declared types should be banned.

: This is frequently the desired behavior, esp with template
: containers of pointers.

: :I would also suggest allowing declarations like:
: :
: :    class B : public A;
: :
: :This could be useful for classes that refer to each other.

: And solves nothing, since this does not give the compiler enough
: information to convert a A* to a B* or vice-versa.  That conversion
: cannot be performed without knowledge of the complete layout of B
: and A.  Clue--derived/base pointer conversion involve applying an
: offset.

The structure of A is needed, but the structure of B is not since base
classes always come before members in the layout.  Therefor knowing the
structure of A is sufficient to perform the conversion.


: :---------------inherit (use) non-inherited fns------------------
: :
: :Ctors don't inherit.  This is usually what is wanted, but not
: :always.  Once again, C++ fails to give the programmer control.
: :The obvious way to give the programmer control would be with the
: :namespace features as in:

: This is pure BS.  You've got the control--what you asking for is
: more automation.  The problem with inheriting a ctor is that it
: leaves all the other base classes uninitialized, and worse yet
: fundemental types in your class are left as random values.  The
: default ctor and op=, which is what you were asking for in the
: example I clipped, are made automatically by composition, which is
: quite different than inheritance.

Calm down.  Your thinking is becoming clouded.  "Automation" means that
something happens automaticly, without it being explicitly requested.
"Control" means that one can specify exactly what happens.  Clearly my
request is for control, not automation.  As for leaving other base
classes uninitialized, to repeat my original post: "Obviously, an error
should be generated where this can't work, like if B has a member that
needs to be constructed with parameters."

: Arguably ctors with signatures other than the standard copy ctor
: and default ctor could be automatically constructed by composition,
: but this would be taking control out of the hands of the programmer
: and introduce all sorts of weird bugs.

Please, not automaticly.


: :---------------------------dont_inherit---------------------------
: :
: :A related problem is when you don't want to inherit a class fn.  I
: :would like a key word like "dont_inherit" to handle this.

: This violates the substitution principle for inheritence.  Your
: derived class can no longer be used where a base class is expected.
: Using a pure virtual is one alternative, but this doesn't always
: work.

:  [good example removed--but I see the point]

I guess I favor violating the "substitution principle" in some cases.

: :Another example is an Init fn.  I don't like constructors for
: :various reasons, so I often use an Init() fn instead.  I would
: :prefer if this weren't inherited. [...]
: :
: :I don't want Init() inherited for the same reasons I usually don't
: :want constructors inherited.  Since Init() isn't virtual, a
: :re-definition shouldn't be forced, but an object of type B
: :shouldn't have access to Init().

: So make Init private.  This sound like you should be using ctors.

: :This is a good example of where C++ fails to give the programmer
: :control.  C++ arbritrarily decided that some fns should inherit
: :and others not.  This control should be given to the programmer.

: No, it is a good example of why you should use ctors for
: construction.

See my criticism of ctors to understand why I use a public Init
function.


: :---------------------order of construction-----------------------
: [...]

: :This is valid C++ code.  I think that listing constructors in a
: :different order from the real order of construction should
: :generate an error.  Forcing programmers to list things in the
: :right order makes the code clearer and less error prone.

: This just pure nuisance syntax sugar.

: :I would also give the programmer more control over the order of
: :construction by allowing one "order" statement in a class to
: :specify the order of construction as in

: [...]

: Why?  This has already been stated once, by the order that the
: elements and base classes appear in.

Did you think about my example?  Here it is again:

    class C {};

    class A {
    public:
        A(C* p);
    };

    class B : public A {
    public:
        B() : c(), A(&c) {} // trouble
        C c;
    };

Are you really saying that this code should compile without generating
an error?  Clearly it is dangerous code.  A will be constructed first,
and will be passed the address of an unconstructed C.  My "order by"
statement would allow the programmer to fix the problem.  It would not
be a nuisance since it would be optional.


: :-------------------------constructor design----------------------
: :
: :C++ constructors are poorly designed.  They are unnecesarily
: :complex and confusing.  In particular, it is annoying that one
: :can't do calculations before construction.

:  [...]

: :Instead of using the current complex syntax of ctors to do this
: :which looks like
: :
: :    myFrame::myFrame
: :        : upper_left(...)
: :        , upper_right(...)
: :        , lower_left(...)
: :        , lower_right(...)
: :    {}
: :
: :a simple "construct" statement, to be used only in constructors
: :would have sufficed.  So in the above example, the ctor for
: :myFrame would look like
: :
: :    myFrame::myFrame {
: :        construct frame();
: :        const int dx = 100;
: :        const int dy = 30;
: :        int x1 = (Dx()/2 - dx)/2;
: :        int x2 = Dx()/2 + x1;
: :        int y1 = (Dy()/2 - dy)/2;
: :        int y2 = Dy()/2 + y1;
: :        construct upper_left("upper left",x1,y1,dx,dy);
: :        construct upper_right("upper right",x2,y1,dx,dy);
: :        construct lower_left("lower left",x1,y2,dx,dy);
: :        construct lower_right("lower right",x2,y2,dx,dy);
: :    }

: You have not come even close to making a case for this.  The
: importance of the ctor syntax is that it guarantees that everything
: is constructed before use.  The compiler would have to perform
: special syntax checks in the code above to ensure that no use of a
: member was made before the construct member statement.  The
: arithmetic you are perfoming above could exist either in a base
: class or just be placed as expressions in the various ctor calls.

C++ does not guarantee that everything is constructed before use.  See
the previous example.  On the other hand my syntax does guarantee this.
And I would not need special syntax checks, just standard use of the
symbol table, adding constucted members to the table as they are
constructed in a ctor fn.  And please, show me how you would perform
the arithmetic using standard C++.


: :--------------no default assignment operator----------------------
: :
: :Default assignment operators are another unwanted favor C++ does
: :for programmers.  They should be generated for simple structs
: :only, for C compatibility.  For other classes, a "default_copy()"
: :fn should be generated if refered to so that the programmer can
: :easily write his own fns as in [...]

: This is easy enough to suppress in those cases where it is a
: problem, and the savings of the default_copy member function are
: quite minimal.  In those cases where a default_copy was used and
: you did want a op=, you would have to go through and undo the
: effects of the default_copy anyway.

I am just complaining about over-automation here.  Programming should
consist of causing things to happen, not suppressing unwanted favors.


: :----------template parameters and base class members--------------
: :
:  [example trimmed]

: :This is because the C++ standard says:
: :
: :    A name from a base class can hide the name of a
: :template-parameter.
: :
: :Bad decision.  The template parameter should take precedence.

: Why?  Your example does't discuss this.

I thought it was self evident.  Please look at my example again and
tell me how you think it should be interpreted.


: :----------------------exceptions----------------------------------
: :
: :I have read Stroustrup's books, but I still fail to see the point
: :of exceptions.  Until someone can enighten me, I think exceptions
: :should be eliminated.

: Exceptions should be eliminated until you are enlightened?  Did
: someone die and elect you Bill Gates?  ;)

I have my opinions.  Unlike Bill Gates, my opinions have no effect on
the world at all.

: :I think code written that makes wide use of exceptions will be a
: :nightmare, totally unreliable.

: Then don't use them.  In the meantime, exceptions provide a means
: for software to fail gracefully, without having to pass and check
: error codes through vast amounts of code that can do nothing about
: the errors.

A reasonable position.  My only complaint is the added complexity of
the language, particularly for compiler writers.


: :------------------------preprocessing-----------------------------
: :
: :Everyone agrees that the preprocessor, Cpp, has got to go.

: That's why the ANSI preprocessor is in place.  In general, however,
: I agree with a lot of what you say about the preprocessor, although
: I don't think that having it operate on the post lexical analysis
: phase is desireable.  A big part of what makes the ANSI
: preprocessor more useful is the ability to perform operations such
: as stringizing and token pasting.

What is the ANSI preprocessor?  Is it different from Cpp?

: :----------------------------the end-----------------------------





Author: rac@intrigue.com (Robert Coie)
Date: 1995/04/08
Raw View
In article <3m4hah$nca@empire.texas.net>, franklin@millenium.texas.net
(Frank Schmidt) wrote in response to article <3m3aci$2r8@silver.jba.co.uk>
by JdeBP%utopium@jba.co.uk:

Frank:
: : : Construction of virtual base classes now happens at the highest class.
: : : This is a mistake.  It is particularly annoying when developing a class
: : : library where I want to use virtual base classes to implement the
: : : library, but I don't want to bother users of the library.  The rule
: : : should be that the virtual base class should be constructed at the
: : : lowest level that contains all references to the virtual base class.

JdeBP:
: : Except that once someone comes along and uses your class library you are in
: : big trouble with this rule.
:
: : Consider the case when they inherit from one of your leaf classes.  In your
: : library, those classes are "the lowest level that contains all references
: : to the virtual base class".  But when someone multiply inherits from two of
: : your leaf classes they suddenly aren't any more, and by your rule the
: : person implementing the new, inherited, class should now call the virtual
: : base class constructors.
:
: : Except that your library classes are also calling the virtual base class
: : constructors as well ...
:
: : Nightmare city.

Frank:
: Exactly the same problem arises with C++ as it is currently designed.  If a
: library contains a concrete class derived from a virtual base class, it must
: construct that virtual base class.  If a user derives a class from this
: concrete class, he too must construct the virtual base class.  The solution
: is that the compiler just picks which constructor of the virtual base class
: to use for each class.  C++ now does this one way, and I suggested another
: way.  Please "Know Your Language" ;)

There's a big difference.  When a user derives a class from two leafs that
share a common virtual base, that user _can_ construct the virtual base,
and therefore insure that it only gets constructed once.  How does your
solution guarantee that the constructor for the virtual base does not get
called multiple times without having to modify the source for the leaf
classes?

: : : Default assignment operators are another unwanted favor C++ does for
: : : programmers.  They should be generated for simple structs only, for C
: : : compatibility.  For other classes, a "default_copy()" fn should be
: : : generated if refered to so that the programmer can easily write his
: : : own fns as in
:
: : Let me get this straight.  You don't like the compiler generating a default
: : shallow copy version of operator= for you, and yet you want it to generate
: : a default shallow copy function for you, just with a different name ?
:
: : Why ?  If you don't like shallow copy, you implement your own operator=.
: : If you do like shallow copy (and are lazy), you let the compiler implement
: : it for you.  What's the problem ?
:
: The problem isn't that the compiler generates the code for the shallow copy,
: it is that it also applies this code automaticly.  I want to explicitly
: control when the compiler generates this code and when it gets applied.

Wouldn't supplying an operator= for each class you make solve this
problem?  I am unclear how muddying the syntax gives you any additional
control.

Robert Coie                              rac@intrigue.com
Implementor, Intrigue Corporation     AppleLink: INTRIGUE





Author: JdeBP@jba.co.uk (Jonathan de Boyne Pollard)
Date: 1995/04/07
Raw View
Frank Schmidt (franklin@millenium.texas.net) wrote:
: I thought I would post this file of my criticisms of C++ that I have
: acumulated.

And cue another long language war.

Oh well, here goes.

: 2.  simple to implement - Is it easy for the compiler writers to
: implement?  This affects the user in 2 ways; fewer compiler bugs and
: faster compile times.

Of course, the reverse side of this criterion is that simple to implement
languages are often simple languages.  i.e. not powerful/elaborate/rich.

It's also often surprising that the hard to implement languages are the
ones that attempt to hide the most from the user.  If you compare the size
of the average BASIC runtime to the size of the average Standard C Library,
and consider what each are doing, then you'll see what I'm getting at.

: 3.  predictable - Looking at a piece of code, how easy is it for you to
: predict what it will do?

Know Your Language.  It's that simple.

Be very careful of falling into the trap of blaming the language for the
fact that you don't understand it.

I don't understand Cyrillic, but that's not a valid criticism of Russian.

: 4.  controllable - Can you make the language/compiler do exactly what
: you want?  Do you feel in control?  Is the language flexible.

Compare this definition to :

: 5.  powerful - This is self explanatory, but could probably best be
: defined as meaning/char.  This is what feature happy languages stress at
: the expense of the previous 4 goals.

You haven't defined "powerful" adequately enough for *me* (when reading
your post) to understand what you are getting at, which is indicative of
the fact that you probably haven't defined it adequately enough for it to
be a useful criterion.

I don't know what anyone else reading your post may think.

: Here is is how I would rate some languages (from 0 to 10):

I disagree with your ratings, by the way.  Especially on the
"controllability" of C and C++.  I'd say that in that respect they were
neck and neck myself.

But let's not get into pointless ratings wars.

: Function templates should not resolve automaticly (except for implicit
: conversions, discussed later).

Why not ?  That effectively knobbles the entire concept.

If you don't want automatic instantiation of templates, the answer is very
easy : don't use templates and write plain overloaded functions instead.

: Most uses of references should be removed from C++.  They are an
: unnecessary complication.

Is this the "they're only pointers under the covers anyway" argument, or
the "we don't need anything other than pass-by-value/return-by-value"
argument ?

: 1.  void fn(T&);
:
: This construct adds nothing but confusion to the language.  In C, if I
: had:
:
:     int i = 8;
:     fn(i);
:
: I could be sure that the variable i was still 8.  But not with C++.

Ah, the "we don't need anything other than pass-by-value" argument.

The situation here is simply  that the contract between the function user
and the function implementor is extended to allow pass-by-modifiable-reference
semantics in addition to the old pass-by-value semantics.  Quite why allowing
such contracts to be formed is a *failing* of the language is beyond me.

In fact, people who suggest this should really be ashamed of their
blinkered viewpoint, because many other programmers writing in other
languages often have pass-by-modifiable-reference as the *default* for
function arguments and are quite able to use them.

Consider the number of MS BASIC programmers who have pass-by-modifiable-
-reference semantics by default, and yet seem to be perfectly happy about
that, and seem to be writing reasonably productive programs.

It's merely a question of learning the forms of contract that function
implementor and function user can form.  There's nothing inherently *wrong*
about pass-by-modifiable-reference.

In other words, Know Your Language.

: 2.  void fn(const T&);
:
: This does serve a useful purpose.  The idea here is that conceptually
: you want to pass a structure by value, but you don't want the compiler
: to bother making a copy of it.  So you use a reference.  But I think it
: is reasonable to say that the compiler should pass by reference if you
: have "void fn(const T)", and this solves this problem.

Which is nothing more than pass-by-reference without the explicit "this is
a reference, boys" syntax.  I'd say that that was *less* clear, not more.

: One other use of references is as a pointer guaranteed not to be null.
: A much better solution to this is to add a key word like "not_null" for
: pointers as in:
:
:     char *not_null s;
:     char * s2;
:     s2 = s;                 // ok
:     s = s2;                 // compiler error
:     s = not_null_cast(s2)   // ok

Bollocks to that!  When I want the compiler fooling around trying to second
guess what I really mean, and whether I could *possibly* (but not
necessarily, given the above example) have a NULL pointer, *I'll* code it,
thank you very much.

This sort of nannying by the compiler makes the language *much* less
controllable.

: Note that a pointer declared "A * const not_null p;" differs from a
: reference only in terms of syntax.

Exactly!  So why bother ?  Just keep references.

: Construction of virtual base classes now happens at the highest class.
: This is a mistake.  It is particularly annoying when developing a class
: library where I want to use virtual base classes to implement the
: library, but I don't want to bother users of the library.  The rule
: should be that the virtual base class should be constructed at the
: lowest level that contains all references to the virtual base class.

Except that once someone comes along and uses your class library you are in
big trouble with this rule.

Consider the case when they inherit from one of your leaf classes.  In your
library, those classes are "the lowest level that contains all references
to the virtual base class".  But when someone multiply inherits from two of
your leaf classes they suddenly aren't any more, and by your rule the
person implementing the new, inherited, class should now call the virtual
base class constructors.

Except that your library classes are also calling the virtual base class
constructors as well ...

Nightmare city.

: One should be able to say that a virtual base class is no longer virtual
: as follow:

No comment, except to ask :

Have you ever encountered a *real world* use for such a thing ?

: In C++ "class B : A {};" is interpreted as "class B : private A {};".
: This is usually an unwanted favor.  The user should be required to
: explicitly state his preference, "private", "protected", or "public".

Just to show that this isn't a knee-jerk "everything that you say is wrong"
post, this is one of your criticisms that I actually agree with.

Just as the fact that a function without an explicit return type returns
"int" implicitly is considered to be bad style and an archaism, so should
not explicitly stating the access control for base class specifiers be.

( I note that you yourself use an unadorned "main()" later on in your own
examples.  Be ashamed! )

Part of the beauty of C++ is that (as long as you know the syntax)
everything is explicitly stated for you in a declaration.

( In fact, this is a major argument against your removing the explicit "&"
from function declarations taking parameters by reference above.  Removing
the explicit "this is a reference, boys" flag from the source and making it
implicit is just as unsavoury a practice as lack of access specifiers. )

: C made a minor mistake in not differenciating between a pointer to a
: type and a pointer to an array of a type.  This has become a major
: mistake in C++.  Consider the following:
:
:     class A { int i1; };
:     class B : public A { int i2; };
:     B b[9];
:     A* p = b;
:     A* p2 = p + 2;
:
: This non-sensical code is legal.  It shouldn't be.

It makes perfect sense to someone who understands the language. This is my
"Know Your Language" point from before.

: It would be nice if one could use any type, not just classes, as the
: base for a class.  For example:
:
:     template <class T> class handle : public T*

Is this a case of your confusing IS-A with HAS-A ?

: What does this declaration mean:
:
:     a b(c);
:
: "a" must be a class.  "b" can be either a function or an object
: depending on "c".  If "c" is a class, "b" is a function.  If "c" is an
: object, "b" is an object.  To fix this kind of ambiguity, C++ should
: require the word "extern" for function declarations.

Hmmmm.  Removing the "implicitness" of linkage specifiers would be a good
idea (see above), but I suspect that simply adding "extern" won't fix all
of the ambiguities between declarations and definitions.

:      A a;
:      void (.*spmf)() = &a.fn;
:      // Implemented as a two-element struct containing a pointer to `a'
:      // and a pointer to `fn'. Note that it is not necessary to handle
:      // virtual functions in a special way.
:
: This would be useful for all sorts of callback functions, as well as
: allowing the programmer to check for vtable corruption.

Good idea, except ...

... it isn't useful in the vast majory of callback functions, because (in
my experience anyway) callbacks are mainly used by the O/S API, which doesn't
expect funny <object,pointer> tuples for callbacks, but simple function
addresses.  A much better way around this is to use reflector functions
instead, anyway.

... checking for vtable corruption is properly the remit of the C++ run-time,
and *not* that of the programmer.  Who is to say that there *is* a vtable
in the first place anyway ?  You shouldn't assume that the implementation
works that way.

: Consider the following program:
:
:     #include <iostream.h>
:     class A {};
:     inline void fn(void*)   { cout << "not ok\n"; }
:     inline void fn(A*)      { cout << "ok\n"; }
:     class B;
:     inline void fn2(B* p)   { fn(p); }
:     class B : public A {};
:
:     main() {
:         B b;
:         fn2(&b);
:         return 0;
:     }
:
: This program will print out "not ok".  The problem is that C++ resolves
: B* to void* rather than A*.  Converions of not fully declared types
: should be banned.

Hmmmm.  I see an additional problem.  What if the compiler chooses to
inline the function ?  Would the behaviour be different, considering that
the inlining would occur at a point where the conversion from B to A would
be known ?

( There are probably "as if" rules to cover this, I suppose. )

: I would also suggest allowing declarations like:
:
:     class B : public A;

Yes.  Again I agree.  ( See ?  It's not all negativity.  (-: )

In fact, it would eliminate a problem that I have.  To forward declare a
DTS C++ class I have to wrap the forward declaration in #pragma pairs to
force it to be a DTS C++ class declaration (otherwise the compiler assumes
that it is a plain C++ class).

I'd much rather have forward declarations of the form

       class MyClass : public SOMObject ;

: It would be nice to be able to initialize arrays [in new expressions]:

But would again introduce more run-time complexity.

: "new" is like "malloc", and "delete" is like "free", but what is like
: "realloc"?  I suggest "renew".

Hmmmm.  So you are willing to sort out the semantics of deep-copy classes,
virtual inheritance, and uncopyable (private copy constructor) classes are
you ?

Give me advance notice before you open this can of worms.

Just so that I can stand well back.  (-:

: Ctors don't inherit.  This is usually what is wanted, but not always.
: Once again, C++ fails to give the programmer control.  The obvious way
: to give the programmer control would be with the namespace features as
: in:
:
:     class A {
:     public:
:         A(int);
:         A& operator=(const A&);
:     };
:
:     class B : public A {
:     public:
:         using A::A;             // inherit constructor
:         using A::operator=;
:         // ...
:     };

Hehehehehehe.  What you want are

   #pragma On(SOMObject_constructors)
   #pragma On(SOM_Object_Assignment_Operators)

<smug look>

: A related problem is when you don't want to inherit a class fn.  I
: would like a key word like "dont_inherit" to handle this.  Here is an
: example:
:
:     class A {
:     public:
:         dont_inherit virtual int Size()    { return sizeof(*this); }
:     };
:
: When Size() gets inherited, it returns the wrong result.  Fns declared
: "dont_inherit virtual" would force its redefinition in each derived
: class.

The pure virtual mechanism does most of what you want already.

:              Another example is an Init fn.  I don't like constructors for
: various reasons, so I often use an Init() fn instead.  I would prefer
: if this weren't inherited.

Sheesh!  Make it private (or protected) then!

: Here is another example of C++ being too automatic and not giving the
: programmer enough control.  Consider this
:
:     class C {};
:
:     class A {
:     public:
:         A(C* p);
:     };
:
:     class B : public A {
:     public:
:         B() : c(), A(&c) {} // trouble
:         C c;
:     };
:
: This is valid C++ code.  I think that listing constructors in a
: different order from the real order of construction should generate an
: error.  Forcing programmers to list things in the right order makes
: the code clearer and less error prone.

Probably a good idea.  Although a good compiler should issue a warning on
this anyway.

: I would also give the programmer more control over the order of
: construction by allowing one "order" statement in a class to specify
: the order of construction as in
:
:     class B : public A {
:     public:
:         order c, B;
:         B() : c(), A(&c) {} // ok
:         C c;
:     };

Such ordering is implicit in the semantics of base class and member
construction already, though.

: C++ constructors are poorly designed.  They are unnecesarily complex
: and confusing.
:                     [.......]
:                                              Instead of using the
: current complex syntax of ctors to do this which looks like
:
:     myFrame::myFrame
:         : upper_left(...)
:         , upper_right(...)
:         , lower_left(...)
:         , lower_right(...)
:     {}
:
: a simple "construct" statement, to be used only in constructors would
: have sufficed.  So in the above example, the ctor for myFrame would
: look like
:
:     myFrame::myFrame {
:         construct frame();
:         const int dx = 100;
:         const int dy = 30;
:         int x1 = (Dx()/2 - dx)/2;
:         int x2 = Dx()/2 + x1;
:         int y1 = (Dy()/2 - dy)/2;
:         int y2 = Dy()/2 + y1;
:         construct upper_left("upper left",x1,y1,dx,dy);
:         construct upper_right("upper right",x2,y1,dx,dy);
:         construct lower_left("lower left",x1,y2,dx,dy);
:         construct lower_right("lower right",x2,y2,dx,dy);
:     }

Aesthetics, pure and simple.

You *can* call class function members in the initialisers for class data
members, by the way.  It's only the initialisers for base class
constructors where you have a potential problem (but such wasn't your
example anyway).

: Yes I know, this is a big change for C++.  But constructors are a big
: problem.

Not in my experience, they aren't.  Again this is mainly a "Know Your
Language" issue.

: Default assignment operators are another unwanted favor C++ does for
: programmers.  They should be generated for simple structs only, for C
: compatibility.  For other classes, a "default_copy()" fn should be
: generated if refered to so that the programmer can easily write his
: own fns as in

Let me get this straight.  You don't like the compiler generating a default
shallow copy version of operator= for you, and yet you want it to generate
a default shallow copy function for you, just with a different name ?

Why ?  If you don't like shallow copy, you implement your own operator=.
If you do like shallow copy (and are lazy), you let the compiler implement
it for you.  What's the problem ?

Another case for

       #pragma On(SOM_Object_Assignment_Operators)

and another <smug look> by the way.

: I have read Stroustrup's books, but I still fail to see the point of
: exceptions.  Until someone can enighten me, I think exceptions should be
: eliminated.  I think code written that makes wide use of exceptions will
: be a nightmare, totally unreliable.

Like the so-called Standard Template Library.  (-:

I must say that I agree with you.  But this is one of those perennial
arguments much like "how shall we indent our source code?".  I myself prefer
returning error codes from functions (and using pass-by-reference for other
return values, of course) to throwing exceptions, and also remain
unconvinced as to the usefulness of exceptions.

It's all a question of style, though, isn't it ?  I like to include all
error handling as part of the main logic of any function because I think
that handling errors is something that you should be thinking of at the
same time as you code the rest of a function.  So I use "error code" error
processing, and handle errors at the point of calling the function.  Other
people like to have all of the error-handling code separated out from the
main body of their code ("It obscures the logic"), so they use the exception
throwing model of error processing.

And if any of the standards people have read this far, count this as one
compiler user's voice against the change to the semantics of operator new.
Bring back NULL return values !

Anyway.  I'll now stand back and watch the war ... (-:





Author: jim.fleming@bytes.com (Jim Fleming)
Date: 1995/04/07
Raw View
In article <3m3aci$2r8@silver.jba.co.uk>, JdeBP@jba.co.uk says...
>
>Frank Schmidt (franklin@millenium.texas.net) wrote:
>: I thought I would post this file of my criticisms of C++ that I have
>: acumulated.
>
>And cue another long language war.
>
>Oh well, here goes.
>
>: 2.  simple to implement - Is it easy for the compiler writers to
>: implement?  This affects the user in 2 ways; fewer compiler bugs and
>: faster compile times.
>
>Of course, the reverse side of this criterion is that simple to implement
>languages are often simple languages.  i.e. not powerful/elaborate/rich.
>
>It's also often surprising that the hard to implement languages are the
>ones that attempt to hide the most from the user.  If you compare the size
>of the average BASIC runtime to the size of the average Standard C Library,
>and consider what each are doing, then you'll see what I'm getting at.
>

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Please DO NOT compare C and C++. As many people have stated here, C++
and C are two different languages. C is small, elegant, and simple to
implement, C++ is NOT! After 15 years, they still do not have it working.

C++ could have been named anything. Some have suggested that C- would
have been a better name. In "English", C+ means a little below average.
I suggest that C++, should be named D-, which is what I would grade it
if a student turned in the *design* as an extension to The C Programming
Language developed by Ritchie, Thompson, Kernighan and Morris.

If Mr. Stroustrup thinks he got a C++ in programming language design,
that is fine. Unfortuantely, his degrees, grades and awards mean nothing
with people that appreciate "fine language design". One thing for sure,
C++ will never get an A+ from anyone with an ability to judge language
design.

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
--
Jim Fleming            /|\      Unir Corporation       Unir Technology, Inc.
%Techno Cat I        /  | \     One Naperville Plaza   184 Shuman Blvd. #100
Penn's Landing      /   |  \    Naperville, IL 60563   Naperville, IL 60563
East End, Tortola  |____|___\   1-708-505-5801         1-800-222-UNIR(8647)
British Virgin Islands__|______ 1-708-305-3277 (FAX)   1-708-305-0600
                 \__/-------\__/       e-mail: jim.fleming@bytes.com
Smooth Sailing on Cruising C+@amarans  ftp: 199.3.34.12 <-----stargate----+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\____to the end of the OuterNet_|