>> The D programming language does nail down many UBs and IDBs:
>> 1) integer sizes are fixed >> 2) bytes are 8 bit >> 3) source code set is unicode >> 4) floating point is IEEE >> 5) char's are unsigned
>> I intend to take this further and define the order of evaluation of >> expressions, too. D won't eliminate all UB and IDB, because things >> like defining endianness are not practical, but it will go as far >> as it can. I'm old enough to have programmed on 36 bit PDP-10s, >> processors >> with 10 bit bytes, DOS near/far/ss programming, and EBCDIC. But >> those machines are dead. Nobody is designing new nutburger >> machines.
Except IBM. :-)
On the other hand, requiring IEEE floating point has already disqualified the EBCDIC systems.
[...]
>> My experience porting D code between platforms is it ports easier >> than the equivalent C++ code.
Porting is easier if you limit the number of potential platforms.
> Porting Java is easy too, if your target platform supports it.
Porting Java is hard, if you haven't ported its platform first!
We had a discussion just last week with a Java developer on reusing his web server code on the mainframe.
- "Oh dear! That's just Java 1.5, I need 1.6 generics for my code. Limiting myself to 1.5 features will cost you a lot more!"
> It's true that writing portable C++ isn't trivial. However, > the most portable C++ code (or, even better, the most portable > C code) is *far* more portable than any Java code, simply because > implementations are viable for so many more platforms.
Porting is easier if you limit the number of potential platforms. :-)
Jerry Coffin wrote: > the Java standard (at least the last time I > looked at it) simply ignores the issue entirely or (more often) includes > some phrase like "IEEE floating point", that makes it sound like the > issue has been dealt with, but when you get down to it doesn't really > mean much at all.
IEEE 754 floating point arithmetic means a lot more than nothing, and certainly far more than C++ floating point, but you're right that it doesn't nail it down 100%.
> Java certainly has a _much_ larger standard library, but this has little > to do with undefined or implementation defined behavior.
I agree with that.
>> 1) integer sizes are fixed >> 2) bytes are 8 bit >> 3) source code set is unicode >> 4) floating point is IEEE >> 5) char's are unsigned
>> I intend to take this further and define the order of evaluation of >> expressions, too. D won't eliminate all UB and IDB, because things like >> defining endianness are not practical, but it will go as far as it can.
> These requirements still limit practical portability, even among modern, > widely-used architectures. Quite a few DSP and even some PDAs, cell > phones, etc., don't support any 8-bit type, just to give one example.
I disagree, because nothing would permit a D *variant* from being customized to unusual architectures. Such would not be less useful than simply dumping the problem off to all users.
I suspect that this is an even better solution for those programming such machines, because they won't be under the delusion that code that has never been tested under such conditions would have been "portably" written with some wrong notion of portability.
> People are still designing and using machines that don't fit the > limitations above. In fact, I'd guess those limitations would cause > problems for the _majority_ of CPUs (though not for the CPUs in things > that are generally thought of as "computers").
A more relevant question is how many programmers are programming for these oddballs, vs programming for mainstream computers?
(I get asked now and then to produce a custom compiler for some oddball CPU, but I ask for all the development money up front because I know there is no market for such a compiler.)
>> The larger a system one is working on, the more important these become. >> Unless you can mechanically detect reliance on UB or IDB, you by >> definition cannot have a reliable or portable program.
> If you rephrased that as "reliable _and_ portable", you'd at least have > a point. Programs that depend on UB or IDB can certainly be reliable as > long as portability isn't required.
UB does not imply reliable or repeatable behavior, so any dependence on UB is inherently unreliable _and_ unportable.
> I can't say I agree. For most programmers most of the time, the only > interesting point is to ensure that the number of bits is sufficient > that overflow just doesn't happen. As long as that's the case, the > difference between one's complement and two's complement (for exmaple) > is entirely irrelevant.
Most of the time, sure. Even 99% of the time. But when you've got a million lines of code, suddenly even the obscure cases become probable. And when you don't have a thorough test suite (who does?) how can you be *sure* you don't have an issue there?
I'm very interested in building languages which can offer a high degree of reliability. While D isn't a language that gets one there 100%, it gets a lot closer than C++ does. I am a little surprised at the resistance to improving C++ along these lines. It's not like pinning down UB is going to break existing code - by definition, it won't.
> Nearly the only time anybody really cares when when writing an extended > precision integer library. You'd accomplish far more by defining (for > one example) the result of the remainder operator when dealing with > negative numbers.
Did that, too, just forgot to mention it.
> You dismiss quite a few architectures that are currently in _wide_ use > as "nutburger", but then you act as if _anybody_ cared about MS-DOS > anymore?
The D programming language explicitly does not support 16 bit platforms. That should leave no doubt about my position on that <g>. C++ was designed to support it, and so is fair game for criticizing its shortcomings in doing so.
I would vote for C++ to explicitly ditch 16 bit support. No problem there!
>> The *exact same* issue exists if the standard says "UB" for integer >> overflow. All the standard is doing here is dumping the problem off onto >> the user, the problem does not go away. It does not aid the user by >> allowing a program to "launch nuclear missiles" to be standard >> conforming behavior upon integer overflow.
> The way you write, you'd think the average programmer spent a > substantial part of his time dealing with integer overflow. This just > isn't the case -- I can hardly remember the last time I wrote anything > where integer overflow was an issue at all.
I rarely worry about it either, but then again I've had several bugs due to it. One in particular is in a storage allocator:
nbytes = dimension * element_size;
Crud, that overflows. I do lots of hash computations, too, which rely on wraparound overflow.
> Limiting portability to deal > with something that isn't a problem to start with is a poor trade off.
This may be the root of our different ideas: We have different definitions of portability.
You (if you don't mind me putting words in your mouth) define it as the likelihood of if a program compiles on X that it will also compile on Y. Whether it works or not depends on how good the programmer is.
I define it as the likelihood of if a program compiles *and* works on X that it will also compile *and* work on Y, regardless of how good the programmer is.
>> 1) reliance on UB or IDB is not mechanically detectable, making programs >> *inherently* unreliable > You're overstating the situation. Certainly some reliance on some UB > and/or IDB is mechnically detectable.
Runtime integer overflow isn't.
>> 2) one cannot rely on what the compiler from machine to machine, version >> to version, or even compiler switch to compiler switch
> This is true only in a purely theoretical sense, and you know it.
In this thread, it was pointed out that new for g++ are optimizations that change the behavior of integer overflow.
My own code has broken from one g++ version to the next from reliance on UB.
> Yes, a > few things change with compiler switches, but 1) only a few, and 2) > compiler switches don't just happen randomly or by accident.
A typical C++ compiler has a bewildering array of switches that change its behavior.
> Yes, when you're writing a library that's intended to be portable to > anything anywhere under any circumstances, these can be major issues.
They're major issues for people who need to develop reliable programs such as, say, a flight control system, or banking software. Such applications need more than reliance on "good" programmers and prayer.
If you're writing a game, who gives a darn if it fails now and then.
> For most people writing end programs, the major issues are things like > keeping track of the make files to ensure that the correct compiler > switches get used on various machines -- and in most cases (at least > IME) these have little to do with UB or IDB and a great deal to do with > the fact that some compilers break perfectly well defined code under > certain circumstances (especially overeager optimization).
g++ 4.1 has 40 options that explicitly modify C++ language behavior. That's 40 factorial interactions. I suspect there are more, like the aforementioned integer optimizations.
>> How much effort have you seen, time and again, going into dealing with >> the implementation defined size of an int? Everybody deals with it, 90% >> of them get it wrong, and nobody solves it the same way as anybody else. >> How is this not very expensive?
> I've seen a lot of effort put into it repeatedly, but I'd say over 99% > of the time, it's been entirely unnecessary from beginning to end.
In D, the effort to deal with it is 0 because the problem is defined out of existence.
> If C > and C++ made it even _more_ difficult to deal with, so people would > learn to keep it from being an issue at all, everybody would really be > better off most of the time.
The way to make things more difficult is to make them compile time errors. Then they cannot be avoided or overlooked. Ideally, if a program compiles, then its output should be defined by the language.
> In any case, C99 and C++ TR1 have both dealt with this for the rare > ocassion that it really is an issue (and, unfortunately, made it still > easier to write size-dependent code when it's completely unnecessary).
It's rarely an issue now because:
1) C++ compilers have dropped 16 bit support (and 16 bit ints). 2) 32 bit C++ compilers all use 32 bit ints. 3) 64 bit C++ compilers also use 32 bit ints.
In other words, C++ has de facto standardized around 32 bit ints.
> C++ 0x will undoubtedly add this to the base C++ language as well. IMO, > this is almost certain to hurt portability, but at least those of us who > are competent can ignore it the majority of the time when it's > counterproductive; languages like Java and D don't even allow that.
In D, you can use a variable sized int if you want to:
typedef int myint;
and use myint everywhere instead of int. To change the size, change the typedef. Nothing is taken away from you by fixing the size of int. It just approaches it from the opposite direction:
C++: use int for variable sizes, typedef for fixed sizes D: use int for fixed sizes, typedef for variable sizes Java: doesn't have typedefs, oh well :-)
>> Conversely, defining the behavior means that one does not have to know >> how other systems work. The less UB and IDB, the easier the porting >> gets, reducing costs.
On Jan 12, 11:42 pm, Jerry Coffin <jcof...@taeus.com> wrote:
> In article <zIOdnW_qHabJjhXanZ2dnUVZ_uWln...@comcast.com>, > > How much effort have you seen, time and again, going into dealing with > > the implementation defined size of an int? Everybody deals with it, 90% > > of them get it wrong, and nobody solves it the same way as anybody else. > > How is this not very expensive?
> I've seen a lot of effort put into it repeatedly, but I'd say over 99% > of the time, it's been entirely unnecessary from beginning to end.
As an example look at boosts rational class, this effectively cubes the problem, and its trivial to get an invalid result (using built in int's) unless you ( the programmer) work very hard. The rational class simply ignores the issue and leaves you to do all the work. Why does it do that?. Because the C++ integer types are a mess that takes a lot of work to deal with.
> If C > and C++ made it even _more_ difficult to deal with, so people would > learn to keep it from being an issue at all, everybody would really be > better off most of the time.
:-) Love it !!! sand(head)->bury() :-)
> This seems to indicate little more than that you've ported code only > within a relatively small range. At least based on what you've said, > porting D code to (say) a Microchip PIC or any of a large number of DSPs > would be somewhere between excruciating and impossible.
IIRC Its difficult to implement 8 bit integer math to be standard conforming on 8 bit PIC using fundamental types , because the C standard requires conversion to an int (16 bits min) (Again IIRC) many Microchip compilers for 8 Bit PIC don't convert 8 bit to 16 bits before the calc, some PICS have 8 x 8 hardware multiply so you can see their logic.
> Just for a few examples, try to find a reasonable way to support an 8- > bit char in:
Again some available instructions are not useful due to semantics of C ints, eg signed x unsigned multiply, IIRC.
There is a nice solution to all this. Make int (and family) optional typedef for some lower level UDT(which must fit in the loose boundaries of 'int' defined in std) If the standard semantics are not useful (as in 8 bit PIC example) simply dont define the typedef. Where defined, if the chosen rep is not implemented on that platform... it don't compile.
generic algorithms for e,g int-like types can where necessary be implemented using templates/ Concepts which leave the detailed semantics to the types.
Int-like Types can disallow expressions ( e.g signed + unsigned fails to compile, or --> signed, or promotes internally to safe containing type and throws if result is out of range, etc, etc)
OTOH If current int semantics are acceptable it is there, and can be customised within the confines of the std spec, if required.
IOW it is possible to have your cake and eat it. I believe the mechanisms are all there, except the one to make fundamental types optional, or chose the rep.
> > I think I'm getting what you say. Let me see if I understand your point > > correctly.
> > * Unsigned has defined behavior for all values and all operations, save > > for division by zero.
> Yes. I would say that unsigned values in c++ form a "closed" set of > values, such that all (defined) arithmetic operations within this > closed set, yield a value that is a member this set. Signed values in C > ++ form an "open" set of values; so - even though all (defined) > operations with signed types yield a member of this open set - not all > members of the set can be represented by a signed type. And whenever a > calculation yields one of these un-representable values, overflow is > said to have occurred.
> > * Int does NOT enjoy that property; if it were guaranteed to use 2's > > complement representation, it WOULD have. That was the fatal flaw in my > > reasoning - I thought C++ always uses 2's complement for int.
> Even 2's complement representation does not guarantee that signed > operations will wrap. A C++ compiler could generaste "add with > overflow" instructions for signed types, and generate "add - no > overflow" instructions for unsigned types and then trap on the > overflow condition flag.
Some processors do not wrap integers. Rather, they "saturate" meaning that overflow conditions results in the maximum value for that type. i.e. int g=INT_MAX; g+=2; assert(g==INT_MAX); This is useful in DSP, where an overflow condition then results in a still usable value, rather than something totally wrong. But it is certainly no longer a finite field. It is not just found in dedicated DSP: Anybody that uses Intel MMX could play around with saturation arithmetic at the hardware level.
"int, unsigned, signed" etc are really shorthand for "do whatever arithmetic is the most natural for my processor." I think this has always been the intent (back in the early days of C, the choice was between "one or two complement," rather than "saturate or wrap."
Most processors of course uses finite fields for unsigned, and the signed part uses two complement. It is always preferable to use unsigned -- if nothing else an optimizer has a far easier time manipulating unsigned values -- there shifts and mult/div are interchangable, and implicit conversions do not have to be "signed extended."
Walter Bright wrote: > Pete Becker wrote: >> On 2008-01-12 01:12:44 -0500, Walter Bright said: >>> On the other hand, Java went the route of eliminating undefined and >>> implementation defined behavior, and has had astonishing success >>> (largely at C++'s expense).
>> Post hoc ergo propter hoc.
> I agree I can't prove it. But neither can one prove James' remark:
> "That's subjective: C and C++ have opted *not* to limit itself to > mainstream architecture, and are probably more widespread than any > other languages partly as a result of that."
> C++ (with a few extensions) was remarkably well suited to writing apps > for DOS, and C++ rode the big surge in DOS and PCs up. I don't believe > C++'s success is due to it being supported on nutburger CPU designs. > Let's face it - the PDP-10 is dead.
But most processors are embedded, and C and C++ have huge market share in the embedded world (where C is still likely more widely used, but C++ has been increasing for many years).
[snip]
> I've read many diatribes against Java, from the ignorant to the well > -informed, and not one ever complained about the integer math behavior > being nailed down.
Compared to Java's other problems for numerical code, that one doesn't even make the radar. (But let's be fair, Java has improved a lot in its 7 versions to date.)
Walter Bright wrote: > James Dennett wrote: >> it allows for optimizations based on assumptions of non-overflow,
> Apparently the new g++ does that, though my question on what those were > is so far unanswered. My experience with optimizations that change the > behavior is that customers call it an optimizer bug, even if the fault > lies with their reliance on UB.
I searched a bit online and found such a case. Compile this code:
#include <cstdio> using namespace std;
int main() { int u = 2000000000; int v = (u * 2) / 2; printf("%d\n", v);
}
with and without the -fwrapv flag. It produces different results.
Walter Bright wrote: > James Dennett wrote: >> DSPs are far from dead, and many don't have support for 8-bit >> bytes in any reasonable fashion.
> I don't know much of anything about DSPs. But I know that many > specialized CPU chips tend to have specialized languages that come with > them, and that's perfectly reasonable. I don't think anyone wants to > recompile Office for a DSP, anyway :-)
>> Fortunately in the real world it's >> not hard for good programmers to have reliable and portable >> programs.
> I disagree. For example, I've never found a non-trivial C++ program that > would port successfully between 16 and 32 bits, even by expert > programmers (far better than just good ones), without doing some > adjustments and bug fixing.
I have seen numerous of them; in fact, they've been pretty much the norm (though 32- to 64- bit portability was the last question, and some OS vendors threw spanners into the works with their APIs there).
> Changing endianness often breaks C++ code, as do changes in struct > member padding.
It often breaks low-level C++ code that was written carelessly. I've ported millions of lines of code that do NOT have issues here. It seems that you've somehow been exposed to less good code than one would hope.
> Varying int sizes, and char signedness, also break > programs. The reason is simple, it is really really hard to look at a > piece of code and verify it doesn't have these issues. It is impossible > to test for portability issues without actually porting the code.
Your estimate of how much of a problem these things are does not match mine.
> I have a large piece of code that works for DMC++, VC++, and an older > g++. Upgrading to the latest g++ breaks it. It still compiles, it just > produces wrong answers. I don't know yet what went wrong, but portable > C++ ain't.
I'd bet it's not a problem with integral type sizes if you're using reasonable coding practices.
>> It allows for implementations which diagnose all overflows;
> Are there any such implementations?
I don't know. Even if there aren't, the standard allows for one in future. The C++ market has been around a long time, and will continue to be significant for a long time yet, and a standard that chooses too often to fix things when it could allow for better QoI is not a good standard.
>> it allows for optimizations based on assumptions of non-overflow,
> Apparently the new g++ does that, though my question on what those were > is so far unanswered.
There are (at least experimentally) some flags which will alert you when g++ optimizes based on wraparound. There's also something like -fno-wrapv which disables those optimizations at some cost in code speed (but the cost is <<< 1% for typical application code).
> My experience with optimizations that change the > behavior is that customers call it an optimizer bug, even if the fault > lies with their reliance on UB.
C++ users also often call RVO a bug, and they call reordering their operations between sequence points bugs, and they call hiding by name a bug, and they call diagnostics for their buggy code bugs. There are trade-offs to be made: there's some merit in fewer complaints from customers, to be sure, but there's cost to disabling optimizations too. Maybe what's needed is a "simple" mode where non-obvious optimizations are disabled and code runs more slowly, and a "strict" mode where the optimizer is allowed to do anything consistent with the language spec.
>> and diagnostics when such optimizations are made in ways that >> could alter behaviour of code; it encourages implementations to >> provide diagnostics for unsafe use.
> I don't see any way to issue warnings on unsafe overflow use based on > static analysis of code.
Please note that I did not restrict this to *static* analysis. However, range checking in static analysers is fairly common, though the type systems of C, C++ (and Java, C#, D, etc.) make it rather hard to be strict without generating a huge number of false positives.
I'll mention again: turning an overflow into wraparound is not generally safe -- code which assumes no overflow is broken in either case.
>> Wrapping semantics may be well-defined, but are *NOT* always safe.
> I'm not arguing that they are safe. I'm saying that well-defined > semantics make code that, once tested, can be reliably ported.
I'd prefer to have code that can be made safe. Porting is secondary (though the average piece of code I've written in my career runs on maybe 5 different platforms).
>> Safety depends on what >> the specification/requirements call for. Pretending that >> modular arithmetic is always the right solution is simplistic.
> I'm not arguing that a specific is always the right solution. I'm > arguing that undefined behavior is the wrong solution because it is, by > definition, not the "right" solution.
But it's a meta-solution: it allows implementations to offer a choice of solutions, and for the market (rather than a BDFL or a committee) to determine which is most useful.
>> Allowing for diagnostics of overflow in many senses can serve >> a broader community better than oversimplifying.
> Does any C++ implementation diagnose integer overflow at runtime?
I do not know of any. My knowledge is far from perfect. I wouldn't be surprised if some tools attempted to do this.
>>> How much effort have you seen, time and again, going into dealing with >>> the implementation defined size of an int? >> Very little; it's a trivial thing. Good programmers use a type >> which is guaranteed to have the properties they need, so they >> won't use unadorned int for anything more than a -32767 to +32767 >> range
> There aren't very many "good" C++ programmers, then <g>.
There's sadly a shortage of competent programmers, and it's somewhat independent of the implementation language.
>> unless they know that their target implementations support >> a larger range, they'll just use an int32_t-like type or a long, >> or long long, as needed. Certainly I've seen mistakes made,
> I was in the trenches when the big shift from 16 bit C++ to 32 bit C++ > took place, and ints doubled in size. I can tell you for a fact that > various schemes for portably doing this were debated ad nauseum, and > that almost none of them actually worked when it became time to do the > real port. If you want an example, look no further than windows.h.
I saw the 16- to 32-bit shift, and the 32- to 64-bit shift, and how well it was handled seemed to vary by community, with the Windows world having huge problems and the Unix world having relatively few (because Unix was diverse from early on, and Windows was a monoculture). The very uniformity and guarantees that Win16 (and later Win32) laid down caused portability problems to later generations of machines; the flexibility that was built in to Unix-style specifications aided that same portability.
> The converse was also true, C++ code developed for 32 bit machines > rarely ported to 16 bits without major effort, often a rewrite was > required.
> Even in D, people cannot seem to shake their C/C++ heritage in worrying > about the size of an int, and typedef it "just in case". I know I am > much happier with "int" than "int32_t". The latter just stinks. Sorry.
It stinks to use "int" if it's not going to be as fast as "int64_t" in some context, certainly. And how am I to get the fastest type for operations that could be done in 16 bits? intfast16_t, or... I have to guess with D whether to use short or int, and profile on every platform? If I say that I need a type of exactly 32 bits, I'm overspecifying the size while underspecifying optimization goals (for speed or for space).
But if your focus is narrow enough that you care only about mainstream desktop and server platforms, it's a perfectly reasonable trade-off.
> It's at least possible you aren't seeing actual problems with int sizes > these days because practically every C++ compiler sets them at 32 bits, > even for 64 bit CPUs. So you never know if your use of typedefs is > correct or not.
sizeof(long) varies still, on common systems, as does sizeof(void*). But my knowledge doesn't come just from "these days" -- as with many, I started working on 8-bit systems with 16-bit addressing. Please don't assume that those who disagree with you do so because they lack experience or knowledge. It's common that they have knowledge which you don't (and vice versa).
>> but I've seen mistakes made in languages with fixed-sized types too.
> So have I. But the question is how prevalent are such mistakes, versus > mistakes from the int sizes changing?
>>> Everybody deals with it, 90% >>> of them get it wrong, and nobody solves it the same way as anybody else. >>> How is this not very expensive?
>> Your perspective/experience do not match mine. Many places deal >> with it, most of the get it right, and most of them solve it in >> very similar ways, moreso since C99 et al standardized typedefs >> for various integral types. The expense is insignificant in all >> competently run projects I've seen.
> I'll bet that in most of those competently run projects, the code has > never been ported to a compiler with different int sizes, so how good a > job they did has never been tested. I saw how well (i.e. badly) it > worked in the last big shift from 16 to 32 bit code.
I'd take that bet (though I'll also say that it's common for other reasons just to say that platforms smaller than 32-bits are unsupported because they don't have the horsepower to run the application).
> Would you like to try porting one of the ones that get it right to 16 > bits? I've got a beer that says it fails <g>.
I'll have to dig up an emulator for a 16-bit platform to take that bet, but it sounds like a good excuse for a beer... I'll keep you posted.
On Jan 13, 1:48 pm, Walter Bright <wal...@digitalmars-nospamm.com> wrote:
> I've read many diatribes against Java, from the ignorant to the well > -informed, and not one ever complained about the integer math behavior > being nailed down.
That's because the potential performance loss is hidden, doubly so if the C++ compilers in wide use don't take advantage of the license to optimize under the no-overflow assumption.
A similar example is that certain Fortran code runs circles around C because of the no-alias assumption, yet few programmers have complained that C doesn't un-define the behavior in the presence of aliasing.
The slight performance loss in the integer arithmetic case may still be tolerable in exchange for a fully nailed-down specification, of course.
On Jan 13, 1:48 pm, Walter Bright <wal...@digitalmars-nospamm.com> wrote:
> > it allows for optimizations based on assumptions of non-overflow,
> Apparently the new g++ does that, though my question on what those were > is so far unanswered. My experience with optimizations that change the > behavior is that customers call it an optimizer bug, even if the fault > lies with their reliance on UB.
Greg Herlihy answered this question in another subthread. In short, 2*x/2 == x. This is the kind of optimization people expect, not the kind they object to.
Walter Bright wrote: > g++ 4.1 has 40 options that explicitly modify C++ language behavior. > That's 40 factorial interactions. I suspect there are more, like the > aforementioned integer optimizations.
Not according to my math. Assuming that each switch is simply on/off there are 2^40 (yes a very big number but many orders of magnitude less than the one you state. 2^40 will fit without overflow into a 64-bit integer type 40 factorial won't. That matters in the context of what we are discussing)
Furthermore I am not sure that all the options are compatible with each other. If they are not then the alternatives get further reduced.
On Jan 10, 5:06 pm, Walter Bright <wal...@digitalmars-nospamm.com> wrote:
> Bart van Ingen Schenau wrote:
> > On the other hand, I have used a compiler for a DSP that supports > > saturating arithmetic (overflow gets clipped to the largest value). > > Although the compiler writers decided otherwise, this mode of > > arithmetic could have been selected to be used for signed operands > > without any violation of the standard.
> The compiler writers made a wise move. Such an unusual mode could > silently introduce pernicious bugs when porting existing, debugged code > to it.
> Since there is no way to defend against such possible errors in one's > code, and the overwhelming majority (dare I say all?) compilers > implement it in one way, that way should be standardized.
I would not be happy with that. I suspect that most uses of overflow in signed integers are unintentional, and such porting would not be introducing bugs -- the software is probably already buggy. I personally would much prefer a compiler that trapped unintentional overflow rather than allowing the program to continue blithely on. I understand that for performance reasons, or ease of implementation, or some other reason most compiler implementors currently prefer wrap- around, but for the standard to mandate it would be a major disservice.
For the (I suspect) rare cases where wrap-around is useful unsigned types are available, and the use of a signed type in such a situation suggests to me that the programmer simply didn't take the possibility into account.
On 2008-01-13 00:48:54 -0500, Walter Bright <wal...@digitalmars-nospamm.com> said:
> Pete Becker wrote: >> On 2008-01-12 01:12:44 -0500, Walter Bright said: >>> On the other hand, Java went the route of eliminating undefined and >>> implementation defined behavior, and has had astonishing success >>> (largely at C++'s expense).
>> Post hoc ergo propter hoc.
> I agree I can't prove it. But neither can one prove James' remark:
I didn't say you can't prove it. I said your argument is nonsense.
>> Programmers who did serious numeric computations hated Java in its >> original incarnation, because the restrictions it imposed on >> floating-point math made it abominably slow on Intel processors.
> Yeah, I know about that. They went too tight with the floating point in > eliminating IDB, and since backed off a turn of the screw. C++ could > easily tighten down the screws several full turns, though.
Maybe. But your claim was that eliminating undefined behavior and implementation defined behavior was somehow responsible for Java's success. This example shows just the opposite.
> I've read many diatribes against Java, from the ignorant to the well > -informed, and not one ever complained about the integer math behavior > being nailed down.
Again irrelevant to your claim that these things were responsible for Java's success.
Java's success was largely the result of having a marketing department. Naive programmers believe their claims that tightening the rules makes programming far easier, despite the absence of concrete evidence.
>> Note that these are not ancient "nutburger" architectures -- these are >> both current and in _wide_ use. Just for an obvious example, the last >> time I was in Costco, they had a brand new HD-DVD player that (on the >> outside of the box!) bragged about using a SHARC processor.
> The C++ compiler for sharc has many sharc specific extensions. It isn't > hard to imagine a D variant that would do the same. You'd have the same > difficulties porting C++ code to the sharc C++ compiler as you would > porting standard D to sharc specific D.
> As for the 32 bit sharc characters, they would map on to the "dchar" 32 > bit D character type. I imagine a D for sharc would issue a compile > error on encountering a "char". At least the programmer then has a clue > he needs to use "dchar" instead, and perhaps double check the code using > that variable.
> As for sharc "shorts" being 32 bits, that doesn't help you if your code > needs to address or manipulate 16 bit data. Just taking away the 16 bit > type doesn't magically make the code work, even if it is in C++ and > still compiles.
> Again, just being able to compile the code doesn't mean it's portable.
That's a good point. I think the crux of the matter is this:
Kernighan & Ritchie fostered mapping C integral types (short, int, long) to the most "fit" types on the target machine in a highly flexible way (e.g. they could all bear the same size), with int modeling the natural word size. That strategy made it feasible to define a compiler for a wide range of machines and to write code for each. The same strategy conjectures that differences among integral types sizes on different platforms is either of little importance, can be reliably taken care of by the programmer.
In my opinion, experience has shown that indeed C's considerable flexibility in mapping names to machine types made it highly implementable on all sorts of machines, but that the second story turned out to be less successful.
Other languages chose to fix the sizes of integral types, strategy that made it less implementable on various machines, but made things easy for porting existing code among machines on which the language could successfully be ported.
I wonder what would be the "best" way. In the latter systems, for example, an implementation for a certain CPU might prohibit use of some types, e.g. "char". Everything in the client code and standard library of that language relying on "char" must go. An interesting conclusion is that such a language must have generic (a.k.a. template) function capabilities, such that commonly useful functions (such as string functions, parsing and conversion functions etc.) do not commit to any types in particular. I think such a system would be a net improvement.
Bo Persson wrote: >>> My experience porting D code between platforms is it ports easier >>> than the equivalent C++ code. > Porting is easier if you limit the number of potential platforms.
Sure, but I was comparing porting C++ from platform A to B with porting D from A to B. The latter was noticeably easier.
>> Porting Java is easy too, if your target platform supports it. > Porting Java is hard, if you haven't ported its platform first!
Porting C++ compilers is pretty hard, too. How many programmers do you know who can write a code generator?
> We had a discussion just last week with a Java developer on reusing > his web server code on the mainframe.
> - "Oh dear! That's just Java 1.5, I need 1.6 generics for my code. > Limiting myself to 1.5 features will cost you a lot more!"
But isn't Java implemented in C? C is more portable and available on every platform, so he should just recompile it and he's good to go.
A look through the source of Boost and STL will show that compiler support for various C++ features is a perennial problem. Source code portability, when using advanced C++ features, has always been a serious problem. How long do you think it will take for all C++ compilers to implement all of C++0x?
Pete Becker wrote: > Java's success was largely the result of having a marketing department.
While a great marketing department is certainly helpful, it is a serious mistake to dismiss Java that way (at least for a language designer, it is).
I regularly talk to heavy Java developers, with the aim of finding out what works and what doesn't work for them. The same goes for C++. I even talk with the Lisp guys when I can find one!
> Naive programmers believe their claims that tightening the rules makes > programming far easier, despite the absence of concrete evidence.
Given the code in C++ and Java:
a = foo() + bar();
which one would I have to rewrite as:
tmp = foo(); a = tmp + bar();
if there is an order of evaluation issue? How would I reliably detect such an issue in the C++ version (putting on my QA hat)?
From a QA standpoint, it's clearly easier to verify the Java version than the C++ one. foo() is evaluated first, then bar(). That's easier to deal with than hmm, which one happens first, and when might that change, and do I have any order dependencies here?
Java does have a predictability issue with the gc, but that is separate from integer, OOE, etc., issues.
Walter Bright wrote: >> Note that these are not ancient "nutburger" architectures -- these are >> both current and in _wide_ use. Just for an obvious example, the last >> time I was in Costco, they had a brand new HD-DVD player that (on the >> outside of the box!) bragged about using a SHARC processor.
I'd like to expand on this issue a bit. The C++ sharc compiler has:
1) 32 bit shorts 2) 32 bit chars 3) 40 bit doubles
While this is legal for a C++ compiler, and arbitrary C++ code may compile successfully, that doesn't at all mean it will run. For example, take zlib, the open source compression library, that's been ported to many platforms. What are the odds that is going to work out of the box with this compiler? I'd say, zero. You're going to have to redesign large sections of it.
For example, let's look at shorts. The only reason people use shorts these days is:
1) to reduce memory consumption 2) to manipulate 16 bit values in an existing data structure 3) to specifically use 16 bit math 4) to manipulate 2 bytes at once
because shorts are often slower than ints. So, any code using shorts for 2..4 will have to be recoded.
Similar reasoning applies to chars. Chars are very often used, not to store characters, but to do byte manipulation. You cannot do byte addressing on the sharc. All the C++ code you wrote to do byte manipulation will need to be thoroughly re-engineered.
Let's look at a hypothetical D compiler for the sharc. A straightforward implementation would probably simply make chars and shorts illegal. Code that uses characters could be edited to use dchars instead (D's 32 bit character type). Code that manipulates bytes and 16 bit values is going to have to be re-engineered, just like the C++ code will be. The only real difference to the programmer is that the D compiler will *tell* you you cannot use chars or shorts, while the C++ compiler will compile it anyway and produce code that won't work properly, leaving you to find it with a debugger.
The 40 bit doubles are a bit more problematic. I'd recommend for D to just go with 40 bit doubles on that platform, and then let the user deal with the possible precision problems just as he would have to with C++.
The bottom line is, which language would be more work for the sharc programmer to port code to? Only experience can tell for sure, but I suspect D would come out ahead as the compiler will flag code that won't work on sharc, while C++ will compile it anyway and leave it at the mercy of his test suite to find the problems.
> Walter Bright wrote: >> James Dennett wrote: >>> it allows for optimizations based on assumptions of non-overflow,
>> Apparently the new g++ does that, though my question on what those were >> is so far unanswered. My experience with optimizations that change the >> behavior is that customers call it an optimizer bug, even if the fault >> lies with their reliance on UB.
> I searched a bit online and found such a case. Compile this code:
> #include <cstdio> > using namespace std;
> int main() { > int u = 2000000000; > int v = (u * 2) / 2; > printf("%d\n", v); > }
> with and without the -fwrapv flag. It produces different results.
I did a google search for: [optimizer fwrapv printf]
Francis Glassborow wrote: > Walter Bright wrote: >> g++ 4.1 has 40 options that explicitly modify C++ language behavior. >> That's 40 factorial interactions. I suspect there are more, like the >> aforementioned integer optimizations.
> Not according to my math. Assuming that each switch is simply on/off > there are 2^40 (yes a very big number but many orders of magnitude less > than the one you state. 2^40 will fit without overflow into a 64-bit > integer type 40 factorial won't. That matters in the context of what we > are discussing)
While your math is right (and mine was wrong), it doesn't matter. The universe will end before you can test all combinations of those switches with the test suite. Then, there are all the other g++ switches, which go on for pages.
> Furthermore I am not sure that all the options are compatible with each > other. If they are not then the alternatives get further reduced.
Sure, but can you get them all tested before the sun turns into a red giant?
Walter Bright wrote: > Bo Persson wrote: >>>> My experience porting D code between platforms is it ports easier >>>> than the equivalent C++ code. >> Porting is easier if you limit the number of potential platforms.
> Sure, but I was comparing porting C++ from platform A to B with > porting D from A to B. The latter was noticeably easier.
It could be because the variation is smaller. There are some platforms were you cannot easily implement D at all, like IBM zSeries with EBCDIC character set and non-IEEE floating point. C++ has no problem with that.
>>> Porting Java is easy too, if your target platform supports it. >> Porting Java is hard, if you haven't ported its platform first!
> Porting C++ compilers is pretty hard, too. How many programmers do > you know who can write a code generator?
The point was rather that Java is very hard, if the intended platform doesn't support the spec. It might require adding dedicated hardware:
>> We had a discussion just last week with a Java developer on reusing >> his web server code on the mainframe.
>> - "Oh dear! That's just Java 1.5, I need 1.6 generics for my code. >> Limiting myself to 1.5 features will cost you a lot more!"
> But isn't Java implemented in C? C is more portable and available on > every platform, so he should just recompile it and he's good to go.
Well, IBM believe they should decide what Java version to run on z/OS. It also needs to access the special Application Assist hardware to do IEEE floating point. C and C++ doesn't have to do that.
For our inhouse applications portability is no concern, but Java still insists on using a portable data format. Even if it is more expensive. A lot more, in this case!
> A look through the source of Boost and STL will show that compiler > support for various C++ features is a perennial problem. Source code > portability, when using advanced C++ features, has always been a > serious problem.
IMO, Boost is trying much too hard to support pre-standard compilers. If they tried to support 1-2 releases of each compiler, instead of 4-5, the number of workarounds needed would be drastically reduced.
> How long do you think it will take for all C++ > compilers to implement all of C++0x?
> On Jan 10, 5:06 pm, Walter Bright <wal...@digitalmars-nospamm.com> > wrote:
> > Since there is no way to defend against such possible errors in one's > > code, and the overwhelming majority (dare I say all?) compilers > > implement it in one way, that way should be standardized.
> I would not be happy with that. I suspect that most uses of overflow > in signed integers are unintentional, and such porting would not be > introducing bugs -- the software is probably already buggy. I > personally would much prefer a compiler that trapped unintentional > overflow rather than allowing the program to continue blithely on. I > understand that for performance reasons, or ease of implementation, or > some other reason most compiler implementors currently prefer wrap- > around, but for the standard to mandate it would be a major > disservice.
> For the (I suspect) rare cases where wrap-around is useful unsigned > types are available, and the use of a signed type in such a situation > suggests to me that the programmer simply didn't take the possibility > into account.
I agree completely: C++ should not define signed arithmetic overflow. After all, a C++ program already has the option of performing unsigned arithmetic in order to avoid overflow. Signed arithmetic, therefore, should be reserved for those calculations in which an overflowed value has no sensible representation (because the value falls outside of the range of integers that the integral type can represent).
I would guess that for many - if not most - programs that perform integer arithmetic, defining overflow to "wrap around" would be counter-productive. After all, it is hard to imagine how an accounting, payroll or billing program would benefit if - the sum of a large number of positive integers - were to turn out to be negative. Clearly, in such a situation, having the program abort would be preferable than having the program carry on - only with this negative sum filling in for the expected (but unrepresentable) value.
Moreover, having a C++ program trap on signed overflow is not some hypothetical possibility. At least one C++ compiler (g++) offers an option (-ftrapv) to trap on signed arithmetic overflow.
For example, compiling the source file below:
// overflow.cc
#include <limits.h> #include <iostream>
unsigned unsigned_overflow() { unsigned u = UINT_MAX;
std::cout << "Testing unsigned overflow...\n"; return u + 1; }
signed signed_overflow() { int s = INT_MAX;
std::cout << "Testing signed overflow...\n"; return s + 1; }
int main() { int u = unsigned_overflow();
std::cout << "Unsigned overflow value: " << u << "\n";
In article <5dSdnf-pHcH0MhTanZ2dnUVZ_i2dn...@comcast.com>, wal...@digitalmars-nospamm.com says...
> Jerry Coffin wrote: > > the Java standard (at least the last time I > > looked at it) simply ignores the issue entirely or (more often) includes > > some phrase like "IEEE floating point", that makes it sound like the > > issue has been dealt with, but when you get down to it doesn't really > > mean much at all.
> IEEE 754 floating point arithmetic means a lot more than nothing, and > certainly far more than C++ floating point, but you're right that it > doesn't nail it down 100%.
For Sun it meant Java code executed far better on hardware built by Sun than by many of their competitors. For most Java users, however, it meant nothing more than a false sense of security (then again, Java in general seems to have been designed specifically and marketed with the idea of instilling a false sense of security).
[ ... ]
> > These requirements still limit practical portability, even among modern, > > widely-used architectures. Quite a few DSP and even some PDAs, cell > > phones, etc., don't support any 8-bit type, just to give one example.
> I disagree, because nothing would permit a D *variant* from being > customized to unusual architectures. Such would not be less useful than > simply dumping the problem off to all users.
Yes, it would. It's entirely possible and indeed quite practical and reasonable to write C and C++ code that works perfectly fine depending only upon what C and C++ guarantee for char (for one example). The fact that under some circumstances char might be 16, 32 or even 64 bits doesn't bother the code a bit. Even code that is written to depend on 8- bit chars is usually quite easy to convert to remove that dependency.
D goes the opposite route: by guaranteeing that char will always be 8 bits, it (tacitly?) encourages people to write their code to depend on that fact. It's a bit difficult to guess at how easy it is to write D code that won't break with other sizes of char, since there's apparently no way to test such code right now.
> I suspect that this is an even better solution for those programming > such machines, because they won't be under the delusion that code that > has never been tested under such conditions would have been "portably" > written with some wrong notion of portability.
I think this is a delusion. People who normally work with such machines are unlikely to be deluded about the amount of code that's really portable. Nonetheless, if somebody _wants_ to do so, they most certainly _can_ write C and/or C++ code that works fine with various sizes of char. With D they can't do any such thing, because anything with a different size of char, by definition, isn't D.
[ ... ]
> A more relevant question is how many programmers are programming for > these oddballs, vs programming for mainstream computers?
That, of course, is a difficult question to answer. The ratio of programmers to CPUs is almost certainly smaller than with mainstream computers, but sales volume is also _quite_ high in many cases, making it difficult to figure out the actual number.
[ ... ]
> UB does not imply reliable or repeatable behavior, so any dependence on > UB is inherently unreliable _and_ unportable.
This is nonsense and you know it. UB does not _imply_ reliable or repeatable, but it does not preclude either one. The standard specifically allows an implementation to define the result of particular undefined behavior -- and when portability isn't a concern, the fact that it's defined only by the implementation is entirely irrelevant.
[ ... ]
> But when you've got a > million lines of code, suddenly even the obscure cases become probable. > And when you don't have a thorough test suite (who does?) how can you be > *sure* you don't have an issue there?
Being truly "*sure*" of anything is pretty rare -- even if it's required by the language, it's nearly impossible to be sure you couldn't run across some obscure bug in the compiler. Even on projects considerably less than a million lines, I've certainly done so.
> I'm very interested in building languages which can offer a high degree > of reliability. While D isn't a language that gets one there 100%, it > gets a lot closer than C++ does.
I've yet to see convincing evidence of that -- though I'll admit I haven't looked very hard, and such evidence would be off-topic here in any case.
> I am a little surprised at the > resistance to improving C++ along these lines. It's not like pinning > down UB is going to break existing code - by definition, it won't.
Yes and no -- it won't break any portable code. It could easily break a lot of code that depends on behavior of a specific implementation.
> > Nearly the only time anybody really cares when when writing an extended > > precision integer library. You'd accomplish far more by defining (for > > one example) the result of the remainder operator when dealing with > > negative numbers.
> Did that, too, just forgot to mention it.
I'm glad to hear that.
> The D programming language explicitly does not support 16 bit platforms. > That should leave no doubt about my position on that <g>. C++ was > designed to support it, and so is fair game for criticizing its > shortcomings in doing so.
> I would vote for C++ to explicitly ditch 16 bit support. No problem there!
To accomplish what? For most practical purposes, 16-bit platforms were "ditched" when exception handling was added. OTOH, if somebody's willing to go to the time, effort, etc., of implementing it for a 16-bit platform, and meets all the requirements, more power to them.
I see no reason to warp the requirements specifically to support 16-bit platforms -- but I also see no reason to explicitly rule them out.
[ ... ]
> Crud, that overflows. I do lots of hash computations, too, which rely on > wraparound overflow.
I've done quite a few hashes too -- but on unsigned types. For unsigned types, the rules in C and C++ are quite explicit and have always been quite sufficient for my purposes.
[ ... ]
> You (if you don't mind me putting words in your mouth) define it as the > likelihood of if a program compiles on X that it will also compile on Y. > Whether it works or not depends on how good the programmer is.
That's not really correct at all. I think portability depends not only on how "good the programmer is", but upon the programmer's intent. Java, for one example, attempts to ensure that every correctly written program is portable.
While I can see the motivation for that, I disagree with it. While portability is a useful attribute, there are also many perfectly good reasons to write code that's not portable.
Just for example, for work I've written a number of custom debuggers for specific platforms (Windows more often than anything else). As almost anybody can guess, virtually none of this code is portable -- and I can't imagine any reasonable change to the language that would enhance its portability in any way.
I should note that although the code doesn't port to other systems, back when Windows NT was current, I had no problem at all with compiling and _using_ quite a bit of it (completely unchanged) on Intel, MIPS and Alpha based machines. On Intel, I believe it worked fine with every compiler I had handy at the time as well (at least with MS, Borland, Intel and GNU).
> I define it as the likelihood of if a program compiles *and* works on X > that it will also compile *and* work on Y, regardless of how good the > programmer is.
That makes it sound a great deal like D probably won't support most of what I develop at all.
> >> 1) reliance on UB or IDB is not mechanically detectable, making programs > >> *inherently* unreliable > > You're overstating the situation. Certainly some reliance on some UB > > and/or IDB is mechnically detectable.
> Runtime integer overflow isn't.
Yes and no. It can detect when runtime integer overflow becomes a possibility, and even (for example) show the source of the values that could result in the overflow.
[ ... ]
> A typical C++ compiler has a bewildering array of switches that change > its behavior.
Change what behavior? I've certainly run across switches that simply broke the compiler for certain code, but these are more or less irrelevant -- the language standard means little with respect to a compiler that doesn't (as used) implement that language.
> > Yes, when you're writing a library that's intended to be portable to > > anything anywhere under any circumstances, these can be major issues.
> They're major issues for people who need to develop reliable programs > such as, say, a flight control system, or banking software. Such > applications need more than reliance on "good" programmers and prayer.
We must have radically different experiences. When I've dealt with high reliability systems, portability was _never_ an issue. Rather the contrary, high-reliability hardware was one of the first requirements, and quite a lot of the code stood no chance of ever running on a typical off-the-shelf PC or anything very similar at all.
Likewise, what was defined as part of the language was largely irrelevant as well -- even when the language required particular behavior, that behavior had to be verified with the specified implementation before it could be depended upon. Granted, attempts have been made (at times) at verifying/certifying implementations to assure that they really conformed with the language requirements. Few (if any) of these efforts seems to survive today though...
[ ... ]
> g++ 4.1 has 40 options that explicitly modify C++ language behavior. > That's 40 factorial interactions. I suspect there are more, like the > aforementioned integer optimizations.
Even at best, your math is flawed. Though I don't use g++ on a regular basis, my experience with other compilers indicates that many of the switches have no interaction at all.
James Dennett wrote: >>> It allows for implementations which diagnose all overflows; >> Are there any such implementations? > I don't know. Even if there aren't, the standard allows for one > in future. The C++ market has been around a long time, and will > continue to be significant for a long time yet, and a standard > that chooses too often to fix things when it could allow for > better QoI is not a good standard.
Yes, C++ has been around for 20+ years. If a particular characteristic of C++ compilers, allowed for by the standard, and not too hard to implement, has not emerged in that time, then I argue there is no significant interest in it. And so allowing for it in the standard is not compelling, particularly if there are costs associated with allowing it.
> There's also something > like -fno-wrapv which disables those optimizations at some cost in > code speed (but the cost is <<< 1% for typical application code).
An optimization which breaks code and offers <<< 1% improvement is a bad idea to implement, even if allowed by the standard.
> Maybe what's needed is a > "simple" mode where non-obvious optimizations are disabled and > code runs more slowly, and a "strict" mode where the optimizer > is allowed to do anything consistent with the language spec.
The siren song of more compiler switches should be resisted as much as possible. Put wax in your ears and rope yourself to the mast :-)
> I'll mention again: turning an overflow into wraparound is > not generally safe -- code which assumes no overflow is broken > in either case.
The code this optimization breaks is not always incorrect code. See the link I posted in my reply to Andrei.
> I'd prefer to have code that can be made safe.
Leaving it as UB doesn't help at all with that.
> But it's a meta-solution: it allows implementations to offer > a choice of solutions, and for the market (rather than a BDFL > or a committee) to determine which is most useful.
But that means you're encouraging *reliance* on undefined behavior.
>> There aren't very many "good" C++ programmers, then <g>. > There's sadly a shortage of competent programmers, and it's > somewhat independent of the implementation language.
The bar for competence in C++ is a lot higher than for other languages. That's pretty marvy if you're a top C++ expert and can command $5000/day in consulting fees, but it stinks if you're on the other side of that having to write the checks. Hence the demand for languages where the costs are lower, a demand that I think C++ dismisses a little too easily.
> The very uniformity and guarantees > that Win16 (and later Win32) laid down caused portability > problems to later generations of machines; the flexibility > that was built in to Unix-style specifications aided that > same portability.
My point was that 16 bit programs *tried* to be portable to 32 bits. All those typedefs in windows.h were there for a reason.
> It stinks to use "int" if it's not going to be as fast as "int64_t" > in some context, certainly. And how am I to get the fastest type > for operations that could be done in 16 bits? intfast16_t, or... > I have to guess with D whether to use short or int, and profile on > every platform? If I say that I need a type of exactly 32 bits, I'm > overspecifying the size while underspecifying optimization goals > (for speed or for space).
You can still use typedefs in D for your various tradeoffs. Like I said in another post, D approaches this from the opposite direction than C++.
C++: int sizes variable, typedef sizes fixed D: int sizes fixed, typedef sizes variable
> But if your focus is narrow enough that you care only about mainstream > desktop and server platforms, it's a perfectly reasonable trade-off.
It's a false choice. D doesn't prevent you from using varying integer sizes. It's just not the *default*.
> Please > don't assume that those who disagree with you do so because they > lack experience or knowledge. It's common that they have knowledge > which you don't (and vice versa).
Point taken.
>> I bet if I sat down and quizzed >> you on arcane details of the spec, I'd find a way to trip you up, too. > Yup; you might start by grilling me on arcana of name lookup, > there are enough landmines there I'd fail on.
It's fun to watch even the experts' mouths drop when I point some of these things out <g>. Ok, so I enjoy a little schadenfreude here and there; I'm going to hell.
> A noble goal. Probably good PL design can make 10% as much > difference as the variation between programmers does, but that's > still a huge potential benefit.
My goal is 10%, and I agree it's huge. If you've got a million dollar budget, that's $100,000 going to the profit.
> A compiler is part of the checklist. Static analysis tools go > further, design and code reviews help, unit testing helps (and > I know you agree on that, as D built it right into the language).
Yes. I found that putting such in the language makes it much more likely that people will use it. Built in unit testing has become a very popular feature of D. Bruce Eckel gets the credit for talking me into that one.
>>> because compilers have warned in any marginal situation,
>> Warnings are a good sign that there's something wrong with the language >> design. BTW, I just tried this:
>> int test(char c) { return c; }
>> with:
>> g++ -c foo.cpp -Wall
>> and it compiled without error or warning. (gcc-4.1)
> What's "marginal" about that situation?
It gives different answers for different signedness of char's, when c has a value 0x80<=c<=0xFF. I should think if a compiler was to usefully warn about these things, that example would be first on the list.
On Jan 14, 10:27 pm, Walter Bright <wal...@digitalmars-nospamm.com> wrote:
> Given the code in C++ and Java:
> a = foo() + bar();
> which one would I have to rewrite as:
> tmp = foo(); > a = tmp + bar();
> if there is an order of evaluation issue? How would I reliably detect > such an issue in the C++ version (putting on my QA hat)?
You make the source code and the unit tests available and someone is sure to report it to you - if the code is worth using.
Consider the opposite example.
int a = b * c;
where the 'Java' code has int fixed at 32 bits, and the C++ port has a 64 bit int. The 'Java' code may have an overflow-induced bug in some rare cases, whereas the C++ port will not suffer from this issue. So it actually works both ways. Closed source vendors that support and test on few platforms will much prefer a fixed int, whereas open source authors whose code runs on a variety of platforms would prefer a variable int in the original C spirit (the largest integral type that does not impose significant performance loss).
Bo Persson wrote: > Walter Bright wrote: >> Bo Persson wrote: >>>>> My experience porting D code between platforms is it ports easier >>>>> than the equivalent C++ code. >>> Porting is easier if you limit the number of potential platforms. >> Sure, but I was comparing porting C++ from platform A to B with >> porting D from A to B. The latter was noticeably easier.
> It could be because the variation is smaller. There are some platforms > were you cannot easily implement D at all, like IBM zSeries with > EBCDIC character set and non-IEEE floating point. C++ has no problem > with that.
No problem compiling the code, that is. It offers no guarantee your ascii code will run. All the porting problems are dumped on the programmer.
So yes, C++ 'supports' EBCDIC. But C++ doesn't support Unicode or UTF encodings. D's native character set is Unicode UTF-8, UTF-16, and UTF-32. EBCDIC is the past, Unicode is the future.
Java, based on ascii, runs on EBCDIC machines. So it clearly can't be that hard.
Floating point is another issue. FP code is often very sensitive to precision and other details. You might even need to use different algorithms for non-IEEE arithmetic. The fact that the code manages to compile on those machines is of no help at all in finding/fixing such dependencies.
>>>> Porting Java is easy too, if your target platform supports it. >>> Porting Java is hard, if you haven't ported its platform first! >> Porting C++ compilers is pretty hard, too. How many programmers do >> you know who can write a code generator?
> The point was rather that Java is very hard, if the intended platform > doesn't support the spec. It might require adding dedicated hardware:
zaap is not required to run Java on those machines (see the zaap faq). All it is is a hardware accelerator specific to Java bytecodes.
>>> We had a discussion just last week with a Java developer on reusing >>> his web server code on the mainframe.
>>> - "Oh dear! That's just Java 1.5, I need 1.6 generics for my code. >>> Limiting myself to 1.5 features will cost you a lot more!" >> But isn't Java implemented in C? C is more portable and available on >> every platform, so he should just recompile it and he's good to go.
> Well, IBM believe they should decide what Java version to run on z/OS.
That's a problem with IBM, not any particular language.
> It also needs to access the special Application Assist hardware to do > IEEE floating point. C and C++ doesn't have to do that.
So, with the JVM implemented in C, how does C being compilable on that machine mean that we can just recompile the JVM and have Java work? We both know that doesn't happen - and that just having code compile doesn't mean it is portable.
> For our inhouse applications portability is no concern, but Java still > insists on using a portable data format.
Then I believe we agree that there's more (a lot more) to portable C++ code than being able to recompile it.
> Even if it is more expensive. > A lot more, in this case!
It boggles the mind to think people will pay $125,000 for zaap which is only good for accelerating Java apps, when they can use cheap linux boxes instead. It's not like Java apps are legacy apps from the 60's.