Boost process and C

J

John F

Richard Tobin said:
Why? The averaging example I gave shows a perfectly clear use of
it.

It might be nonsense to claim that adding date values gives you a
date, but that's not what I said.

Another analogy: I have heard it claimed that adding celsius
temperatures is nonsense, yet people do it all the time when
averaging
temperatures.

There is a difference between

(date1+date2)/2 (adds dates and divides by two)

and

date1+(date2-date1)/2 (proportionally adds the difference between two
dates to another date)

The former is meaningless (in a mathematical sense), the latter is
perfectly legal.

If you argue that adding dates is legal then I suggest you define the
binary operator and the division-operator in a serious way. (e.g. it
should tell: What is (March, 13th 2003) / 2? )

Temperatures are a different thing. They have another mathematical
structure. Adding them does not make sense in a physical way... As it
is for dates. Averaging dates would be legal too but you would have to
find a mapping to the real numbers to do so.

That means you need to define the offset and the scale you want to
use. This does not give a meaning to the sum of two dates but enables
you to define the average date in a series.

regards
John
 
R

REH

The original point was just that if you want the "full power" of
operator-overloading, combined with manual memory management, you also
need constructors, destructors and references. Or something like them.

That's all. Whether C should be extended with any or all of:

1. operator overloading
2. references
3. constructors and destructors

is a matter of opinion.

Ah, I gotta ya. Personally, I'd like C to stay small, simple, and compact.
I use C++ or Ada when I want those extra features, and still have a language
that is designed for efficiency.

REH
 
J

jacob navia

REH said:
Ah, I gotta ya. Personally, I'd like C to stay small, simple, and compact.
I use C++ or Ada when I want those extra features, and still have a language
that is designed for efficiency.

REH

The operator overloading change do not affect efficiency at all. One of
the most "dreaded" features of C++ is the problem of avoiding
copy-constructors, constructors and destructors in apparently efficient
and simple statements like

a = *b;

or similar. This is nowhere required by operator overloading.

Of course some people will say that this is not the "full power" of
operator overloading, and I would just say that they are right. But I
would rename that to "full bloat" :)

The problem of C++ is that it wasn't able to say STOP. It was unable to
see when features become misfeatures because of the incredible bloat
that they continued to accept.

One feature (operator overloading) led to another and to another and
they ended up with a language that nobody in the world can grasp as a
whole. Still, most C++ compilers do not arrive at full compliance
because of the incredible feature set. Look at the incredible efforts
done by the gcc team and still there is no 100% compliance.

This is a lesson to be learned.

Still, we must not fall in the opposite direction, and refuse any
change. I suspect that most of the animosity against the changes I
propose is precisely this feeling that C is going to disappear again in
a bloated stuff where the efficiency and the surface of the language is
extended beyond repair.

This is not AT ALL my intention. The proposed changes do not affect the
efficency at all, and they are really optional in the sense that the
efficiency loss is only paid by people that use them. In C++ you get
your constructors/destructors whether you want it or not. Operator
overloading is a change that will affect only people that use that
feature. Nobody else has to pay for it.

The same thing for ALL other changes that are done in lcc-win32:

o overloaded functions: No efficiency lost since the compiler generates
the same machine code as before. This is just compile time changes.
o operator overloading: No efficiency lost for people that do not use
this feature. Some cycles (minimal) of extra cost are there for simple
implementations like the one in lcc-win32. An optimizing compiler
would have no trouble in eliminating it.
o Default arguments: No efficiency loss at all. This is just some
syntatic sugar.

Note that in contrast to C++ C remains NOT object oriented!

jacob
 
K

Keith Thompson

John F said:
There is a difference between

(date1+date2)/2 (adds dates and divides by two)

and

date1+(date2-date1)/2 (proportionally adds the difference between two
dates to another date)

The former is meaningless (in a mathematical sense), the latter is
perfectly legal.

[...]

The former includes a subexpression that yields a result that's not
meaningful, but the expression has a whole is meaningful.

Dates, temperatures, and pointers are all similar in the sense that
each can be thought of as a pair: base+offset, where the base is
respectively an arbitrary epoch, an arbitrary zero temperature, or a
zero address (or the base address of a containing object). In each
case, the "base" is some fixed point, and the "offset" is a scalar
quantity. Offsets by themselves can be added and subtracted freely;
the base is meaningful only if you have exactly zero or one of it.
(With no base, you have an interval or distance; with a base, you have
a position.)

