Other things I can think of:
Garbage collection would be real useful sometimes (but only if it was
optional).
Some standardized compiler hooks (maybe some kind of pragma) that would
provide a way to marshall and unmarshall data structures without needing
extra bolt-on preprocessors or hand coded I/O routines for each object, etc.
--
>>==>> The *Best* political site <URL:http://www.vote-smart.org/> >>==+
email: Tom.H...@worldnet.att.net icbm: Delray Beach, FL |
<URL:http://home.att.net/~Tom.Horsley> Free Software and Politics <<==+
[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
[ about comp.lang.c++.moderated. First time posters: do this! ]
>What do you all see as shortcomings of C++ or any of its standard library
>components?
Graphics and threading. I know, there are platforms that do support C++,
but don't graphics and/or threading...
>What would you do to change it?
Java on C++ backend.
And the worst thing of C++: lack of implementations that fully support
the C++ standard. There are a lot of nice features that I have to refrain
from in code that needs to be ported to more than 2 or 3 platforms.
Best regards,
Wim Lavrijsen
>> Hmmmmmm. I'm a big newbie, but I think that one shortcoming of C++ is
>> its lack of even a simple WINDOW class. I mean, like a class that
>> could just show a number or string or something in a simple, no widget
>> window. Hmmmmmmm....
>
>And how do you expect this class to be implemented on a system that does
>not have a screen?
For that matter, how do you implement std::iostream on a system that
doesn't have a filesystem?
--
--------------
siemel b naran
--------------
This is one area where C++ could borrow ideas from Ada.
Unfortunately, to do this would break the compatibility between C arrays and
C++ arrays.
The answer is to make an array a first class type with built-in index range
checking. Violation of the designated range, in the case above labeled as
diagnosable, could be flagged by the compiler. Violation of the designated
range in the case above labeled as not diagnosable could be flagged at
run-time by throwing a pre-defined exception.
The real question is whether the C++ community wants to make such a
fundamental change in the language. My guess is that they do not, at this
time.
>
>We have an efficiency v security issue. C++ chose to favour efficiency.
>There is no reason why a coding standard should not prohibit unchecked
>array access, and tools could be used to trace breaches of such a rule,
>but it is not a language issue (for C++) but a coding issue. Other
>languages may elect to opt for security at language level and require
>checking in such cases even though that necessarily reduces performance
>(for general hardware)
This is true enough, except for the assumption that it always involves a
performance loss. It is possible to design a language such that safety and
performance are both optimized. A language with enough syntactic attributes
can be designed such that most of the security issues refered to above can
be identified by the compiler. Once a system is proven to be free of those
security issues, the compiler can optimize out corresponding run-time
checks. This would lead to a longer compile session. For most products that
is an acceptable price for strong help with security issues.
Jim Rogers
Colorado Springs, Colorado
Which GUI ? Motif + X, Berlin, Apple, BeOS, Display Postscript, ...
Cheers,
Juergen
--
\ Real name : Jürgen Heinzl \ no flames /
\ EMail Private : jue...@monocerus.demon.co.uk \ send money instead /
Francis Glassborow Journal Editor, Association of C & C++ Users
64 Southfield Rd
Oxford OX4 1PA +44(0)1865 246490
All opinions are mine and do not represent those of any organisation
|> In other words, the major problems with C++ are that it needs to have
|> more features added, and that not enough platforms support the
|> features it already has. :-)
Well, one of the major problems in practice is the incompatibility
between platforms. Whether this is because there are not enough
features, or too many too quickly, I leave to the reader to judge.
Seriously, the biggest single problem is with C++ is C. If Bjarne had
ignored all issues of C compatibility from the start, I'm sure he could
have come up with a better language. Of course, there is a very great
chance that I never would have heard of that language, so I'm not so
sure how much good it would have done me:-).
--
James Kanze mailto:ka...@gabi-soft.de
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
Ziegelhüttenweg 17a, 60598 Frankfurt, Germany Tel. +49(069)63198627
|> Tim Ottinger wrote:
|> <snip>
|> > We could pretty much eliminate cast conversion operators (o'tor
|> > bool(), etc). Hardly anyone uses them, and with reason.
|> Not in my opinion. Used sensibly they are very handy and saves a lot of
|> processor cycles.
I don't understand. How can the way I invoke a function change its cost
in processor cycles?
>On 19 Feb 2000 06:21:10 -0500, Victor Bazarov <vAba...@dAnai.com> wrote:
>>"David Ferguson" <DavidF...@NOcsiSPAM.com> wrote...
>
>>> A typeof() operator would be very useful. For example:
>
>>A question: how would you use it on a larger scale than just in one
>>place? I mean, if you know the name of the data member, why don't
>>you know the type?
I'd love it for every place I use containers.
typeof(container)::iterator i = container.begin();
Tim
Yeah, but you know, I think that our Javish brothers have convinced
me. The language itself is mostly right. Sure, some syntax is a little
contorted and all, but it's mostly right. We probably just need better
implementations of the language proper.
The library that exists is pretty good. A few rough corners, but those
can be squared away as practice gives insight to new corrections.
But then nearly all of us have to wrestle with concurrency. Plenty of
practice exists. We've been granted the 'volatile' keyword to help
deal with this. I would like to see us working toward some direct
language support for concurrency, where non-compliant compilers can
simply dump "unimplemented" warnings on those bits and pieces, as
we'd hope they do with stdin/stdout/stderr now. Having optional bits
is not a bad thing. I think that with the number of CS people in C++,
we could end up with something that would beat java's 'synchronized'
around the block and down the street.
Actually, I am only half joking when I suggest that ACE would be a
great addition to the standard library. Those guys have already ported
it across the universe to just about every useful platform in sight
(and some questionable ones). Apparently they're good at discovering
good, understandable, useful interfaces and making them work in many
places.
Now, with GUI, I agreed that probably most (AFAIK) C++ stuff is not
gui code. When it is gui, which gui? Win32 would be a poor model in
most ways, though it's the most common desktop environment this year.
MFC is hardly a paragon of C++ library design. OWL? WxWindows? What
would you choose to copy? Maybe not Win32 at all, but XWindows? Okay,
which lib there? InterViews? ET++? What?
If practice gave us a pretty clear direction here, we could adopt a
GUI interface standard that would be reasonable on many platforms.
However, most attempts to build multi-platform C++ GUI libraries have
not met with unqualified success, though many have failed.
Not knowing enough may be the only good reason for C++ to not have a
standard GUI library supported on multiple platforms. Heck ,we do
streams and files, after all. Why not?
If only Doug Schmidt and his team (or the team that remains after his
alleged move out of wustl.edu) would turn their attention to capturing
the practice and patterns of GUI -- we'd be looking at something
useful there. ;-)
>In article <88n31f$32o$1...@bob.news.rcn.net>, Malavon <mal...@yahoo.com>
>writes
>>What kind of declaration syntax would you favor?
>>
>>Richard Finegan
>>
>>
>>> Reflection, garbage collection missing. I really like those features.
>>> Declaration syntax is ugly. Not unmanageable, but ugly. C's was ugly,
>>> and C++ maintains the tradition.
>>>
>Well a better one than that described by Bjarne Stroustrup as an
>interesting experiment that failed (or words largely to that effect) C++
>had little choice because of the design specification to stay close to
>C.
>
For a new language to succeed it would need to come with a
translator that converted all the C and C++ programs to itself 100%.
Ken Walter
Remove -zamboni to reply
All the above is hearsay and the opinion of no one in particular
>What kind of declaration syntax would you favor?
>
Let me have a little time to work that through and maybe I can make
some suggestions to shoot down.
Look at the declaration for a function that returns a pointer to a
function, though, and you get my point. But I get yours also, that
there's not point in complaining if there's not a better way in sight.
In python and smalltalk and others, type isn't an issue, declaration
syntax isn't there. Well, that's nice for them. C++ is a
statically-typed language for a reason.
I'm trying to remember my old Ada training, but the years have stolen
much of it from me. I seemed to remember it being nicer, in that I
asked for a number able to store values in a given range, rather than
by number of bits. I still think that was good. I liked the
in/out/inout declarations. I don't remember reference semantics at
all. :-(
For the most part, the UML notation isn't too bad. I like writing
name:type, and a few changes might make it mostly palatable:
x:int
y:double
z:*double // Maybe?
q:&double // Hmmm....
f: struct (int x, double y, string z) // Maybe?
a: * function(int y): bool // Not quite there.
Of course, the use of typedefs for functions and the like gives
us an out. Without those, I'd be hurtin' for certain.
Of course, anything I recommend would cause a million problems
for preexisting code. I guess if I want a radical change, it's best
to look for it in a radically-different language than to cry about
grafting it on to one in widespread use. But still, the question
was about what I thought the shortcomings were, and I think the
declaration syntax is one of them.
Tim
The template approach gets unwieldy very quickly when you reference multiple
types (say ten or twenty) from disparate parts of the program. The
traditional approach to this kind of task is to create a bunch of typedef's.
--- WithOUT typeof() --- WithOUT typeof() --- WithOUT typeof() --- WithOUT
typeof() ---
#include <iostream>
using namespace std;
typedef short TVacation; // <-- First a bunch of typedef's to
insulate from type changes
typedef int TSalary;
typedef long THours;
typedef long double TWorkLoad;
struct TEmployee {
TVacation Vacation; // <-- Consistently use the typename
TSalary Salary;
THours Hours;
TWorkLoad WorkLoad;
};
int main(int argc, char* argv[]) {
TEmployee Employee = {10, 20, 100, 1000};
THours h = Employee.Hours; // <-- Using the typename insulates
you from trivial changes
TWorkLoad wl = Employee.WorkLoad;
for (TVacation vcnt = Employee.Vacation; vcnt; --vcnt ) {
Employee.Hours += h;
Employee.WorkLoad += wl;
}
cout << "Hours increased to " << Employee.Hours
<< "\nWorkload increased to " << Employee.WorkLoad << endl;
return 0;
}
That is ok if you own the all the source. It can still be painful. It is a
difficult discipline to follow. I just want to insulate myself from trivial
changes in integer types (short, long, long long, signed and unsigned) in
Other Peoples Code (OPC). Rather than introduce a new 'name' (via typedef)
I would like to key off of a well know member.
--- With typeof() --- With typeof() --- With typeof() --- With typeof() ---
#include <iostream>
using namespace std;
struct TEmployee { // <-- Assume you can't change this
definition its OPC
short Vacation;
int Salary;
long Hours;
long double WorkLoad;
};
int main(int argc, char* argv[]) {
TEmployee Employee = {10, 20, 100, 1000};
typeof(Employee.Hours) h = Employee.Hours; // <-- You just
want a compatible type here
typeof(Employee.WorkLoad) wl = Employee.WorkLoad;
for (typeof(Employee.Vacation) vcnt = Employee.Vacation; vcnt; --vcnt )
{
Employee.Hours += h;
Employee.WorkLoad += wl;
}
cout << "Hours increased to " << Employee.Hours
<< "\nWorkload increased to " << Employee.WorkLoad << endl;
return 0;
}
I haven't done any serious STL container programming but it seams like
typeof() would also be handy for declaring iterators.
list<string> Names;
...
for (typeof(Names)::iterator it = Names.begin(); ....)
It just keeps you from having to look up and redefine types. Its not a big
deal but it would sure make my life a little easier, especially when working
with OPC.
Cheers...David
>In article <88n31f$32o$1...@bob.news.rcn.net>, Malavon <mal...@yahoo.com>
>writes
>>What kind of declaration syntax would you favor?
>>
>>Richard Finegan
>>
>>
>>> Reflection, garbage collection missing. I really like those features.
>>> Declaration syntax is ugly. Not unmanageable, but ugly. C's was ugly,
>>> and C++ maintains the tradition.
>>>
>Well a better one than that described by Bjarne Stroustrup as an
>interesting experiment that failed (or words largely to that effect) C++
>had little choice because of the design specification to stay close to
>C.
Recently somebody posted the URLs of a couple of papers that addressed the
declaration syntax issues of C++, as well as giving the whole syntax of the
language a complete workover. I don't have them to hand but I'm sure they can
be easily found.
Actually, that leads to one of my major beefs about C++, which is the
complexity of parsing which makes the design of source code analysis tools
quite complicated. The papers I mentioned before propose an LALR grammar which
can be parsed by lex and yacc. Of course, that then makes it incompatable with
C.
My other major problem is that it is necessary to expose the private members of
a class in the declaration for a class, leading to needless dependancies. I am
aware of the pImpl idiom for getting around this problem, and have used it
myself but I would prefer to have it built into the language. I suspect,
however, that to do this would require extra support from the linker and I
believe one of Stroustrup's goals was to make C++ usable with existing linker
technology.
Bob
Seems to me that arguing that it makes it easier to write non-idiomatic
code is an argument against adding this feature, not one in support of
it.
--
Pete Becker
Dinkumware, Ltd. (http://www.dinkumware.com)
Contibuting Editor, C/C++ Users Journal (http://www.cuj.com)
class string
{
int len;
char *ptr;
public:
explicit string(const char *ch)
: len(strlen(ch)+1), ptr(new char[len])
{
strcpy(ptr, ch);
}
operator const char *() const { return ptr; }
bool operator==(const char *ch) { return strcmp(ptr, ch) == 0; }
bool operator==(const string &s) { return operator==(s.ptr); }
// etc...
};
void func()
{
string xxx("xxx");
if(xxx == string("yyy"))
// do whatever
else if(xxx == "zzz")
// do otherstuff
}
The first if-clause invokes the constructor, strlen, new etc, while the
else-if-clause need not invoke any of that, simply return the ptr.
I'd say this could save you cpu-cycles.
--
Martin Fabian http://www.s2.chalmers.se/~fabian/
--
"Cheer up. It may never happen" (Edina Monsoon)
/* Remove NOSPAM from reply-to address to mail me */
...
> > I'd love it for every place I use containers.
> >
> > typeof(container)::iterator i = container.begin();
container may be const. iterator won't do. You need
typeof(container.begin()) i = container.begin();
which is a bit ugly, if you ask me. :)
let i = container.begin();
is better, but this is still syntactic sugar.
C++ needs typeof() for other reasons.
> Seems to me that arguing that it makes it easier to write non-idiomatic
> code is an argument against adding this feature, not one in support of
> it.
The recent "for_each vs for" debate clearly shows that the for_each approach
is far from idiomatic.
--
Peter Dimov
Multi Media Ltd.
>Tim Ottinger wrote:
>> I'd love it for every place I use containers.
>>
>> typeof(container)::iterator i = container.begin();
>>
>
>Seems to me that arguing that it makes it easier to write non-idiomatic
>code is an argument against adding this feature, not one in support of
>it.
Heaven forbid we should write code more generically, and save us from
ever wanting to switch containers, especially if we have to do
something not provided in <algorithm>.
Every place I have an STL container, I have a typedef. This is so that
I can refer to the type of the container more uniformly and less
container-specifically -- I name them by what I want them to do for
me, not by what they are.
Now, everyone has written deque<zzz>::iterator, list<yyy>::iterator,
etc. We do it all of the time. The XP rule "Once And Only Once" is a
good rule even for non-xp people -- you don't repeat code and
especially not by copy and paste (retyping being the lesser evil), but
you instead realize that there are abstractions you've missed
encapsulating (or else you'd never need to repeat).
It's an Occam's razor thing. If everyone has to write the same
snippets over and over, then they shouldn't have to write them at all.
The longer C++ and STL are around, the friendlier they should get to
the workaday developer. New features are great if they add simplicity
or utility, and otherwise we should avoid them. We should not avoid
them for tradition's sake.
Maybe I've misread you, if so I'm sorry and please explain more.
Tim
I rarely do it. Creating named iterators is usually a mistake. Call
begin() and end() and pass the result to an algorithm. That way the
iterators don't outlive their containers, and you don't risk
invalidating an iterator.
If you do need named iterators, add a typedef for its type right after
the typedef for the container type. Or just use your typedef for the
container in the qualified name for the iterator type.
--
Pete Becker
Dinkumware, Ltd. (http://www.dinkumware.com)
Contibuting Editor, C/C++ Users Journal (http://www.cuj.com)
[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
Well that eliminates all the competitors regardless as to their choice
of grammar. The real problem with many programmers migrating to new
languages is that they do not understand that it is a different
language. The more you make a new language look like an old one the
harder it is for some to recognise that it is a new language. They keep
wanting to do the same things in the same way and grumble when they
can't.
Francis Glassborow Journal Editor, Association of C & C++ Users
64 Southfield Rd
Oxford OX4 1PA +44(0)1865 246490
All opinions are mine and do not represent those of any organisation
[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
Okay, you're a newbie, so I forgive you !
But why do I hate that idea ? The idea to add such a feature without
adding a real abstract, portable window toolkit sounds completely
idiotic in my ears. You can output such things with iostreams already,
can't you ? And if such a function would be part of the standard, how
should any of the window toolkits work with it ? For then you would
have two window toolkits in one program: the first in the standard
libary, to "just display some message", and the second in the real
lib. In short, chaos. Lack of structure. Lack of concept. Lack of
design. Lack of anything. Yes, I really hate that idea 8-)
Or even easier:
container::iterator i = container.begin();
(with syntax "postfix-expression :: type")
Or even
container.begin i();
(with my suggested constructor/function unification feature)
Note that "expr::" allows to implement "typeof", and c/f
unification allows to implement "declare" (aka "var" aka "let")
in the (extended) language directly.
You don't have to use a constructor for a conversion. See below.
>For instance, a string class
>(overly simplified and maybe not the best implementation, I confess)
>could have something like
>
>class string
>{
> int len;
> char *ptr;
>public:
> explicit string(const char *ch)
> : len(strlen(ch)+1), ptr(new char[len])
> {
> strcpy(ptr, ch);
> }
>
> operator const char *() const { return ptr; }
> bool operator==(const char *ch) { return strcmp(ptr, ch) == 0; }
> bool operator==(const string &s) { return operator==(s.ptr); }
> // etc...
>};
>
>void func()
>{
> string xxx("xxx");
> if(xxx == string("yyy"))
> // do whatever
> else if(xxx == "zzz")
> // do otherstuff
>}
>
>The first if-clause invokes the constructor, strlen, new etc, while the
>else-if-clause need not invoke any of that, simply return the ptr.
The second case calls your bool operator==(const char *ch) function
and has absolutely nothing to do with your const char* operator
function. The code you have posted makes no use of casting operators
at all so what is your point?
>I'd say this could save you cpu-cycles.
No.
What on earth is wrong with having explicit conversion functions? In
your example, how about:
const char* c_str(){return ptr;}
Now, if you want a const char* you have to say so (myString.c_str()),
which improves type safety while at the same time being exactly as
efficient, and requiring exactly the same number of lines of code as
your cast operator.
Why do you think the C++ Standard Library string class has no const
char* operator, if it's such a good idea?
Finally though, use of cast operators can lead to useful syntatic
convenience if used carefully. But it is nothing beyond a syntatic
convenience as far as I can tell.
Tom
>Steve Clamage <stephen...@sun.com> writes:
>
>|> In other words, the major problems with C++ are that it needs to have
>|> more features added, and that not enough platforms support the
>|> features it already has. :-)
>
>Well, one of the major problems in practice is the incompatibility
>between platforms. Whether this is because there are not enough
>features, or too many too quickly, I leave to the reader to judge.
>
>Seriously, the biggest single problem is with C++ is C. If Bjarne had
>ignored all issues of C compatibility from the start, I'm sure he could
>have come up with a better language. Of course, there is a very great
>chance that I never would have heard of that language, so I'm not so
>sure how much good it would have done me:-).
One of my favorite quotes about the subject (quoting from memory, may
not be %100 accurate):
"We never have a clean slate. If C hadn't been there for C++ to be
almost compatible with, I would have chosen to be almost compatible
with some other language". Bjarne Stroustup, D&E.
Ran Shalgi
--
Visit Bizarre++: web page dedicated to some eccentric C++ aspects
http://home.beseen.com/technology/bizarre_cpp/
Replace 127.0.0.1 with expand.com when replying by e-mail
Generic programming largely uses values without exactly knowing their types,
or the types they relate to (pointed-to type, inner types, etc.).
Andrei
I don't quite understand your argument. I think typeof is a great tool for
generic programming.
What I'd like to see in addition to typeof is automatic type deduction from
initializer, like:
typeof() i = cont.begin(); // deduces i from its initializer
Amusing idea, but uuuuuuuuuuuuuuuuuuuugly ! syntax !
What about:
typed i = cont.begin ();
Or:
typed i(cont.begin ());
Well there are many alternative keywords for "typed":
typing
autotyped
auto_typed
selftyped
initialized
magic
inittype
assign_type
I personally like "typed" most. It means "This variable
is typed, but the type is not specified at the standard
place".
>typeof() i = cont.begin(); // deduces i from its initializer
auto typeof i=cont.begin(); // i prefer this way
--
--------------
siemel b naran
--------------
[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
>> Now, everyone has written deque<zzz>::iterator, list<yyy>::iterator,
>> etc. We do it all of the time.
>
>I rarely do it. Creating named iterators is usually a mistake. Call
>begin() and end() and pass the result to an algorithm. That way the
>iterators don't outlive their containers, and you don't risk
>invalidating an iterator.
You mean you actually use std::for_each all over the place? I find
it's nice for the simplest of cases, but gets unwiedly quickly.
So I often write code like this,
typedef Container::const_iterator Iter;
Iter iter=container.begin();
const Iter end=container.end();
for ( ; iter!=end; ++iter) {
// line 1 of body
// line 2 of body
// maybe more lines
}
Don't worry about 'iter' and 'end' living beyond their alloted
lifetimes. If it is really a problem I wrap the above in its own
"if (true)" block.
I guess we could do this
for (container_iterator iter(container); iter.ok(); ++iter) { ... }
where template class container_iterator<Container> holds two private
variables -- 'iter' and 'end' as above. This class seems so useful
that maybe it should be part of namespace std. What do you say?
template<class T> void f(T t)
{
// ...
}
f(cont.begin());
That's a rather drastic abbreviation of the standard STL idiom for using
iterators, but I think it makes the point clear. When you explicitly
create named iterators you take on a number of risks, the most
significant of which is that the iterator will outlive its validity,
either because the container goes away or because the container gets
modified in a way that invalidates the iterator.
--
Pete Becker
Dinkumware, Ltd. (http://www.dinkumware.com)
Contibuting Editor, C/C++ Users Journal (http://www.cuj.com)
[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
Unfortunately in order to start with a clean slate, the benefits of such a
move would have to be so great as to outweight the cost of the move. Witness
the history of the OS, they suffer much the same fate. It's the minority
that can make the investment in the long term to make such switches, so such
drastic changes usually only get accepted by a religous few.
--
David Bradley
bradleyd@>REMOVE THIS<digineer.com
How about this -
void &i = lvalue;
const void &i = rvalue;
Then we don't need any new keywords!
What I would like, and no doubt would make compiler writers shriek, is
compile-time enforced preconditions, postconditions, and invariants.
I.e., pointer arguments could be required to be non-NULL, values in a
specific range, etc.
So you might write code like
int foo(Bar* bar) pre(bar != NULL) post(return < 4);
Then, if foo gets called, either bar must have been tested in advance
(including being a precondition in the caller), or you have to add
(say) an assume() statement.
So we might have code like:
void newFunc(Bar* bar) pre(bar != NULL)
{
foo(bar); // ok because the precondition tests bar
Bar* newBar = new(nothrow) Bar; // might return NULL
foo(newBar); // compile error, precondition untested
assume(newBar!=NULL); // compiler can assume non-NULL
foo(newBar);
Bar* newerBar = new(nothrow) Bar;
if ( newerBar )
foo(newerBar); // precondition checked by if
}
assume could also trigger an assert if the test fails.
I have no idea how difficult this would be to implement (relative to
all the other fun stuff compiler writers implement), however. One
might also like to add preconditions to built-in operations, for
example dereferencing a pointer (and thus needing it not to be NULL)
or performing a divide.(and thus needing the denominator not to be 0.
Still, at the moment for me the tools I have (revision control,
differencing, debugging, browsing, auto-documenting, optimizing, etc.)
are more in need of improvement than the C++ language itself.
Andrew Bell
That thinking is what led us to so many different meanings for static.
The language is opaque enough without adding such inscrutable
mechanisms.
Francis Glassborow Journal Editor, Association of C & C++ Users
64 Southfield Rd
Oxford OX4 1PA +44(0)1865 246490
All opinions are mine and do not represent those of any organisation
[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
>Tim Ottinger wrote:
>> Now, everyone has written deque<zzz>::iterator, list<yyy>::iterator,
>> etc. We do it all of the time.
>
>I rarely do it. Creating named iterators is usually a mistake. Call
>begin() and end() and pass the result to an algorithm. That way the
>iterators don't outlive their containers, and you don't risk
>invalidating an iterator.
>
>If you do need named iterators, add a typedef for its type right after
>the typedef for the container type. Or just use your typedef for the
>container in the qualified name for the iterator type.
I do it all the time, but I still have to do it. It would be nice
not to have to all the time. The container itself (well, its class)
knows what kind of iterator it uses. If we could always use it
without naming it, it would be fine. Frankly, though, the binders are
not the most readable and memorable of the stl members, and a lot of
code is still more readable with the c-style for loop.
If only we had a nicer syntax (well, outside of Smalltalk, Python,
etc).
But what we have is a nice bolt-on to a pretty good language. I
shouldn't complain too much lest I'm mistaken for a C++ hater (and I'm
not).
Tim
Yes, but new keywords would be far more readable
than this, wouldn't they ? This is much like the
overuse of "static" in classic C, only much
worse.
The same might be said of named pointers, but we have those.
I don't think the fact that a possible new language feature might be misused
should be a valid reason for deciding against adding it to the language.
Cheers,
Daniel.
Are you serious? Runtime is debatable but compile time in a context of
variables?
Francis Glassborow Journal Editor, Association of C & C++ Users
64 Southfield Rd
Oxford OX4 1PA +44(0)1865 246490
All opinions are mine and do not represent those of any organisation
[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
Yea. Look how main() has been misused over the years. :-)
Jim Rogers
Colorado Springs, Colorado
True. But iterators are not pointers, and need not have the same
weaknesses.
>
> I don't think the fact that a possible new language feature might be misused
> should be a valid reason for deciding against adding it to the language.
>
That's not what I said. The message that I replied to said, in effect,
that this feature should be added because it would make it easier to use
iterators in a way that they were not designed to be used. I said that
that is a weak argument. I said nothing about whether there are other
arguments that support this.
--
Pete Becker
Dinkumware, Ltd. (http://www.dinkumware.com)
Contibuting Editor, C/C++ Users Journal (http://www.cuj.com)
[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
I prefer:
cont.begin i();
or:
cont.begin i;
>
> Or:
>
> typed i(cont.begin ());
>
> Well there are many alternative keywords for "typed":
>
> typing
> autotyped
> auto_typed
> selftyped
> initialized
> magic
> inittype
> assign_type
Other keywords already suggested:
let
declare
var
However note that my solution doesn't need a new keyword at all.
>
> I personally like "typed" most. It means "This variable
> is typed, but the type is not specified at the standard
> place".
Every variable in C++ is typed, so "typed" doesn't really
tell you something new, if interpreted literally.
What about "type_not_specified_at_the_standard_place"? ;-)
(This keyword would have the additional advantage that
it's unlikely to clash with existing identifiers...)
Your pointer example sounds very much like references to me. The use of
references would (more or less) satisfy your non-NULL pre-condition for
pointers while retaining "pointerness" of the arguments passed to the
function.
General rule: If you need to support a NULL pointer use pointers, othewise,
use references.
Pre- and post-conditions in C++ would require the generation of code in
order
to solve your requirement--which would be executed at run-time. The
flexibly
of C/C++ allows you to "extend" the language with macros, and the like.
I would imagine you could do what you want to do with a few simple macros
and
a class. This would also avoid having horrendously performance-degrading
prolog and epilog code in each function.
I often use a embedded class in a function to keep control of
allocation/deallocation (garbage collection). (albeit somewhat kinky) For
example:
void function(void)
{
void * pSomeMemory = NULL;
struct proepi
{
proepi(void *& p, size_t size) : m_p(p) { m_p = (void *)new
char[size]; };
~proepi()
{
delete [] m_p;
}
private:
void *& m_p;
} proepi(pSomeMemory, 10);
// do something here...
}
This is great for garbage collection in functions with multiple exit points
(or functions in general) with very-little performance degradation. A good
compiler will simply optimize out the calls a use inline anyway. I tend to
use this more for legacy code (I don't tend to write functions with multiple
exit points, so this type of code is unwarranted).
This could also be accomplished with exceptions; but that tends to require
rewriting the code to include many throws--which isn't usually an option
with
legacy code.
But, to get to the point, this type of scheme could be used to add pre and
post condition processing. Creation of a map of post and pre conditions
that
would be processed in the ctor and dtor (respectively) would allow you to
assert zero or more conditions without having the compiler add expensive
prolog and epilog code to each and every function.
-- Peter
>===== Original Message From Andrew Bell <bel...@home.dontspamme.com> =====
>On 16 Feb 2000 17:24:06 -0500, "Malavon" <mal...@yahoo.com> wrote:
>>What do you all see as shortcomings of C++ or any of its standard library
>>components?
>
>What I would like, and no doubt would make compiler writers shriek, is
>compile-time enforced preconditions, postconditions, and invariants.
>I.e., pointer arguments could be required to be non-NULL, values in a
>specific range, etc.
>
>So you might write code like
>
>int foo(Bar* bar) pre(bar != NULL) post(return < 4);
>
>Then, if foo gets called, either bar must have been tested in advance
>(including being a precondition in the caller), or you have to add
>(say) an assume() statement.
>
>So we might have code like:
>
>void newFunc(Bar* bar) pre(bar != NULL)
>{
> foo(bar); // ok because the precondition tests bar
> Bar* newBar = new(nothrow) Bar; // might return NULL
> foo(newBar); // compile error, precondition untested
> assume(newBar!=NULL); // compiler can assume non-NULL
> foo(newBar);
> Bar* newerBar = new(nothrow) Bar;
> if ( newerBar )
> foo(newerBar); // precondition checked by if
>}
>
>assume could also trigger an assert if the test fails.
>
>I have no idea how difficult this would be to implement (relative to
>all the other fun stuff compiler writers implement), however. One
>might also like to add preconditions to built-in operations, for
>example dereferencing a pointer (and thus needing it not to be NULL)
>or performing a divide.(and thus needing the denominator not to be 0.
>
>Still, at the moment for me the tools I have (revision control,
>differencing, debugging, browsing, auto-documenting, optimizing, etc.)
>are more in need of improvement than the C++ language itself.
[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
|> I guess we could do this
|>
|> for (container_iterator iter(container); iter.ok(); ++iter) { ... }
|>
|> where template class container_iterator<Container> holds two private
|> variables -- 'iter' and 'end' as above. This class seems so useful
|> that maybe it should be part of namespace std. What do you say?
That if container_iterator is a template, your for loop is illegal, and
having to type std::container_iterator< std::vector< int > > in the
declaration sort of defeats the purpose.
One possibility would be for container_iterator to be an abstract base
class whose concrete instantiations are generated by a template
function, thus:
for ( container_iterator& iter = mk_iter( container ) ; ...
This works, but entails a very noticeable run-time cost: all operations
on the iterator are virtual functions. It is also more flexible than
the templated iterators, of course, and the OSE library uses the idiom
extensively, but I never found the added flexibility useful enough to
add to my own containers (which is to say, I never needed it).
--
James Kanze mailto:ka...@gabi-soft.de
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
Ziegelhüttenweg 17a, 60598 Frankfurt, Germany Tel. +49(069)63198627
>icedance...@ibm-zamboni.net (Ken Walter) writes:
>
>|> For a new language to succeed it would need to come with a
>|> translator that converted all the C and C++ programs to itself 100%.
>
>We can thus conclude that Java has no chance of succeeding. Nor does
>Delphi, or Visual Basic. And what about Perl? Or SQL?
>
>For that matter, how did C become so popular without a translator to
>convert all of the Cobol and Fortran programs into C.
C was not displacing those languages!
Now it is much more difficult unless there
was an obvious large advantage to switching.
It also helps a new language if it is amost free on the most popular
OS
the is being used to learn programming.
Ken Walter
Remove -zamboni to reply
All the above is hearsay and the opinion of no one in particular
I am not sure I like multiple function returns. The problem is in specifying
the order of the returned values. This requires that all values are always
returned. Allowing multiple return values will lead to people wanting
variable lists of return values. There are ways to make this work, such
as returning an array or a list, but these are currently available. Will you
define default return values just as you can have default function
parameters?
Overall, I think multiple return values, although technically workable, will
add
an unwanted level of complexity. Unless there is a special syntax for
declaring
multiple return functions you will have a real problem linking to code. How
will
the linker be able to tell if any function returns no values, one value or
many
values? Will you be able to simply ignore certain return values as so many
people have traditionally done in C? What happens to the ignored values?
What happens to execution efficiency under these conditions?
Jim Rogers
Colorado Springs, Colorado
Your suggestion sounds a lot like the kinds of checks built into an Ada
compiler. Of course, Ada also adds runtime checks, unless you tell it
not to. Many of the possible runtime checks are eliminated by the
compiler during optimization.
Ada deals with the template issues (generics in Ada) by forcing the
instantiation to be declared at compile time. This allows the compiler to
do a lot of the checking. Ada also specifies several different notations
indicating the kind of parameter being passed. For instance, you can
define a parameter that can have a formal of any type, or you can define
a parameter with a formal of only discrete types. There are many more
possiblities, but that would be a diversion from this conversation.
The point is that much of this has already been done in another
language. There are good reasons for having these capabilities. On the
other hand, you must decide if this is the direction you want C++ to go,
or if you merely need to change to another existing language.
I see no reason to expect all languages to converge on a set of
capabilities and/or syntax. Different languages are designed to satisfy
differing needs and priorities. If you find yourself working in a domain
best satisfied by another languge, then use that other language. If the
language you are using is satisfactory for your needs, then keep on using
that language.
You already have them. Use assert().
And if you want a language like Eiffel, use Eiffel, and don't try
to make all other languages which you get knowledge of look like
Eiffel. Eiffel doesn't have a "virtual" keyword, and Eiffel
selects the methods which should be virtual by itself. Therefore
Eiffel needs some way to control what those functions really do.
Therefore it has those pre- and postconditions.
But you don't need anything more nifty than assert() in C++.
> I have no idea how difficult this would be to implement
Just use Eiffel. The thing you want needs totally different
compilers. Eiffel compilers are already the way you want them.
It also has a garbage collector etc. - everything you need to
make programming very comfortable and -- the resulting programs
very slow.
Hey ! I've forgotten something really, really
supersimple !!!
I want the definition of the
vector<T>::operator[] (size_type arg)
contain by default the assertion
assert (0 <= arg && arg < size())
(size_type might be signed depending upon the
allocator).
> >
> >The first if-clause invokes the constructor, strlen, new etc, while the
> >else-if-clause need not invoke any of that, simply return the ptr.
>
> The second case calls your bool operator==(const char *ch) function
> and has absolutely nothing to do with your const char* operator
> function. The code you have posted makes no use of casting operators
> at all so what is your point?
>
You're right, of couse, my mistake.
> >I'd say this could save you cpu-cycles.
>
> No.
>
> What on earth is wrong with having explicit conversion functions?
>
There's nothing "wrong" with that, I simply (tried to) argue that it
would (seem to) be easier to implement compiler optimizations for
conversion operators than for conversion functions.
> In your example, how about:
>
> const char* c_str(){return ptr;}
>
> Now, if you want a const char* you have to say so (myString.c_str()),
> which improves type safety while at the same time being exactly as
> efficient, and requiring exactly the same number of lines of code as
> your cast operator.
>
Could, yes, but again to me it looks as a conversion operator is more
easily spotted as a candidate for optimizing the call away, but then, I
don't implement compilers.
> Why do you think the C++ Standard Library string class has no const
> char* operator, if it's such a good idea?
>
Beats me, I always use my own String class that inherits privately from
std::string and has such a conversion operator. Of course, in new code
it's hardly ever called (and maybe that's why it isn't in std::string),
but when cooperating with old code...
> Finally though, use of cast operators can lead to useful syntatic
> convenience if used carefully. But it is nothing beyond a syntatic
> convenience as far as I can tell.
>
Maybe...
--
Martin Fabian http://www.s2.chalmers.se/~fabian/
--
"Cheer up. It may never happen" (Edina Monsoon)
/* Remove NOSPAM from reply-to address to mail me */
And if you want to assign a simple expression ?
typed i = 103;
works fine: i is an integer
> or:
> cont.begin i;
Even uglier than the above, for 'cont.begin' is an expression,
and its not even the expression you want to assign to i !
> [...]
> However note that my solution doesn't need a new keyword at all.
But it lacks IMHO any logic. It is hard to read for both
the compiler and the human reader.
And why do you fear new keywords that much ? See below.
> Every variable in C++ is typed, so "typed" doesn't really
> tell you something new, if interpreted literally.
Exactly, it shouldn't tell something new. It should just
say "here is a placeholder for the type, get it from the
expression". Well maybe "auto_typed" is the better choice.
> What about "type_not_specified_at_the_standard_place"? ;-)
Guess what. Too long. ;-)
> (This keyword would have the additional advantage that
> it's unlikely to clash with existing identifiers...)
I can't agree that this is a problem. C++ has introduced
many new keywords in the past (try, reinterpret_cast, mutable,
template, true, static_cast, ...) which where not there from
the begining. Especially "try", "throw", "catch", "true" and
"false" had broken many existing programs, I think.
Too, this wouldn't be released immediately, so you'll have
time to fix all existing libs etc. In the end, it's only a
grep, and then maybe a search+replace.
Multiple return values are already available. Just declare some
of the parameters to be output only values. There is really no
need for a "better" mechanism.
The only reason why you might want to have a return value in C
and C++ is that it is much more comfortable to write:
__________________________________________
double a;
a = pow (b, M_PI) - sqrt (b) - 1;
__________________________________________
than writing:
__________________________________________
double a, tmp;
pow (b, M_PI, a); // the last parameter is output only
sqrt (b, tmp); // the last parameter is output only
a = a - tmp - 1;
__________________________________________
As you can see, having singlereturn functions make life easier
and are very helpful syntactic sugar, especially for complex
mathematical functions, because you can use the function result
immediately in expressions, without assigning them first to
some other variable.
Having an assignment list instead of output parameters
introduces NOTHING NEW _EXCEPT_ A DIFFERENT SYNTAX WHICH ISN'T
EASIER, ONLY DIFFERENT. That is against the basic design
principles of C and C++. And you would still not be able to use
multireturn functions in expressions.
Yes, and it's also very unlikely to be used. ;^)
Schobi
--
bitb...@gmx.de is never read
I'm hschober at gmx dot de
For multiple return values all that is needed is for the compiler to
covertly create an un-named struct. If the struct returned does not
match the effective struct being assigned to it calls an error. It is
just syntactic sugar.
Francis Glassborow Journal Editor, Association of C & C++ Users
64 Southfield Rd
Oxford OX4 1PA +44(0)1865 246490
All opinions are mine and do not represent those of any organisation
[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
> I'd love it for every place I use containers.
>
> typeof(container)::iterator i = container.begin();
STL has brought into relief an asymmetrical (vis-a-vis sizeof) omission
from the language. IMO expressions like this should be idiomatic for
operating on all a container's elements:
for(typeof(c)::iterator i = c.begin(); i != c.end(); ++i)
Tim
Sent via Deja.com http://www.deja.com/
Before you buy.
It seems I didn't receive your direct e-mail...
>>It would be impossible for a compiler to enforce compile-time restrictions
on
>>variables that aren't assigned to run-time.
>
>In a lot of cases, the compiler definitely could enforce such
>restrictions. The question is how well one could define the cases
>where it could do so, and just how challenging a task it would be for
>compiler vendors to write.
>
>If you have a line
> int a = 4;
>The compiler can assume a == 4 on subsequent lines in the current
>scope until there's a possibility it changes (either directly, or by
>passing it as a non-const ref or pointer.) If you have code
> if ( b < 6 )
> a = b;
> else
> a = 4;
>
>We know a < 6 or a == 4, which simplifies to a < 6. (Again, until we
>hit code that might change a.)
>
I realize that's a general case... But, if it were me I wouldn't have
written
the last a = 4 line...
I'm not trying to suggest that adding the ability to define assumptions or
ranges, et al, wouldn't be a good idea. I just haven't seen an instance
where
it would make me rush out and buy a compiler that supported it. Given your
above sample (again, I realize it's a general case meant for
exemplification)
if I we assigned 4 to <a>; then, I don't think I'd write any code to do
something with a where it could be anything but four until it changed.
Sure,
the compiler can assume a = 4 until it changes and optimize anything out
that
check a for anything other than four; but, why would I write code like that?
For example:
int a = 4;
if(a == 5)
{
// do something more
}
if ( b < 6 )
a = b;
else
a = 4;
The compiler could simply not execute anything in "do something more", as
well
as the last a = 4 line.
>If we pass a as a non-const pointer (I'm figuring we ignore the
>possibility of const_casts), then that pointer might be copied to a
>global, and thus after a function call the compiler can no longer make
>assumptions about the value of a until it gets retested or reassigned.
>
>>Your pointer example sounds very much like references to me.
>
>References are just (useful) symantic sugar for pointers. Consider:
> Bar* barPtr = NULL;
> Bar& barRef = *barPtr;
>Boom, you have a "NULL" reference. Thus one really wants the
>precondition that a ptr != NULL before operator*() can be called on
>it, as well as operator->().
>
Agreed. But, rather than go the way of Java an neuter some of the syntax to
enforce certain things, I'd rather spend the time and write the code
properly.
Sure, we can't say Bar& barRef = NULL; and we can say Bar & barRef =
*barPrt;
but, when we do things like that we invite defects and poor performance.
>>Pre- and post-conditions in C++ would require the generation of code in
order
>>to solve your requirement--which would be executed at run-time.
>
>In the general case, yes, but run-time code is not acceptable.
>Instead, we want the compiler to figure out everything it possibly
>can, and when it's not smart enough, we "help" it with the assume()
>pseudo-function, which adds the enclosed boolean expression to its set
>of currently true conditions. We would need to clearly define what
>the compiler should be able to determine, otherwise some compilers
>might need more "help" than others, and thus writing portable code
>would be a problem.
Also, I agree with that. But why would I go to the trouble to tell the
compiler to assume something about the code if I wrote something outside
those
assumptions? Say, for example, we want the compiler to assume a is in the
range of 1 to 255 inclusive. It could the optimize code out that ran under
the assumption that a was outside of that range:
void SomeFunction(int a) assume(a >=1, a <=255)
{
switch(a)
{
case 1:
// do something
break;
...
case 400:
// do something
break;
}
}
With that assumption it would simply not compile case 400: into object code.
But, again; why would I write code to deal with <a> outside the bounds I've
told the compiler to assume? I could simply delete the case 400 code and
I'd
have the same result.
I'm generally not in favor of additions to the language that substitute for
good design decisions. Without good design a C++-compiled program isn't
going
to be as efficient or as reliable as one that is; regardless of what we tell
the compiler.
-- Peter
Actually there is one more asymmetry - the code above does not work for
built-in arrays. Given appropriate overloads of begin() and end(), the
following code works for all:
for(typeof() i = begin(c); i != end(c); ++i)
Andrei
> I want the definition of the
>
> vector<T>::operator[] (size_type arg)
>
> contain by default the assertion
>
> assert (0 <= arg && arg < size())
>
> (size_type might be signed depending upon the
> allocator).
Not in a standard conforming implementation. In 23.1/5, Table 65
(Container requirements), it is explicitly stated that size_type must be
an unsigned integral type.
Gerhard Menzl
: Hey ! I've forgotten something really, really
: supersimple !!!
:
: I want the definition of the
:
: vector<T>::operator[] (size_type arg)
:
: contain by default the assertion
:
: assert (0 <= arg && arg < size())
:
: (size_type might be signed depending upon the
: allocator).
No. See table 32. Size_type must be unsigned and difference_type
must be signed.
John
>You already have them. Use assert().
asserts are not tested at compile time.
>And if you want a language like Eiffel, use Eiffel [...]
As I understand it, Eiffel likewise does not do compile time testing,
it also places asserts in the code.
We use plenty of asserts. But that doesn't prevent me from having to
do
void funcThatTakesPointer(Foo* foo)
{
assert(foo);
if (!foo )
// debugging code we could avoid with preconditions
...
}
because despite my best efforts, my 20+ fellow programmers and I
occasionally make mistakes. And each mistake can cost our customers
hours of wasted time -- and also cost us customers. Our testers try
their best to catch as many errors as possible, but we don't end up
testing every case our customers will try.
What I'm proposing is
void funcThatTakesPointer(Foo* foo) precondition(foo)
{
// code that doesn't need to test foo for NULL...
}
Thus every function that calls funcThatTakesPointer *has* to test the
parameter it uses for NULL (often from its own preconditions or
invariants), or it simply won't compile. And code that doesn't
compile doesn't go to customers.
>Eiffel compilers are already the way you want them.
For the reason already specified, no they aren't.
>It also has a garbage collector etc. - everything you need to
>make programming very comfortable and -- the resulting programs
>very slow.
Compile time tests may slow down compilers, but not the resulting
program.
Andrew Bell, who is definitely feeling outnumbered (but still enjoying
the discussion)
Probably the most fundamentally useful bit would be the precondition
that a pointer not be NULL. How many bugs occur simply from a NULL
pointer being dereferenced? People write initial code to try and get
the base stuff working, and then forget to add code to test pointers
for NULL, especially when that condition is rare. So then a user with
a large database and less memory than the programmer's high-end
development machine tries the code, and hits that NULL return.
Mozilla's a pretty modern C++ app, and every so often it crashes on me
with an access violation -- probably a NULL pointer dereference -- as
the most common cause. Typical use of C++ by professional programmers
does not avoid these problems. Perhaps preconditions et al would help
them do so.
>Sure, the compiler can assume a = 4 until it changes and optimize anything
out
>that check a for anything other than four; but, why would I write code like
that?
Sorry, that was meant to be two separate examples, not a continuous
bit of code. The C++ as-if rule already allows compilers to eliminate
code that can never be executed or has no side-effects.
>But why would I go to the trouble to tell the
>compiler to assume something about the code if I wrote something outside
>those assumptions?
You wouldn't. The thing is, without preconditions et al, you have to
write code to handle cases outside those assumptions.
Take a function that normalizes a 2-D vector. What do you do if the
vector is 0,0? Some people will throw an exception, but not everyone
loves exceptions. So others will just leave the vector as 0,0, and
still others will set it to some "default" normalized value. If the
function had a precondition of the input vector not being 0,0, there's
no question about what it does -- it doesn't compile if you try -- and
thus no worries about what it might return.
In a way, C++ already has preconditions et al: the type system. You
can't assign a char* to a double, because the assignment operator for
double has a precondition that the value be a numeric type. The
return type of a function is a postcondition; a function with return
type of double isn't going to return a pointer. Do you consider it a
language flaw that the compiler tells you when you try to violate the
"contract", or do you want the compiler telling you about these
problems? I want the compiler catching these errors, and as many
others as it can. It's better at it than I am.
Andrew Bell
Okay, but I would be surprised if every programmer which codes
a new allocator is aware of this definition, so I would prefer
to add it anyway.
Oh my god, you want to have an automatic proof that
your program is correct ? Don't you know that this
is impossible ? Many people have tried it before !
A compiler which works also as a prover will require
many gigabyte of memory even for smaller programs,
and it will run for weeks and months. Just ask the
AI experts :-((( You will never be able to release
your programs in time if you want to apply this
fully. Okay, in a limited degree this might be
possible, but not purely as "compile time only"
version.
><tboe...@my-deja.com> wrote in message news:89e2q9$vs5$1...@nnrp1.deja.com...
>>
>> for(typeof(c)::iterator i = c.begin(); i != c.end(); ++i)
>
>Actually there is one more asymmetry - the code above does not work for
>built-in arrays. Given appropriate overloads of begin() and end(), the
>following code works for all:
>
>for(typeof() i = begin(c); i != end(c); ++i)
As long as we are wishing for new features, we may well wish
that the original quote *does* work for built-in arrays; that
is, make c.begin() etc valid. We could then view the dot
operator as bringing us one step closer to type attributes,
which is a goodo thing, IMHO,
---------------------------------------------
Ziv Caspi
zi...@netvision.net.il
Use a reference.
--
mailto:j...@acm.org phone:+49-7031-464-7698 (HP TELNET
778-7698)
http://www.bawue.de/~jjk/ fax:+49-7031-464-7351
PGP: 06 04 1C 35 7B DC 1F 26 As the air to a bird, or the sea to a fish,
0x555DA8B5 BB A2 F0 66 77 75 E1 08 so is contempt to the contemptible. [Blake]
There's certain things I do as a matter of habit when programming to avoid
silly little typo errors (like if(1 == variable); if I accidentally type 1 =
variable the compiler will complain and I won't have a bug I didn't notice).
But, there's other things that should always be done; which should be
covered
in an adequate design.
>===== Original Message From Andrew Bell <bel...@home.dontspamme.com> =====
> People write initial code to try and get
>the base stuff working, and then forget to add code to test pointers
>for NULL, especially when that condition is rare. So then a user with
>a large database and less memory than the programmer's high-end
>development machine tries the code, and hits that NULL return.
>
With a proper design you don't have people writing code "to try and get it
working". You should know how it works before you write it. If you want to
write a prototype do so; then scrap it and re-write the application when you
know all the details of the design.
I don't expect, and I don't condone, extensions to the language that replace
proper design. (I don't use goto, and I don't use setjmp...).
>>But why would I go to the trouble to tell the
>>compiler to assume something about the code if I wrote something outside
>>those assumptions?
>
>You wouldn't. The thing is, without preconditions et al, you have to
>write code to handle cases outside those assumptions.
So, what's the compiler going to do with out-of-bounds conditions? Throw
and
exception? If it throws an exception, which it should because it can't
execute the code--you've told the compiler the code doesn't work under those
conditions; then, I have to write code to handle the exception. Then, we're
back where we started; I still have to write code--the compiler doesn't know
what to do unless I tell it. Telling it about ranges, or conditions isn't
enough for it to handle what to do if the condition fails.
The C++ language already does what you want it to; you simply write the code
to handle those conditions (e.g. assert, et al.).
-- Peter
If you have the definitions
template<class T> inline T typed(T& t) { return t; }
template<class T> inline T typed(T const& t) { return t; }
your line would compile as is. Alternatively you could write:
typed i(103);
(Indeed, the second one would be sufficient for your example;
the first is only needed for "perverse" things like auto_ptr)
>
> > or:
> > cont.begin i;
>
> Even uglier than the above, for 'cont.begin' is an expression,
> and its not even the expression you want to assign to i !
Yes, it's an expression, just as it is an expression above.
Here the empty brackets are just removed, to give syntactical
equivalence with normal variable definitions (where the
final () is not allowed since it would make a function
declaration out of it).
>
> > [...]
> > However note that my solution doesn't need a new keyword at all.
>
> But it lacks IMHO any logic. It is hard to read for both
> the compiler and the human reader.
To understand the logic, see my posting with subject
"Some considerations on functions and constructors"
from end of January (and the later posting where I
described what would have to be changed in the C++
grammar for this).
BTW, note that it's not just another syntax for the same
thing, but it's based on the more general observation that
every value-returning function indeed constructs an object,
and therefore should be usable everywhere where a constructor
is.
>
> And why do you fear new keywords that much ? See below.
Do a search on "new keyword" or similar on Deja, and you'll find
a lot about it...
[...]
Please rewrite the following code to not use map<X, T>::const_iterator:
template<class X, class T>
T get(std::map<X, T> const& map, X x, T default_value = T())
{
std::map<T>::const_iterator iter = map.find(x);
if (iter == map.end())
return default_value;
else
return iter->second;
}
With a declare feature, the first line would read:
declare iter = map.find(x);
and with my constructor/function unification suggestion, it would read
map.find iter(x);
|> In article <SNdu4.15759$PA2.1...@bgtnsc06-news.ops.worldnet.att.net>,
|> James S. Rogers <jimmaure...@worldnet.att.net> writes
|> >Overall, I think multiple return values, although technically workable, =
|> will
|> >add
|> >an unwanted level of complexity. Unless there is a special syntax for
|> >declaring
|> >multiple return functions you will have a real problem linking to code. =
|> For multiple return values all that is needed is for the compiler to
|> covertly create an un-named struct. If the struct returned does not
|> match the effective struct being assigned to it calls an error. It is
|> just syntactic sugar.
That's one possible implementation. There are more efficient
implementations, which aren't available from C++.
--
James Kanze mailto:ka...@gabi-soft.de
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
Ziegelhüttenweg 17a, 60598 Frankfurt, Germany Tel. +49(069)63198627
[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
|> ka...@gabi-soft.de wrote in message <867lfrv...@gabi-soft.de>...
|> >tott...@concentric.net (Tim Ottinger) writes:
|> >Fundamentally, I like something of the Pascal approach here, but on a
|> >statement by statement basis:
|> > procedure proc( ... ) ;
|> > function func( ... ) : int ;
|> > var i : int ;
|> > const c : int = 10 ;
|> >Actually, I'd like to take the idea further, so that assignment was a
|> >type of statement, rather than an operator, and you could write:
|> > i , j = j , i ;
|> >Of course, for that to work, we'd have to get rid the comma operator.
|> >But given that, we could also allow multiple function returns, say:
|> >
|> > function f( ... ) returns a:int, b:double ;
|> > i , d = f() ;
|> I am not sure I like multiple function returns. The problem is in
|> specifying the order of the returned values. This requires that all
|> values are always returned. Allowing multiple return values will
|> lead to people wanting variable lists of return values. There are
|> ways to make this work, such as returning an array or a list, but
|> these are currently available. Will you define default return values
|> just as you can have default function parameters?
There really should have been a smiley on the suggestion concerning
multiple returns. I once implemented a language using them -- it was
fun to use, but it was only used for a few, very little programs, so I
have no idea how the idea would scale. Anyway, I don't think that the
idea fits well into the general framework we've inherited from C.
Most of the time I've actually wanted multiple return values, it has
been to support a few, fixed idioms: returning a value with a flag
ok/failed or returning a value with an error code. In C++, both can be
more elegantly addressed using template classes -- in the French
speaking C++ group, I'm known for (amongst other things) my
recommendations of Barton and Nackman's Fallible template class.
|> Overall, I think multiple return values, although technically
|> workable, will add an unwanted level of complexity. Unless there is
|> a special syntax for declaring multiple return functions you will
|> have a real problem linking to code. How will the linker be able to
|> tell if any function returns no values, one value or many values?
|> Will you be able to simply ignore certain return values as so many
|> people have traditionally done in C? What happens to the ignored
|> values?
The same way it knows now whether the function returns int, double or
void: it won't:-). The implementation is NOT a problem, and functions
returning only one value would work exactly as at present. (You won't
pay for what you won't use.)
More interesting than the multiple returns is certainly the multiple
assignment necessary to be able to use them. It's nice to be able to
write:
x , y = y , x ;
for a swap.
|> What happens to execution efficiency under these conditions?
I don't think multiple returns will necessarily cause efficiency
problems.
Not if they understand Turing machines and the Halting Problem:)
Francis Glassborow Journal Editor, Association of C & C++ Users
64 Southfield Rd
Oxford OX4 1PA +44(0)1865 246490
All opinions are mine and do not represent those of any organisation
[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
Hmm. If at all, I would write:
template <class T>
class auto_typed {
T val_;
auto_typed ();
public:
auto_typed (const T& val) : val_(val) {}
~auto_typed () {}
T& operator = (const T& val) { val_ = val; return val_; }
operator const T& () const { return val_; }
operator T& () { return val_; }
};
but it still doesn't work too well.
I don't understand. You want to impose a runtime test that is completely
unnecessary on all applications just to support an erroneous assumption?
A programmer that uses the standard library without bothering to check
the relevant definitions should consider looking for another profession,
I think.
Gerhard Menzl
No, just that it satisfies a few relatively simple preconditions,
postconditions, and invariants. (I would not expect, for example, be
able to verify that a pointer does not point to deallocated memory,
save possibly right after a delete statement.) So what would be
absolutely required is some definition of what possible conditions
could be tested and enforced.
However, since I've been getting about as enthusiastic a response as a
condom machine at the Vatican, I think I'll give it a rest...
Andrew Bell
struct AddWithCarry
{
unsigned int sum;
unsigned int carry;
AddWithCarry( unsigned int left, unsigned int right )
: sum( left + right ),
carry( left > std::numeric_limits<unsigned int>::max() - right )
{}
};
Add accessors and access control to taste.
James Kanze <ka...@gabi-soft.de> also wants:
> x , y = y , x ;
Given the right declarations, you could write this as the more
cumbersome
make_ref_pair( x, y ) = make_pair( y, x )
--Lisa Lippincott
Why? It looks okay the way it is.
--
Pete Becker
Dinkumware, Ltd. (http://www.dinkumware.com)
Contibuting Editor, C/C++ Users Journal (http://www.cuj.com)
: Pete Becker wrote:
: > Seems to me that arguing that it makes it easier to write non-idiomatic
: > code is an argument against adding this feature, not one in support of
: > it.
:
: Please rewrite the following code to not use map<X, T>::const_iterator:
:
: template<class X, class T>
: T get(std::map<X, T> const& map, X x, T default_value = T())
: {
: std::map<T>::const_iterator iter = map.find(x);
: if (iter == map.end())
: return default_value;
: else
: return iter->second;
: }
return map.find(x) == map.end() ? default_value : map.find(x)->second;
Don't like the efficiency? ;)
template <class Iter, class Z>
Z const& idiomatic_default_find (Iter f, Iter e, Z const& d) {
return f == e ? d : *f;
}
template<class X, class T, class C>
T get(std::map<X, T, C> const& map, X x, T default_value = T()) {
return idiomatic_default_find(map.find(x), map.end(),
pair<X, T>(x, default_value)).second;
}
As an added benefit, the idiomatic_default_find can be used with any
container and an appropriate find.
John
template <typename I, typename T>
inline T pick(I iter, I end, T default_value)
{
return (iter == end ? default_value : iter->second);
}
template<typename X, typename T>
inline T get(std::map<X,T> const &map, X x, T default_value = T())
{
return pick(map.find(x), map.end(), default_value);
>So, what's the compiler going to do with out-of-bounds conditions?
Refuse to compile the source.
Now, in my proposal, I talked about the idea of an assume()
pseudo-function that tells the compiler to assume the provided
condition is true. Perhaps a better solution (since otherwise, for
true safety one still has to write the error-checking) is to have
assume also generate code to test the condition and throw a special
exception if the test fails.
>[...] then, I have to write code to handle the exception.
Note, however, the exception throw has moved. Instead of being inside
the function with the conditions, it's now in the calling code.
(While not that big a plus generally, if we have a compiler that can
warn us if we throw an exception that will violate a throw specifier,
that lets the throw specifiers be more restrictive.
>Telling it about ranges, or conditions isn't
>enough for it to handle what to do if the condition fails.
Compiling...
Error in line 123 : precondition "a!=NULL" not guaranteed for function
foo(char* a)
I think I'd better write this whole thing up as a complete formal
proposal. Dealing with what seem to be misconceptions of the initial
proposal is taking just too much time.
Andrew Bell
For example, say I want to write a button class in a GUI package. When
this button is pressed a member function of some other class needs to be
executed. How can this be done? Well the obvious solution is to pass a
pointer to a member function to the button's constructor. Unfortunately
this can't be done. We either have to wrap every single member function
that needs to be called by the button with a static member function or we
have to come up with some complicated scheme like signals & slots as used
by Qt / KDE.
A while ago there was a book published called C++ Gems that had a paper on
callbacks/functors that looked promising. Unfortunately the implementation
described resorted to copying member functions using mem_copy with the
maximum size of pointer to member function hard coded. Kind of scary.
I don't recall exactly how the callback mechanisms work for MFC but I
remember looking at the code and being appalled at how compiler dependent
it was.
The cat's pajamas, IMHO, would be to have some dynamic function calling
capability like objective-C or smalltalk has. I realize that there would
be costs associated. I realize the dangers of relaxing the strong static
typing rules. I don't what to just switch to one of those other languages
because I don't want to incur those cost for every line of code. Just the
once I choose.
I believe the NextStep GUI (Now Apple's Aqua) to be the best GUI tool kit
ever built. It uses Objective-C. Could this tool kit be implemented in
C++? I'm not sure that it could without dynamic calling mechanism added
on. I have never seen a very good mechanism.
Chris King
ki...@pixar.com
> A programmer that uses the standard library without bothering to check
> the relevant definitions should consider looking for another profession,
> I think.
You understand completely. If the type is unsigned, the compiler will
optimize the assertion away anyway, and if not, I've caught an
additional
error. Whats wrong ?
Programmers make errors all the time, and it is good if one can handle
them.
Sometimes partial solutions to problems are fine, but at other times
they result in complacency which actually worsens the situation. There
are times when it is much better to say 'You are always responsible,
though you may find some tools that will help you meet that
responsibility.'
Francis Glassborow Journal Editor, Association of C & C++ Users
64 Southfield Rd
Oxford OX4 1PA +44(0)1865 246490
All opinions are mine and do not represent those of any organisation
[ Send an empty e-mail to c++-...@netlab.cs.rpi.edu for info ]
>Use a reference.
They don't work very well as members of vector<>, new doesn't return
them, they *can* be "NULL" (dereference a NULL pointer), etc.
References seem like they have the precondition that they can't be
NULL, but it's a false sense of security.
Andrew Bell
There was a nifty tip in an article I saw ( I believe it was by Scott
Meyers ) on how to do compile time asserts. I think it went something
like this:
template < bool b > class static_assert;
template<> class static_assert<true> { public: static_assert(void) {} };
#define STATIC_ASSERT(x) static_assert<x>()
Then you can do stuff like:
STATIC_ASSERT( sizeof(long) == 4 );
If the asserted condition is false, the compiler will complain because
there is no definition of static_assert<false>. And the optimizer
should remove any generated code.
-Fred Hewett, Compuware Corporation
Sent via Deja.com http://www.deja.com/
Before you buy.
Two approaches to doing this in current C++ have been discussed here
before. Both will give the template capabilities of your compiler
a good workout. And neither works quite as well as a capability
built into the language would. But I think both are adequate for
most tasks.
One approach is to pair a pointer to an object with an applicable pointer
to a member function. The obstacle this presents is that while the types
of both pointers depend on the object type, you want the type of the
callback object to be independent of the object type.
You can solve this problem by casting the object pointer to void *,
and the member pointer to a generic member pointer. But this creates
another problem: you can't call the member function without casting
both pointers back to their original types.
The solution is to include a third item in the callback object: a pointer
to a function (a "thunk") which performs the casts and makes the member
function call. This thunk function can be generated by a template private
to the callback class.
The second approach can be seen as a variant on the first. When
the member function is known at compile time, it can be a template
parameter to the thunk function, rather than a run-time parameter.
While this approach is limited to pointers to member functions
which are compile time constants, it is likely more efficient in
both time and space. And it more easily embraces non-member functions.
On the other hand, few compilers currently support it.
> I believe the NextStep GUI (Now Apple's Aqua) to be the best GUI tool kit
> ever built. It uses Objective-C. Could this tool kit be implemented in
> C++? I'm not sure that it could without dynamic calling mechanism added
> on. I have never seen a very good mechanism.
I think something similar, and in some ways better, could be put
together. While Interface Builder relies on a runtime introspection
mechanism, source-level inspection would suffice for building new
applications (as opposed to hacking application binaries). I think
that the symbolic debugging information put out by most compilers
should suffice.
A second problem is that the nib files produced by Interface Builder
require Objective-C's runtime support. But rather than producing nib
files, a C++ interface builder could produce source code to tie the
pieces together. If sufficient attention were paid to minimizing the
ties between this code and the human-generated code -- such as using
callback objects as above -- I think people could avoid the temptation
to meddle with the computer-generated code.
--Lisa Lippincott
How about creating your own types for the conditions? For example, if
you need postconditions and preconditions like "the value is nonzero"
just create a new class NonZero. Something like this (sorry, my C++ is
a bit rusty but I guess you'll get the point):
#include <cassert>
#include <iostream>
// Classes which provide nonzero values.
template <class T>
class NonZero {
public:
NonZero(const NonZero<T>& other) : value(other.value) { }
const NonZero<T>& operator=(const NonZero<T>& other) { }
value = other.value;
}
T get() { return value; }
protected:
NonZero(T value) : value(value) { }
T value;
};
template <class T>
class AssumeNonZero : public NonZero<T> {
public:
AssumeNonZero(T value) : NonZero<T>(value) {
/* we trust the programmer - no run-time checks */
}
};
template <class T>
class CheckNonZero : public NonZero<T> {
public:
CheckNonZero(T value) : NonZero<T>(value) {
/* run-time check */
assert(value != 0);
}
};
// An example of a function with a postcondition:
NonZero<int> foo(int a) {
int value = a < 0 ? a - 1 : a + 1;
return AssumeNonZero<int>(value); // We know that value is non-zero.
}
// An example of a function with a precondition:
int bar(NonZero<int> a) {
return 100 / a.get(); // Will never fail.
}
int main() {
int a;
cin >> a;
// Compile-time check:
NonZero<int> nz1 = foo(a);
cout << bar(nz1) << endl;
// Run-time check:
CheckNonZero<int> nz2(a);
cout << bar(nz2) << endl;
// More compile-time checks:
NonZero<int> nz3 = nz2;
cout << bar(nz3) << endl;
// Programmer explicitly checks:
a--;
if(a != 0) {
AssumeNonZero<int> nz4 = a;
cout << bar(nz4) << endl;
}
return 0;
}
This does the trick for non-NULL pointers, too, and it may be extended
for many different conditions. With some additional methods, utility
functions and typedefs this might even be practically usable... But I
don't think this is really something we should do in C++. :)
--
Jukka Suomela - http://www.iki.fi/suo/
Servin-Maijan tie 10 F 83, 02150 ESPOO, FINLAND
But I think my point is still valid. C++ has poor support for callbacks.
Programers that want to use them have to jump through hoops. Hoops that are not
standardized in any way. Yet callbacks are so obviously useful. Not providing
native language support was a gross omission.
Chris King
ki...@pixar.com
Lisa Lippincott wrote:
You may find it easier to pass a reference to an object, rather than a pointer to
a function.
In your original posting you asked:
> For example, say I want to write a button class in a GUI package. When
> this button is pressed a member function of some other class needs to be
> executed. How can this be done?
>
If instead of passing a pointer to the member fuction of the other class you pass
a pointer (or reference) to an object of the class, the button code call call the
required member function directly. This is effectively the OO approach to
callbacks, and isn't at all like jumping through a hoop.
You can make the technique more poweful still by interposing another class that
functions as a command dispatcher, and can route events from many GUI elements to
the class(es) and methods that will ultimately handle them. Take a look at Design
Patterns - especially the Command and Mediator patterns - for more ideas.
Cheers,
Daniel.
There is no "if". size_type is always unsigned. Testing whether an
unsigned value is positive is utterly pointless and will most certainly
not catch any errors.
Gerhard Menzl
> For example, say I want to write a button class in a GUI package. When
> this button is pressed a member function of some other class needs to be
> executed. How can this be done? Well the obvious solution is to pass a
> pointer to a member function to the button's constructor. Unfortunately
> this can't be done. We either have to wrap every single member function
> that needs to be called by the button with a static member function or we
> have to come up with some complicated scheme like signals & slots as used
> by Qt / KDE.
What would be needed is a function accepting a pointer to a "caller" class:
class caller {
public:
virtual void do() =0;
}
class CallMyClass : public caller {
private:
MyData data;
MyClass& obj;
MyMethod func;
public:
CallMyClass(MyData d, MyClass o, MyMethod f):data(d),obj(o),func(f) {};
virtual do() { obj.func(data); }
}
The API would provide class caller, every type with "My" in it would be
program-specific. Now a program can pass a reference to CallMyClass(...),
and have any function called. The program can also change all properties
(obj, func or data) of the callback at any moment, as long as they're the
same type. Just add another function to the class.
CallMyClass could even be a template provided by the API, to make things
even easier for programmers.
The _real_ problem is that people expect C API's to be able to call C++
specific constructs.
> A while ago there was a book published called C++ Gems that had a paper on
> callbacks/functors that looked promising. Unfortunately the implementation
> described resorted to copying member functions using mem_copy with the
> maximum size of pointer to member function hard coded. Kind of scary.
There is no need to copy CallMyClass. I admit, if you only have a caller
interface you'd risk slicing. But the simple solution to that would be to
require a clone() function. mem_copy sounds like a C solution to a C++
problem.
> I don't recall exactly how the callback mechanisms work for MFC but I
> remember looking at the code and being appalled at how compiler dependent
> it was.
Well, there is something to say for that, given that M is also the vendor
of a not-so-popular but widely sold C++ compiler, and they view MFC as a
sales argument for that compiler. There is very little incentive for them
to make things portable.
> The cat's pajamas, IMHO, would be to have some dynamic function calling
> capability like objective-C or smalltalk has. I realize that there would
> be costs associated. I realize the dangers of relaxing the strong static
> typing rules. I don't what to just switch to one of those other languages
> because I don't want to incur those cost for every line of code. Just the
> once I choose.
Given the fact that things *can* be done typesafe, I say things *should* be
done typesafe. I don't think C++ needs more opportunities for new programmers
to shoot themselves. Forcing typesafety and flexibilty at the same time
through templates is a huge advantage of C++, not enjoyed by most languages.
Michiel Salters
Michiel Salters wrote:
>
> What would be needed is a function accepting a pointer to a "caller" class:
> class caller {
> public:
> virtual void do() =0;
> }
>
> class CallMyClass : public caller {
> private:
> MyData data;
> MyClass& obj;
> MyMethod func;
> public:
> CallMyClass(MyData d, MyClass o, MyMethod f):data(d),obj(o),func(f) {};
> virtual do() { obj.func(data); }
> }
>
> The API would provide class caller, every type with "My" in it would be
> program-specific. Now a program can pass a reference to CallMyClass(...),
> and have any function called. The program can also change all properties
> (obj, func or data) of the callback at any moment, as long as they're the
> same type. Just add another function to the class.
>
> CallMyClass could even be a template provided by the API, to make things
> even easier for programmers.
Yes I prefer a general solution that will work with any type of object Also it
needs to work for methods with any set of argument types. If it can be done with a
template fine. If not we need language support. I don't care for the virtual method
in your example. I want to be able to copy these things around by value (just as I
can a pointer to a function) and I don't want it to get sliced.
>
> The _real_ problem is that people expect C API's to be able to call C++
> specific constructs.
I'm not asking for these things to be called from C. I am asking to be able to use
callbacks (sometimes called functors or closures). Callbacks are a design pattern
that should be as equally valid in C++ as it is in C. however, to make callbacks
generally useful in C++ we need a way to bundle a pointer to an object and a
pointer to a method in that object. The language does not support this as it
stands.
> Given the fact that things *can* be done typesafe, I say things *should* be
> done typesafe. I don't think C++ needs more opportunities for new programmers
> to shoot themselves. Forcing typesafety and flexibilty at the same time
> through templates is a huge advantage of C++, not enjoyed by most languages.
Absolutly. If something can be done in a typesafe way, it should be. However, there
are things that this language makes awkward because of its typesafety.
Communicating between GUIs and applications is one of them. Implementing
interpreters is another.
What I am asking for is a not that radical. A way to defer to run time type
checking in the rare cases where it is needed. This is not that radical.
dynamic_cast has already started down this road.
>
> If instead of passing a pointer to the member fuction of the other class you pass
> a pointer (or reference) to an object of the class, the button code call call the
> required member function directly. This is effectively the OO approach to
> callbacks, and isn't at all like jumping through a hoop.
Ok this works fine if you have a one to one mapping from buttons to model classes
(model in the sense on model-view-controller). There is a pure virtual base class
with just one member function "onButtonPressed". The model implements that function.
Now lets say we have a model that needs to have 100 buttons connected to it. How do
we do that? Probably the best thing we can do is have the model contain 100 instances
of a class that implements that virtual base class by just calling a method in the
parent.
Callbacks seem much more reasonable to me.
Chris King
ki...@pixar.com
I don't believe that's enough. Interface Builder and the Cocoa framework
require
or use more Objective-C-isms, in particular:
- Introspection: that's probably the easy part. Doing it with debug
information
might work in an extremely implementation-dependent way. In my opinion, a
simple
preprocessor probably does it better, see for instance the Mozart
implementation
(http://home.earthlink.net/~descubes/Mozart), even if it's quite
special-purpose.
- Categories: extending a class "later", including if you have only the binary
form of the class. This is possible only if dynamic dispatch uses tables that
can be extended, and C++'s virtual tables do not allow that. To some extent,
Mozart does that too, since overriders can be added to a 'performer' at any
time, but this flexibility comes at a price.
- Forwarding: the ability for an object to take an arbitrary action when a
method does not exist in the target class, in particular sending the message
to
another class. This is very useful for proxy classes, widely used in
WebObjects,
and implementing that in C++ is quite non-trivial, I believe.
Anyway, the point is that all of this can probably be written in C++ somehow,
but none if it is part of C++.
Other pet shortcomings of mine:
<RANT>
- No modules. Why are we stuck with #include as the only modularity mechanism
and pay the compile-time price? I would be happy with a #import where it would
undefined behaviour to depend on any context before it (hey, the compiler
could
read it only once for the whole application, can you imagine that!)
- No standard / portable toolkit: networking, GUI, real-time, threading, etc,
are typical problems in C++, and are not/less in some other languages, if only
Visual Basic or Java.
- A real problem with readability. How do _you_ parse "f < g < h , i > (j, k,
l)" ? Or "A<int, void **(**)()> f(X, Y, Z);" ? Or "for (++i, --j; i[j] + 0[k];
**a&**(--b)()) printf("%d%d%d", i, j, k);" (Yes, this is still valid C++, I
believe)? At some point, C++ became too close to line noise for my taste...
- Wrong default (in my opinion) for const
- Totally off-base argument passing conventions (I'd much prefer 'in' and
'out'
rather than 'by value always, but it may be a reference or a pointer)
- A serious problem with pointer aliasing... Give us restrict, at least :-)
- A definition of 'volatile' that matches no hardware well and serves no real
purpose.
- No nice and standard way to specify implementation-specific behavior.
#pragma?
It's a preprocessor directive? Are you kidding? I'd much prefer pragmas to be,
say, between [[ and ]], and to have [[inline]] (with a proper #define inline
[[inline]]), [[volatile]], [[parallel]], etc. Well, even [[reflective]], for
that matter.
- A totally incomprehensible transfer of most of the C Standard Library to
things like <cmath>, with just enough incompatibility that implementing it
right
is beyond the capabilities of mere mortals. And, of course, <cmath> in general
is implemented as a file named "cmath", and except on the Macintosh, no way
for
your editor to know that this is C++ (OK, I know about Emacs' -*- C++ -*-
thing,
but is that elegant or what?)
- No decent type-safe variable argument lists.
- No easy way to overload multiple operators (something like A + B * C on
matrices)
</RANT>
<OFFTOPIC>
Now, if anybody wants to help me implementing the LX programming language, it
solves all this. See the URL above :-)
</OFFTOPIC>
Now, _you_ asked for it, didn't you :-)
Christophe
> Daniel James wrote:
> > If instead of passing a pointer to the member fuction of the other class
you pass
> > a pointer (or reference) to an object of the class, the button code call
call the
> > required member function directly. This is effectively the OO approach to
> > callbacks, and isn't at all like jumping through a hoop.
>
> Ok this works fine if you have a one to one mapping from buttons to model
classes
> (model in the sense on model-view-controller). There is a pure virtual base
class
> with just one member function "onButtonPressed". The model implements that
function.
> Now lets say we have a model that needs to have 100 buttons connected to it.
How do
> we do that? Probably the best thing we can do is have the model contain 100
instances
> of a class that implements that virtual base class by just calling a method
in the
> parent.
> Callbacks seem much more reasonable to me.
How many callbacks do you require?
I can't see why one callback could be used instead of 100 action classes. If
you want
100 possibly different behaviors, you'd better have 100 callbacks, or one
callback
with a huge switch. Both implementations can be transformed into an object
model.
After all, such an action class/functor is nothing but a C++ wrapper for a
function
pointer. Instead of storing a function pointer, we store an object pointer. It
could
be slightly less efficient, since the object pointer might point to a vtable,
which
would require an extra dereference. But I can imagine a future compiler
optimizing
away single-entry vtables.
Of course, even if you would pass 100 references to functors, you don't need
100
functor objects, just as passing 100 callback pointers doesn't require 100
functions.
I just can't find any measure of reasonability that causes callbacks to be
preferred
over functors.
Michiel Salters
> Michiel Salters wrote:
> > What would be needed is a function accepting a pointer to a "caller"
class:
> > class caller {
> > public:
> > virtual void do() =0;
> > }
> > class CallMyClass : public caller {
> > private:
> > MyData data;
> > MyClass& obj;
> > MyMethod func;
> > public:
> > CallMyClass(MyData d, MyClass o, MyMethod
f):data(d),obj(o),func(f) {};
> > virtual void do() { obj.func(data); }
> > }
> > The API would provide class caller, every type with "My" in it would be
> > program-specific. Now a program can pass a reference to CallMyClass(...),
> > and have any function called. The program can also change all properties
> > (obj, func or data) of the callback at any moment, as long as they're the
> > same type. Just add another function to the class.
> > CallMyClass could even be a template provided by the API, to make things
> > even easier for programmers.
> Yes I prefer a general solution that will work with any type of object Also
it
> needs to work for methods with any set of argument types. If it can be done
with a
> template fine. If not we need language support. I don't care for the virtual
method
> in your example. I want to be able to copy these things around by value
(just as I
> can a pointer to a function) and I don't want it to get sliced.
Slicing is not a problem, since you don't pass objects of this type around,
but
references. Each reference will always point to the same object, with all its
data intact. It is the virtual call mechanism's responsibility to make sure
the
extra data of the derived class is available to the overriding do().
I'm not sure what you mean by "it needs to work for methods with any set
of argument types". In general, such callback mechanisms impose restrictions
on the called-back function or method. Most commonly, these restrictions are
on mandatory arguments of the called-back entity: "The callback function shall
take one argument, of type API_type. This argument will be API_type(1) if the
callback is called in situation A, and API_type(2) if in situation B". After
all, the callback is there to interact with the library and should adapt to
it.
Consider the classic C callback, qsort(). You can't just pass printf() to it.
If you mean: The API must be able to specify any type of data it will pass to
the callback, then yes, that's possible. If the API specifies class caller {
virtual T_A do (T_B b, T_C c)=0; } you will need a derived a derived class
(possibly a template instantiation) with an implementation of this do. But if
you want to use your void X::Y(T_C c, T_B* pb, T_A& ra) that's also possible:
class Xcaller:public caller {
...
T_A do (T_B b, T_C c) { T_A tmp; obj.func(c, &b, tmp); return tmp; }
}
Therefore, this API allows you to easily call methods with matching
signatures (instantiate a template), and with only a bit more difficulty
lets you call methods whose interface differs more (write a simple wrapper
yourself). If you don't have a method you wanted to be called, you can
implement the entire desired functionality in a class derived from caller.
Just provide the entire functionality in Derived::do().
This one keeps coming up, and I agree. I think a lot of people are quite
keen on this idea, and I expect to see it widely implemented before the
next C++ standard.
> - No standard / portable toolkit: networking, GUI, real-time,
> threading, etc, are typical problems in C++, and are not/less
> in some other languages, if only Visual Basic or Java.
Yeah, but so are a zillion other things. Somewhere you have to draw the
line between the things you put in the language and the things you leave
for third parties. I think C++ got this pretty much right: it
standardised a lot of largely abstract stuff -- containers, algorithms,
arithmetic functions, etc. -- and left the "interacting with the outside
world" stuff for the OS and third parties. The only major extension in
this area I think would be worthwhile is threading (and *maybe*
realtime).
> - A real problem with readability. How do _you_ parse
> "f < g < h , i > (j, k, l)" ?
I don't. I believe that's a syntax error.
> Or "A<int, void **(**)()> f(X, Y, Z);" ?
I think that is too as it is, but if you preceded it with "template"
it's a declaration of a function template f() that takes two template
arguments, an int and a pointer to a pointer to a function taking no
arguments and returning a pointer to pointer to void, and three function
arguments of types X, Y, and Z.
Er ... I think.
"When little monsters like this creep up in my code I generally beat
them to death with some typedefs." -- Jos Horsmeier
> Or "for (++i, --j; i[j] + 0[k]; **a&**(--b)()) printf("%d%d%d",
> i, j, k);"
Excuse me, my brain just exploded.
> (Yes, this is still valid C++, I believe)? At some point, C++
> became too close to line noise for my taste...
Seconded. I wish Dr Stroustrup had persisted with that improved
declaration syntax.
> - Wrong default (in my opinion) for const
Sorry, I don't understand this one. Do you mean everything should be
const by default and has to be declared variable? Ugh.
> - Totally off-base argument passing conventions (I'd much
> prefer 'in' and 'out' rather than 'by value always, but it
> may be a reference or a pointer)
But exactly what do "in" and "out" actually mean, in low level terms?
C++ is a low level language (it's a high level language too, of course),
and the ability to specify exactly how arguments are passed isn't
something we can live without.
> - A serious problem with pointer aliasing... Give us restrict,
> at least :-)
Seconded. Since it's in C99, we'll probably get it fairly quickly.
Actually, I believe GCC already supports it, but I don't know how well.
> - A definition of 'volatile' that matches no hardware well
> and serves no real purpose.
Could you explain?
> - No nice and standard way to specify implementation-specific
> behavior. #pragma? It's a preprocessor directive? Are you
> kidding? I'd much prefer pragmas to be, say, between [[ and ]],
> and to have [[inline]] (with a proper #define inline
> [[inline]]), [[volatile]], [[parallel]], etc. Well, even
> [[reflective]], for that matter.
C99 has _Pragma() which does the same thing. I like your syntax better,
though. I don't think the sequence "[[" can appear in currently valid
C++.
> - A totally incomprehensible transfer of most of the C
> Standard Library to things like <cmath>, with just enough
> incompatibility that implementing it right is beyond the
> capabilities of mere mortals. And, of course, <cmath> in
> general is implemented as a file named "cmath", and except
> on the Macintosh, no way for your editor to know that this
> is C++ (OK, I know about Emacs' -*- C++ -*- thing, but is
> that elegant or what?)
Frankly, I think they should have just left the C library alone, apart
from the handful of necessary signature changes. Any program in the real
world is going to include a bunch of the C headers anyway because it
needs to talk to the Posix/Windows/whatever API, so why bother?
> - No decent type-safe variable argument lists.
I put together a suggestion along these lines a while ago; see
ftp://animal.ihug.co.nz/pub/tvf.txt
> - No easy way to overload multiple operators (something like
> A + B * C on matrices)
I'm torn on this one. It would be very useful. It would also be very
complicated.
--
Ross Smith <ros...@ihug.co.nz> The Internet Group, Auckland, New Zealand
========================================================================
"So that's 2 T-1s and a newsfeed ... would you like clues with that?"
-- Peter Da Silva
Now, I see that you're working on your own language, so I'm sure you
must see some pretty good reasons in here. I don't intend to blindly
defend C++, but I'm just hoping you could explain some of your points.
>- No modules. Why are we stuck with #include as the only modularity mechanism
>and pay the compile-time price? I would be happy with a #import where it would
>undefined behaviour to depend on any context before it (hey, the compiler
>could read it only once for the whole application, can you imagine that!)
No arguments here. This could definitely use some attention from the
standards committee. It's a much bigger problem in C++ than it ever
was in C, so I can see why it took a while, but this needs to be dealt
with eventually.
>- A real problem with readability. How do _you_ parse "f < g < h , i > (j, k,
>l)" ? Or "A<int, void **(**)()> f(X, Y, Z);" ? Or "for (++i, --j; i[j] + 0[k];
>**a&**(--b)()) printf("%d%d%d", i, j, k);" (Yes, this is still valid C++, I
>believe)? At some point, C++ became too close to line noise for my taste...
Here, I've got an argument: Yes, this is unreadable junk. I'll
assume for the moment that it was all valid. Even if I doubt some of
this code, certainly it can get pretty ugly. But, how many of them
*require* this syntax? For the valid constructs here, I'm sure they
can be replaced with readable code. No language can claim to prevent
unreadable code.
>- Totally off-base argument passing conventions (I'd much prefer 'in' and
>'out'
>rather than 'by value always, but it may be a reference or a pointer)
Why?
>- A serious problem with pointer aliasing... Give us restrict, at least :-)
What would restrict do? I'm not familiar with your meaning.
>- No nice and standard way to specify implementation-specific behavior.
>#pragma?
>It's a preprocessor directive? Are you kidding? I'd much prefer pragmas to be,
>say, between [[ and ]], and to have [[inline]] (with a proper #define inline
>[[inline]]), [[volatile]], [[parallel]], etc. Well, even [[reflective]], for
>that matter.
Don't really see what you're getting at here. What do the extra [[]]
add? Why is pragma a problem?
Seeing as you're working on your own language, is pragma even
relevant? Won't you have the luxury of both setting & implementing
your own standard & removing the concept of implementation-specific
behavior?
>- A totally incomprehensible transfer of most of the C Standard Library to
>things like <cmath>, with just enough incompatibility that implementing it
>right
>is beyond the capabilities of mere mortals. And, of course, <cmath> in general
>is implemented as a file named "cmath", and except on the Macintosh, no way
>for
>your editor to know that this is C++ (OK, I know about Emacs' -*- C++ -*-
>thing,
>but is that elegant or what?)
I don't get it. I don't use cmath, but I gather it was brought over
intact from C.
Of course it'll be ugly; it's there for compatibility. If you want to
avoid the namespace question, just include <cmath.h> instead of
<cmath>. (In my implementation at least, <cmath> would map directly
to <cmath.h> but with some flag set that instructed it to handle
namespaces differently. I expect most compilers act similarly - does
it really make sense to have both a cmath and cmath.h that are
identical except for the namespace handling?)
>- No decent type-safe variable argument lists.
I've never seen a good reason to have a variable argument list.
IOstreams convinced me you don't need this as bad as you think you do.
If you would care to counter, I'd be glad to read why.
>- No easy way to overload multiple operators (something like A + B * C on
>matrices)
You mean, as opposed to figuring out Result=B*C and then calling
A+Result? Well, that's the mathematical precedence, isn't it?
Now, if you can have some significant performance improvement by
putting 'em all in one function, then you could use some kind of lazy
evaluation deal. Admittedly not easy, but I believe it would be
effective. Can't say from experience, as I don't recall a situation
where this would be significant in my past work.
I've found C++ to be a very flexible language that adapts well to
different environments. In conclusion, I'm very curious about ways it
could be improved (or eventualy replaced). Can you back up your
complaints?
Charles S Haws
What if it follows:
int f;
const int h = 0;
const int i = 1;
template <int h, int i> int g(int, int, int);
const int j = 3, k = 4, l = 5;
HP's aCC certainly accepts it in that context. What it means is (parentheses and
comments added):
f < (/* explicit instantiation */ g<h,i> (j, k, l) )
Now, of course, you could also have:
int f, g, h, i, j, k, l;
In which case it means:
(/*bool*/(f < g) < h) /* comma operator */, (i > /* another comma operator */
(j, k, l))
This, of course, unless I made a mistake.
I believe you just illustrated my point, thank you :-)
>
> > Or "A<int, void **(**)()> f(X, Y, Z);" ?
>
> I think that is too as it is, but if you preceded it with "template"
> it's a declaration of a function template f() that takes two template
> arguments, an int and a pointer to a pointer to a function taking no
> arguments and returning a pointer to pointer to void, and three function
> arguments of types X, Y, and Z.
>
> Er ... I think.
Without template preceding it, but assuming there are definitions like:
int X, Y, Z;
template <class T, class U> struct A { A(int, int int); };
this creates an object named 'f' constructed with X, Y and Z as arguments.
Of course, if you rather have:
class X{}; class Y{}; class Z{};
then you have a function declaration taking X, Y and Z arguments. Take your
pick.
> "When little monsters like this creep up in my code I generally beat
> them to death with some typedefs." -- Jos Horsmeier
>
> > Or "for (++i, --j; i[j] + 0[k]; **a&**(--b)()) printf("%d%d%d",
> > i, j, k);"
>
> Excuse me, my brain just exploded.
Score: 1.
>
> > (Yes, this is still valid C++, I believe)? At some point, C++
> > became too close to line noise for my taste...
>
> Seconded. I wish Dr Stroustrup had persisted with that improved
> declaration syntax.
>
> > - Wrong default (in my opinion) for const
>
> Sorry, I don't understand this one. Do you mean everything should be
> const by default and has to be declared variable? Ugh.
at least for references, pointers, and possibly member functions, yes. Writing
const-safe code with C++ today is quite difficult, I think.
>
> > - Totally off-base argument passing conventions (I'd much
> > prefer 'in' and 'out' rather than 'by value always, but it
> > may be a reference or a pointer)
>
> But exactly what do "in" and "out" actually mean, in low level terms?
> C++ is a low level language (it's a high level language too, of course),
> and the ability to specify exactly how arguments are passed isn't
> something we can live without.
The point is: in general, you care whether an argument enters or exit, not how
it passed. When you say:
struct X { int a; int b; };
void f(const X &arg);
you tell the compiler: I believe that copying a single pointer is cheaper than
copying two integers. That's certainly not true on a 64-bit architecture with a
32-bit int type, since you copy 64-bit each time. That's typically something
that the compiler knows better than you do. So you would probably be better of
saying:
void f (in X arg);
and let the compiler choose whether this is better passed by value or by
reference.
"Premature optimization is the root of all evil" - Knuth
(note that the opposite is true too: "Belated pessimization is the leaf of no
good" - Len Lattanzi.)
>
> > - A serious problem with pointer aliasing... Give us restrict,
> > at least :-)
>
> Seconded. Since it's in C99, we'll probably get it fairly quickly.
> Actually, I believe GCC already supports it, but I don't know how well.
>
> > - A definition of 'volatile' that matches no hardware well
> > and serves no real purpose.
>
> Could you explain?
What does 'volatile' mean? The standard says:
8 [Note: volatile is a hint to the implementation to avoid aggressive
optimization involving the object because the value of the object
might be changed by means undetectable by an implementation. See
_intro.execution_ for detailed semantics. In general, the semantics
of volatile are intended to be the same in C++ as they are in C. ]
The truth is that what matters may be:
- Do not change load/store ordering (critical for instance for database
operations)
- Preserve multiple consecutive store or loads (useful for accessing hardware
registers)
- Preserve the access size so that it matches the declared type access size.
This one is more subtle: I remember a bug in some software I wrote that accessed
memory on a realtime card from a PC through a VME back-plane. The x86 and the
VME bus use different byte-ordering, but some hardware in the interface card
made byte swapping transparent based on the access size. Too bad, even on
volatile pointers, my C++ compiler would optimize away something like
*((volatile long *) ptr) |= 1 as a byte or (rather than long or), defeating the
byte swapping hardware. Nasty bug.
- Do not access memory outside of program control (do not perform cache prefetch
operations, for instance)
- etc. etc. etc.
> > - No nice and standard way to specify implementation-specific
> > behavior. #pragma? It's a preprocessor directive? Are you
> > kidding? I'd much prefer pragmas to be, say, between [[ and ]],
> > and to have [[inline]] (with a proper #define inline
> > [[inline]]), [[volatile]], [[parallel]], etc. Well, even
> > [[reflective]], for that matter.
>
> C99 has _Pragma() which does the same thing. I like your syntax better,
> though. I don't think the sequence "[[" can appear in currently valid
> C++.
That's why I chose it :-) Unfortunately, I think it can appear in valid
Objective-C code.
> > - No decent type-safe variable argument lists.
>
> I put together a suggestion along these lines a while ago; see
> ftp://animal.ihug.co.nz/pub/tvf.txt
That looks interesting, but this is not really vararg, since you can only pass
multiple objects of the same type. You can also achieve much of the same result
by overloading operator, (I think Blitz++ allows you to initialize vectors that
way)
>
> > - No easy way to overload multiple operators (something like
> > A + B * C on matrices)
>
> I'm torn on this one. It would be very useful. It would also be very
> complicated.
Not necessarily. The syntax in LX for this is:
function MulAdd(Matrix A, B, C) return Matrix
written A+B*C
written B*C+A;
I did not try to think about a similar syntax for C++, but I believe this could
be done.
Christophe
Chris King wrote:
>
> I am surprised that nobody has mentioned the poor support for pointer to
> member functions in C++ for use with callbacks.
Sorry, but this is one area where Java has done it right - with
Interfaces.
Smalltalk can also handle this easily.
What C++ needs to easily handle the callback issue is the ability to
call
a known (by signature) member function of an unknown (or anonymous)
object.
Using templates requires too much a-priori knowledge on the part of the
Widget requiring the callback.
This could maybe be accomplished by a new SignatureType declaration, for
example:
(in the header file defining the widget interface)
SignatureType <return type> MyWidgetCallbackSig(<parameter list>);
MyWidget::InstallCallback(MyWidgetCallbackSig);
// in the declaration here MyWidgetCallbackSig actually represents an
// object instantiated from a class that declares a member with that
// specific signature
(in the application class declaration defining the callback member)
class MyClass
{
...
MyWidgetCallbackSig mycallback();
...
};
(and in the method definition itself)
<return type> MyClass::mycallback(<parameterlist>)
{
// etc.
}
Then, when the callback is actually registered with the Widget, the
compiler
should have enough knowledge to determine whether the object passed to
the
installing routine is in fact an instantiation of a class that defines
a method with the correct signature.
Peter Colby
A.h:
__________
class A {
int i;
public:
A () {}
~A () {}
[...]
};
__________
A.cpp:
__________
classspace A {
A () : i(0) {}
~A () {}
[...]
};
__________
This way I don't have to write A::A and such
all the time.
Especially this is comfortable for templates,
where you have to write:
B.cpp [without classspace]:
__________
template<class T1, class T2, class T3, class T4>
T1 B::f1 (T2& arg1, T3& arg2)
{
[...]
}
template<class T1, class T2, class T3, class T4>
T2 B::f2 (T4& arg1, T3& arg2)
{
[...]
}
template<class T1, class T2, class T3, class T4>
T4 B::f3 (T1& arg1, T2& arg2)
{
[...]
}
[...]
__________
Instead one could simply write now:
B.cpp [with classspace]:
__________
template<class T1, class T2, class T3, class T4>
classspace B {
T1 f1 (T2& arg1, T3& arg2)
{
[...]
}
T2 f2 (T4& arg1, T3& arg2)
{
[...]
}
T4 f3 (T1& arg1, T2& arg2)
{
[...]
}
[...]
};
__________