Topic: a plea for a feature
Author: Erik Naggum <erik@naggum.no>
Date: 29 Mar 1994 20:13:21 +0200 Raw View
[Ajay Kamdar]
| It is possible to have the compiler help you detect and prevent such
| problems by using an intermediate class for the size of the hashtable.
yes, I discovered this on my own and have used it as an example of why C++
needs the mechanism I want. it is clearly unsatisfactory to have multiple
classes beyond actual need just to make the compiler stop doing something
is shouldn't and you didn't want it to do in the first place.
| This technique is widely known and practiced.
if so, it just reinforces my impression that C++ needs a construct to turn
off the constructor-as-conversion-operator. actually, I want to turn it
_on_, so I know that it happens only when I want it to.
the "solution" I use today is to have intermediate classes for almost every
class I define, and this should be unnecessary. it's the realization that
I had to do this all over the place that prompted my "plea for a feature".
Bjarne has obviously wanted this feature for a reason, but I don't get it.
are you there? could you explain, briefly?
best regards,
</erik>
--
Erik Naggum <erik@naggum.no> <SGML@ifi.uio.no> | memento, terrigena.
ISO 8879 SGML, ISO 10744 HyTime, ISO 10646 UCS | memento, vita brevis.
for information on SGML and HyTime, try ftp.ifi.uio.no:/pub/SGML first.
Author: bs@alice.att.com (Bjarne Stroustrup)
Date: 29 Mar 94 21:22:11 GMT Raw View
erik@naggum.no (Erik Naggum @ Naggum Software; +47 2295 0313) writes
> given the declarations:
>
> class Hashtable {
> ...
> public:
> Hashtable (int);
> ...
> }
>
> some_function (Hashtable&);
>
> and the code fragment
>
> some_function (4);
> I obtain unexpected results. I would have liked to get an error message
> about this.
You got a call some_function(Hashtable(4)). That was a design bug. I realized that
in 1988 or so and that behavior is outlawed by the ARM. Cfront 2.0 (now VERY old)
accepts the example, but warns with the +w option. Newer compilers give errors or
unconditional warnings.
To get caught you need to use a const reference:
void another_fct(const Hashtable&);
void f()
{
another_fct(4); // another_fct(Hashtable(4));
}
This solves most of the problems of this kind that I have experienced.
- Bjarne
Author: jamshid@ses.com (Jamshid Afshar)
Date: 30 Mar 1994 16:21:55 -0600 Raw View
Redirected to c.l.c++.
In article <19940328.1574.erik@naggum.no>, Erik Naggum <erik@naggum.no> wrote:
> class Hashtable {
> public:
> Hashtable (int);
> }
> some_function (Hashtable&);
>
>and the code fragment
>
> some_function (4);
>
>I obtain unexpected results. I would have liked to get an error message
>about this. [...]
If your compiler does not give an error for the above code, or at
least warn that it's obsolete, loudly report the compiler bug. It is
not legal for the compiler to create a temporary Hasthtable (using 4)
to initialize the non-const reference parameter of some_function().
If some_function() actually takes a *const* reference in your code,
then the call would be legal, so see below for a workaround.
>is it possible to consider a mechanism to specify in a function prototype
>that it wants stricter type checking, no conversions, etc?
Yes, there's a technique for doing this. It's discussed in Andrew
Koenig's column in the January 1994 _C++ Report_ and I think it might
be used in the upcoming ANSI/ISO dynarray class. Instead of the
Hashtable constructor taking an int, have it take a NoConv<int>.
template<class T>
class NoConv {
T t;
public:
T( const T& i ) : t(i) {}
operator const T&() const { return t; }
operator T&() const { return t; }
};
class Hashtable {
//...
public:
Hashtable (NoConv<int> i) { table = new Node*[i]; }
}
void some_function (const Hashtable&);
int main() {
some_function( 4 ); // error
Hashtable h1(4); // okay
Hashtable h2 = 4; // error
some_function(h1); // okay
return 0;
}
It relies on the fact that the compiler will apply only one
user-defined conversion at a time in any given context. Before using
this technique I would make sure that it is appropriate for the class
to take the single parameter.
>(yes, I'm aware of the "solution" to use Hashtable *, instead, and that's
>what I do. however, as a matter of style, I dislike the -> operator, and
>can't understand why (*fun)() became fun() while (*nofun).foo must remain
>nofun->bar. the compiler knows what type it is, and could easily interpret
>nofun.bar "the right way". I might as well put in a plea for this, too.)
I don't think adding even more special cases interpretations to C++ or
more ways to write the same thing would make C++ easier to read or
learn. I do prefer the solution using NoConv<int> to using a
Hashtable pointer instead of a reference.
>maybe I should ask why constructors are used as convertors with no means to
>stop them from being used that way.
Yes, I believe that's the key problem you're having. The NoConv<T>
idiom isn't as clean looking as a language extension, but it probably
works well in practice.
>the other minor things have to do with things I do not want but can't turn
>off except by silly things like making operator=() a private member. maybe
>I'm still doing things for which C++ is unsuited or I still haven't grokked
>the C++ way, but the many defaults in C++ are getting in the way of my work
>much too often. especially since I want to write code that I _know_ will
>work, and which will cause abuses to be reported as errors. is it me, or
>is code in C++ much harder to prove correct than code in C, to the point of
>being intractably hard?
Making operator=() private, often along with the copy constructor, is
a common idiom. C++ is full of idioms, but you don't have to learn
them all at once and I don't think they interfere with or compare in
complexity with the actual design and programming that software
engineers are attempting today.
I definitely don't believe that C code is in theory or in practice
easier to prove correct than C++. Btw, I assume you're referring to
"proving correctness" very loosely -- a rigorous proof of a real
program in either language (any language?) is intractably hard. I'm
also assuming you know that C++ is a superset of C, so you're either
questioning the suitability of object-oriented design or C++'s
implementation of OO features.
IMO C++ does a very good job of allowing the programmer to decompose a
problem domain into classes whose objects have well-defined states.
Public member functions should move objects (completely) from one
state to another. While it's certainly possible to write good C code
which does the same thing, I believe C++ lends itself much more
readily to the use of abstractions, and good abstractions are the only
thing that can make large software systems tractable.
As an example, I can't tell you the number of times that I've been
looking at C code trying to figure out why the program was crashing or
why it wasn't working as documented, only to get lost in a screenful
of code that I finally figured out was doing a (theoretically) simple
linked list manipulation. A good list class template would have made
this code much shorter, clearer, more direct, and therefore less
error-prone. I'm not saying that writing a good list and iterator
class template is easy -- it's not, but being able to write and test
it separately greatly reduces development time of the list and of any
code using a list. Templates simply can't be simulated *well* in C.
Neither can inheritance or exception handling. I think most
programmers agree that these features are useful, even necessary, for
today's software projects, but there is a debate over whether C++
implements these features well enough to be useful. I of course
think it does.
Jamshid Afshar
jamshid@ses.com
Author: adk@Warren.MENTORG.COM (Ajay Kamdar)
Date: 28 Mar 1994 15:47:46 -0500 Raw View
In article <19940328.1574.erik@naggum.no>, Erik Naggum <erik@naggum.no> wrote:
>given the declarations:
>
> class Hashtable {
> ...
> public:
> Hashtable (int);
> ...
> }
>
> some_function (Hashtable&);
>
>and the code fragment
>
> some_function (4);
>
>I obtain unexpected results. I would have liked to get an error message
>about this. substitute more complicated types and type relationships, and
>it becomes very hard to figure out what could go wrong and protect against
>them, and the compiler is no help at all.
>
It is possible to have the compiler help you detect and prevent such
problems by using an intermediate class for the size of the hashtable.
class HashTableSize {
public:
HashTableSize(int size);
//.....
};
class HashTable {
public:
HashTable(HashTableSize tableSize);
//......
};
some_function(HashTable &);
main() {
HashTable ht(4); // this is ok
some_function(4); // ERROR. This will fail to compile.
}
This technique is widely known and practiced. If my memory serves me right,
I believe it also appeared in an article in one of the popular C++ magazines
within the last six months.
- Ajay
--
I am not speaking for Mentor Graphics. | Email : ajay_kamdar@mentorg.com
| a.kamdar@ieee.org
Ajay Kamdar |
Mentor Graphics, IC Group (Warren, NJ) | Phone : (908) 604-0842