(Assume Celsius or Fahrenheit temperatures; the zero base of Kelvin
scale is uniquely meaningful, so it's not restricted to this model.)

Subtracting two dates
date1 - date2
is equivalent to:
(base+offset1) - (base+offset2)
which reduces to:
offset1 - offset2
which is meaningful; it's an interval, measurable in seconds, with no
defined base.

Adding two dates:
date1 + date2
is equivalent to:
(base+offset1) + (base+offset2)
which reduces to:
2*base + offset1 - offset2
which is not directly meaningful because of the 2*base term.

However, if it's used as an intermediate expression, as in:
(date1 + date2) / 2
we have
((base+offset1) + (base+offset2)) / 2
or
(2*base + offset1 - offset2) / 2
or
base + (offset1 - offset2)/2
a meaningful expression that denotes the midpoint between the two
dates.

Ideally, we'd like to make expressions with meaningful results legal,
and expressions without meaningful results illegal. Theoretically, we
could do this by allowing meaningless intermediate results, as long as
the result of the full expression is meaningful.

There are (at least) two problems with this approach. First, it makes
the language rules more complex, which affects both compiler writers
(who can deal with it) and users (who likely can't). Try explaining
to a newbie that (date1 + date2), or (pointer1 + pointer2), is usually
illegal, except that it's allowed as a subexpression if and only if
the expression as a whole obeys certain constraints. Second, it makes
it difficult to define the circumstances in which overflow can occur.
Pointer arithmetic in C is defined only within a single object (or
just past its end); allowing pointer+pointer gives you intermediate
results that not only don't have an obvious meaning, but may not be
representable, depending on where in the address space the object
happens to be allocated.

Ignoring the overflow problem, if you're using raw numbers to
represent dates, something like (date1+date2)/2 is ok, as long as you
have the discipline to avoid writing a full expression that's not
meaningful:
x = date1 + date2; /* meaningless, but the compiler won't complain */

If you're using a hypothetical language that does this kind of smart
type checking, allowing date1+date2 as a subexpression but not as a
full expression, you can add dates to your heart's content and depend
on the implementation to detect any type matching errors. It would be
interesting to design this kind of thing, either as a language feature
(off-topic here), or as a library with run-time checking (doable in
standard C, with functions rather than overloaded operators). Again,
overflow is a concern unless you can either use extended precision for
intermediate results, or rely on the implementation to rearrange the
expressions for you.

Finally, if you're using standard C and operating on pointers, you'll
just have to rearrange the expression yourself. If you need to know
the address halfway between two pointers, you can't write:
mid = (ptr1 + ptr2) / 2;
You'll just have to bite the bullet and write:
mid = ptr1 + (ptr2 - ptr1) / 2;
 
I

Ian Collins

Richard said:
But you still can't tell from any particular _call_ to a function
whether that function is likely to change its arguments, or is
guaranteed not to. As long as you have references (real ones, not
navia-references), you cannot trust a single function call - you have to
look up all prototypes, even if you're only trying to understand someone
else's code.
I must be missing something, you have the same problem with pointers,
don't you?

If you see someFn( &x ), how do you know if someFn's prototype is

void someFn( int* ); or
void someFn( const int* );

without looking it up?

In new code, how can you call a function without knowing its prototype?
 
C

CBFalconer

jacob said:
.... snip ...

This is not AT ALL my intention. The proposed changes do not affect
the efficency at all, and they are really optional in the sense
that the efficiency loss is only paid by people that use them. In
C++ you get your constructors/destructors whether you want it or
not. Operator overloading is a change that will affect only people
that use that feature. Nobody else has to pay for it.

The same thing for ALL other changes that are done in lcc-win32:

o overloaded functions: No efficiency lost since the compiler generates
the same machine code as before. This is just compile time changes.
o operator overloading: No efficiency lost for people that do not use
this feature. Some cycles (minimal) of extra cost are there for simple
implementations like the one in lcc-win32. An optimizing compiler
would have no trouble in eliminating it.
o Default arguments: No efficiency loss at all. This is just some
syntatic sugar.

Not so.

default arguments:
The compiler has to generate default parameter values. It can now
no longer detect omitted parameters during a call. One more source
of evil gotchas.

operator overloading:
Already exists in some places, since you can add floats, or
integers. Any further extension needs a complex set of automatic
cast rules and/or a set of implementing runtime routines, with
attendant source errors possible.

Overloaded functions:
The objections to default arguments apply. I could probably think
of more, including bloating the compiler proper.

If you examine the raw size of the C standard, you will find it is
roughly twice that of better designed languages already, to spell
out all the ifs, buts, and just in cases required by the baroque
syntax. The C++ standard eliminates much of this by simply
referring to the C standard. You would get further proposing
simplifications rather than complexities. C just grew, and the
standard shows it.

For example, a future standard could restrict 'precedence' to three
levels (e.g. logical, additive, and multiplicative) only, requiring
parentheses for any further control, yet allowing the actions of
the present silly system. The result would be clearer code without
necessarily breaking older code. It could deprecate such
obfuscative things as +=, without efficiency losses, since
optimizers can easily handle the longer phrases.

I can mention these possibilities, but I don't seriously expect
them to take place. And I will not run about weeping and whining
if the general readership does not enthusiastically rally behind
them.

--
"If you want to post a followup via groups.google.com, don't use
the broken "Reply" link at the bottom of the article. Click on
"show options" at the top of the article, then click on the
"Reply" at the bottom of the article headers." - Keith Thompson
More details at: <http://cfaj.freeshell.org/google/>
Also see <http://www.safalra.com/special/googlegroupsreply/>
 
K

Keith Thompson

Ian Collins said:
I must be missing something, you have the same problem with pointers,
don't you?

If you see someFn( &x ), how do you know if someFn's prototype is

void someFn( int* ); or
void someFn( const int* );

without looking it up?

In new code, how can you call a function without knowing its prototype?

If you see someFn(x), you can be sure that someFn will not change the
value of x, even without understanding everything about someFn.

In a language with references, or with some sort of pass-by-reference
parameter mechanism, you can't be sure of that.
 
I

Ian Collins

jacob said:
The operator overloading change do not affect efficiency at all. One of
the most "dreaded" features of C++ is the problem of avoiding
copy-constructors, constructors and destructors in apparently efficient
and simple statements like

a = *b;
Case 1:

int a;
int *b;

a = *b;

Show me where con/destructors are used.

Case 2:

struct x { int a, int b; };

struct x a;
struct x *b;

a = *b;

Show me where con/destructors are used.

You keep rambling on about this without any solid evidence.
This is not AT ALL my intention. The proposed changes do not affect the
efficency at all, and they are really optional in the sense that the
efficiency loss is only paid by people that use them. In C++ you get
your constructors/destructors whether you want it or not. Operator
overloading is a change that will affect only people that use that
feature. Nobody else has to pay for it.
Twaddle.

In case 2 above, both C and C++ compiler can generate the same code.
The same thing for ALL other changes that are done in lcc-win32:

o overloaded functions: No efficiency lost since the compiler generates
the same machine code as before. This is just compile time changes.
But opens up that other can of worms, name mangling and all the
incompatibilities that can introduce.
o operator overloading: No efficiency lost for people that do not use
this feature. Some cycles (minimal) of extra cost are there for simple
implementations like the one in lcc-win32. An optimizing compiler
would have no trouble in eliminating it.
o Default arguments: No efficiency loss at all. This is just some
syntatic sugar.

Note that in contrast to C++ C remains NOT object oriented!
None of this realy belongs in C, you can get all these features from C++
without any loss of efficiency.

Ian
 
I

Ian Collins

Keith said:
If you see someFn(x), you can be sure that someFn will not change the
value of x, even without understanding everything about someFn.

In a language with references, or with some sort of pass-by-reference
parameter mechanism, you can't be sure of that.
I see your point.

I'd still look at the prototype out of simple curiosity!
 
R

Richard Tobin

John F said:
There is a difference between

(date1+date2)/2 (adds dates and divides by two)

and

date1+(date2-date1)/2 (proportionally adds the difference between two
dates to another date)
The former is meaningless (in a mathematical sense)

That's OK, I understand mathematics, tell me the "mathematical sense"
in which it's meaningless. Perhaps you could start with a mathematical
definition of "meaningless", since I've never come across one.
If you argue that adding dates is legal then I suggest you define the
binary operator

What binary operator?
and the division-operator in a serious way. (e.g. it
should tell: What is (March, 13th 2003) / 2? )

It's something which when multiplied by 2 gives you March 13 2003,
and which when added to (March 11 2003) / 2 gives youy March 12 2003.
It is not, of course, a date. There isn't any conventional way to
write it, nor is there a conventional name for its type. But we can
perfectly well define various operations on it.

-- Richard
 
K

Keith Thompson

Ian Collins said:
I see your point.

I'd still look at the prototype out of simple curiosity!

Sure, but it might not be obvious which of a dozen included headers
contains the prototype. Knowing that someFn can't modify x can be
useful in getting a quick partial understanding of the code in a
fairly quick look.

For example, suppose the code looks like this:

int x = 42;
/* do some stuff */
someFn(x);
/* do some other stuff */
printf("x = %d\n", x);

If you expect the printf to print "x = 42", and it instead prints
"x = -3" you can be sure that the problem is either before or after
the function call, not in the function itself.
 
I

Ian Collins

Keith said:
Sure, but it might not be obvious which of a dozen included headers
contains the prototype. Knowing that someFn can't modify x can be
useful in getting a quick partial understanding of the code in a
fairly quick look.

For example, suppose the code looks like this:

int x = 42;
/* do some stuff */
someFn(x);
/* do some other stuff */
printf("x = %d\n", x);

If you expect the printf to print "x = 42", and it instead prints
"x = -3" you can be sure that the problem is either before or after
the function call, not in the function itself.
All good and let me reiterate: I don't think references belong in C.
 
J

jacob navia

Ian Collins a écrit :
Case 1:

int a;
int *b;

a = *b;

Show me where con/destructors are used.

Case 2:

struct x { int a, int b; };

struct x a;
struct x *b;

a = *b;

Show me where con/destructors are used.

You keep rambling on about this without any solid evidence.

Maybe. If b is a pointer to a class... That was case 3) in your list
that you left out. Why?

And what about an array of classes ???

Etc Etc. Please consult ANY C++ textbook and they will tell you many
ways to cleverly avoid that problem.

This is a great argument.

"Twaddle".

I am impressed.
In case 2 above, both C and C++ compiler can generate the same code.



But opens up that other can of worms, name mangling and all the
incompatibilities that can introduce.

Only if you want the compiler to generate automatically the overloaded
names. If you provide the names, no mangling is done:

double fn1(double);
int fn2(long double,int);

double overloaded FN.fn1(double); // This will resolve to a call to fn1
int overloaded FN.fn2(long double); // Will resolve to a call to fn2

FN(2.3); --> will resolve into a call to fn1
FN(2.3L,1); --> will resolve into a call to fn2

No name mangling is done in this case. The overloaded function FN will
resolve to a call of fn1 in one case, fn2 in another case.
 
J

jacob navia

CBFalconer a écrit :
jacob navia wrote:

... snip ...



Not so.

default arguments:
The compiler has to generate default parameter values. It can now
no longer detect omitted parameters during a call. One more source
of evil gotchas.

No. Only functions with a prototype specifying default arguments can
have them. All other functions stay exactly the same.

Required arguments must come first in the call, and all optional
arguments come later. The compiler generates them from the prototype of
the function. This reduces the memory footprint for many functions.

Not RAM of course. Human memory, that is far more precious. Can't buy a
Gig of human memory at the next store to accomodate functions with 8 or
even 10 arguments!

operator overloading:
Already exists in some places, since you can add floats, or
integers. Any further extension needs a complex set of automatic
cast rules and/or a set of implementing runtime routines, with
attendant source errors possible.

Yes, and they are possible even now with the overloading of
int/floats/doubles/complex additions :)
Overloaded functions:
The objections to default arguments apply. I could probably think
of more, including bloating the compiler proper.

The whole module for operator overloading and overloaded functions is
1723 lines of C including comments and lines with just an opening brace.
 
M

Mark McIntyre

Maybe. If b is a pointer to a class... That was case 3) in your list
that you left out. Why?

I'll take a wild guess - because C doesn't have classes?
And what about an array of classes ???

What, some sort of school assembly?
Etc Etc. Please consult ANY C++ textbook and they will tell you many
ways to cleverly avoid that problem.

What, *any* c++ text book? Hmm, I checked in "Learn Visual C++ in 21
days" and it barely mentioned classes...
--
Mark McIntyre

"Debugging is twice as hard as writing the code in the first place.
Therefore, if you write the code as cleverly as possible, you are,
by definition, not smart enough to debug it."
--Brian Kernighan
 
K

Keith Thompson

CBFalconer said:
For example, a future standard could restrict 'precedence' to three
levels (e.g. logical, additive, and multiplicative) only, requiring
parentheses for any further control, yet allowing the actions of
the present silly system. The result would be clearer code without
necessarily breaking older code.

You'd need at least four level, unless you want to require
x = y + z;
to be written as
x = (y + z);

But if you did reduce the number of precedence level, an automatic
tool could translate existing C to add newly required parentheses.
It could deprecate such
obfuscative things as +=, without efficiency losses, since
optimizers can easily handle the longer phrases.

<OPINION>LHS += RHS can be much clearer than LHS = LHS + RHS if LHS is
a complex expression</OPINION> -- and it's not equivalent if LHS has
side effects. (The counterargument is that, in cases where it
matters, you should write simpler code.)
 
I

Ian Collins

jacob said:
Ian Collins a écrit :


Maybe. If b is a pointer to a class... That was case 3) in your list
that you left out. Why?
Sorry but you are digging yourself into a hole. Ignoring the face that
C doesn't have classes, in C++ there is one difference between a struct
and a class - the default access. I could have written

Case 3:

class x { public: int a, int b; };

x a;
x *b;

a = *b;

But it isn't C and it adds nothing to the argument.

I say again, show me an example of con/destructor use in any of the above.
And what about an array of classes ???
What about one?
Etc Etc. Please consult ANY C++ textbook and they will tell you many
ways to cleverly avoid that problem.


This is a great argument.

"Twaddle".

I am impressed.
Thank you, it was the most concise summary I could use without being rude :)
You didn't address the body of the argument, the line above.
Only if you want the compiler to generate automatically the overloaded
names. If you provide the names, no mangling is done:

double fn1(double);
int fn2(long double,int);

double overloaded FN.fn1(double); // This will resolve to a call to fn1
int overloaded FN.fn2(long double); // Will resolve to a call to fn2

FN(2.3); --> will resolve into a call to fn1
FN(2.3L,1); --> will resolve into a call to fn2

No name mangling is done in this case. The overloaded function FN will
resolve to a call of fn1 in one case, fn2 in another case.
Maybe not, but it's dead ugly and not recognisable C syntax. It's
introducing more complexity into the language.
 
W

websnarf

Robert said:
...and I would be suspcious of anyone who claimed to be an
experienced programmer who can't write the necessary routines
from scratch in less time than it takes to download, install, and
understand a dedicated string library. In fact I want to see a
single experienced programmer who hasn't written his own little
string library at some point in his career -- be it as a
homework assignment or just to kill time on a rainy weekend.

Can you write 102 well tested functions that are aliasing safe, support
write protection, have a performance advantage over comparable straight
C across the board on multiple platforms, in portable C, immune to
buffer overflows and size overflowing in less than an hour? That I
would like to see.

You also make the mistake of thinking experienced programmers are C
programmers. C (and C++) is the only language where someone would want
to *write* a string library. No other language needs it. And of
course, the fact the many C programmers might be writing string
libraries doesn't seemed to have affected the number of buffer overflow
flaws that are commonly shipped in applications written in C.
All these points are moot anyway -- it is very likely that the
people manipulating millions of tiny string needs a completely
different library than the huge-document-in-memory crowd. For
obvious reason, there is no one-size-fits-all solution.

Right -- Bstrlib is sometimes less than optimal for some instances of
those cases (though it is *always* safer and easier), and is really
only universally targetted at every single other case.
 
I

Ian Collins

Can you write 102 well tested functions that are aliasing safe, support
write protection, have a performance advantage over comparable straight
C across the board on multiple platforms, in portable C, immune to
buffer overflows and size overflowing in less than an hour? That I
would like to see.

You also make the mistake of thinking experienced programmers are C
programmers. C (and C++) is the only language where someone would want
to *write* a string library. No other language needs it. And of
course, the fact the many C programmers might be writing string
libraries doesn't seemed to have affected the number of buffer overflow
flaws that are commonly shipped in applications written in C.
That's one of the things that hurts C, think how much more productive
those programmers would be if they didn't have to write their own string
library.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,183
Messages
2,570,967
Members
47,518
Latest member
RomanGratt

Latest Threads

Top