C++ has Boost libraries, C?

C

Chris Dollin

jacob said:
The topic is C, and its possible extensions in libraries like "Boost".
In that context, we came as to how write those libraries in C, and
I started this sub-sub thread about overloading because I think it
simplifies the language.

Exactly. It's increasingly off-topic.
 
J

jacob navia

Chris said:
jacob navia wrote:




Exactly. It's increasingly off-topic.

When Mr heathfield and others discuss Shakespeare or
other C related subjects :) nobody says anything.

Even if that could be off topic... maybe, literature
is an interesting subject. I did not object, even if the
thread was about calloc bugs.

But if I want to discuss about possible evolution of the
language, possible ways to correct the warts of the
language, suddenly the only argument is

"OFF TOPIC OFF TOPIC"
"GO AWAY HERETIC!!!!!"

"IT IS NOT C!!!!"

etc.
 
W

Walter Roberson

Roland Pibinger a écrit :
This is wrong. Completely wrong.
Everything evolves. The only things that do not evolve
any more are corpses: dead things.

"corpse" can only be applied to things that were living.

For example, does water evolve? No. Is water a corpse? No.

Okay, so water is a static item, so consider fire instead. Fire
is all about change. Does fire evolve? What is the ISO or IETF
document number on fire, and how often do the committees meet?
Have you stopped using your BBQ because propane is passe'?
 
M

Marc Boyer

jacob navia said:
One of the worst features of C++ is precisely the constructors /
destructors stuff. Since there is no GC, you have to have exception
handling to destroy the objects that are out of scope when throwing
an exception. None of this is needed in the schema proposed in
lcc-win32: the GC takes care of that without any extra overhead.

How does your GB handle mutual references (like in observer
pattern) ? And what about other ressources than memory (like
a window) ?

Marc Boyer
 
S

Serve Laurijssen

jacob navia said:
Serve Laurijssen a écrit :
It's impossible to create a generic library for lets say containers when
you want to run on embedded systems too.

This is a fallacy. Embedded systems, precisely because of
the feeble hardware ressources, need MORE the good algorithms
than workstations, where with enough horsepower you can
afford a bubble sort for instance and get by with it.


A generic container library using operator overloading
does NOT introduce any higher costs than what is existent
NOW since you have to call a library function anyway, so
a function call does not incur in any extra overhead.

Specifically if you access a list with

List s,s1;

...
s1 = s[4];

or you use

List s,s1;

...
s1 = GetNthFromList(s,4);

It is the SAME overhead.

Uhm, I never said that the performance issue had to do with operator
overloading
Anyway, what I wanted to respond to that in this particular example we
already see a misuse of operator overloading.
The general idea of good handling of operator overloading is that the
operators behave the same as the primitive operators. That means that '+'
will not subtract, '*' will not divide and that the '[]' operator will be an
indexing operator with constant time lookup :)

I actually have had to fix a program where the programmer was bitten by his
own abuse of overloading '[]' operator on a list class and the code
performed REALLY slow. It's all too easy to write things like

int i;
for (i=0; i<10000; i+=2)
{
swapitem(list, list[i+1]);
}

notice how the [] operator tricks you into thinking that only 5000
iterations are performed in this loop (i+=2)
but that *also* for every iteration of i another 2 times i iterations
performed over a linked list are performed!

Theres a good reason the standard C++ list doesnt overload operator[] !!
 
I

Ian Collins

jacob said:
Ian Collins wrote:

Look, C is just syntactic sugar for assembly, as all other
computer languages.
You miss or ignore the point again. Operator overloading doesn't add
any functionality to a language, it simply improves the syntax. C++
style constructors and destructors add the ability to manage resources
(any resource, not just memory) with RAII.
Why constructors/destructors are unnecessary?
This means that the incredible inefficiency of having a function call
for each object creation can be avoided if you wish.

Objects may have to be initialised, either by an automatic constructor,
or by manual assignment. If initialisation isn't required, a
constructor isn't required.
It means that
the complexity in the compiler that needs to figure out if the
constructor is a "trivial" one and can be optimized away can be avoided
too.
No different from determining if another function can be inlined.
You will immediately cry:

"But the freeing of the object is very error prone"
"You will end with a memory leak"

Within lcc-win32 I have developed a better solution: a GARBAGE
COLLECTOR, that frees you from all the destructor/freeing memory
woes.
Apples and oranges. How does GC release a lock when an object goes out
of scope? Memory isn't the only resource that can be managed.
One of the worst features of C++ is precisely the constructors /
destructors stuff. Since there is no GC, you have to have exception
handling to destroy the objects that are out of scope when throwing
an exception.

Who said anything about exceptions?
 
W

Walter Roberson

Serve Laurijssen said:
Anyway, what I wanted to respond to that in this particular example we
already see a misuse of operator overloading.
The general idea of good handling of operator overloading is that the
operators behave the same as the primitive operators. That means that '+'
will not subtract, '*' will not divide and that the '[]' operator will be an
indexing operator with constant time lookup :)

To quibble slightly:

- [] is validly used in languages with sparse array operations,
with a time that is usually not constant

- [] is used in some languages for tables or associative arrays
(e.g., index by string); indexing for those may be anywhere from
linear time to log time to constant time (e.g., if a perfect hash works)

My quibble is thus that the general expectation is that [] will not
necessarily use constant time, just that it be "fast".

Your point is correct, though, that there can be a disconnect between
"fast" for any one index operation and fast for a repeated
set of operations when the details are buried under the hood.
 
J

jacob navia

Ian Collins a écrit :
Objects may have to be initialised, either by an automatic constructor,
or by manual assignment. If initialisation isn't required, a
constructor isn't required.

You can save yourself any initializtion if it is not
needed. And if you need it you can initialize some fields,
not everything, if you care about speed.

Above all, it is NOT automatic, i.e. the compiler doesn't
FORCE you an initialization!
Apples and oranges. How does GC release a lock when an object goes out
of scope? Memory isn't the only resource that can be managed.

Mostly, constructores manage memory. For the cases when
there is another need, you will have to do that yourself.

There is just no compiler support for a complex feature that
is not always needed. This remains C, not C++.
 
J

jacob navia

Serve Laurijssen a écrit :
Uhm, I never said that the performance issue had to do with operator
overloading
Anyway, what I wanted to respond to that in this particular example we
already see a misuse of operator overloading.
The general idea of good handling of operator overloading is that the
operators behave the same as the primitive operators. That means that '+'
will not subtract, '*' will not divide and that the '[]' operator will be an
indexing operator with constant time lookup :)

Why constant time lookup? It depends if you want that feature or not.
The flexibility of having just to change a declaration and all your code
still works if you use a normal array, a list, a flexible array, etc
is worth the inconvenience of having in mind which container is
being used and how fast/slow it is.
I actually have had to fix a program where the programmer was bitten by his
own abuse of overloading '[]' operator on a list class and the code
performed REALLY slow. It's all too easy to write things like

int i;
for (i=0; i<10000; i+=2)
{
swapitem(list, list[i+1]);
}


This can be avoided if the list has a cursor...

notice how the [] operator tricks you into thinking that only 5000
iterations are performed in this loop (i+=2)
but that *also* for every iteration of i another 2 times i iterations
performed over a linked list are performed!

If the list has no cursor.
Theres a good reason the standard C++ list doesnt overload operator[] !!


No

But since this implementation of operator overloading comes after the C++
one, I have been able to avoid some of the pitfalls of the C++
implementation
like for instance, the "&&" and "||" operators can't be overloaded...
 
J

jacob navia

Walter Roberson a écrit :
Anyway, what I wanted to respond to that in this particular example we
already see a misuse of operator overloading.
The general idea of good handling of operator overloading is that the
operators behave the same as the primitive operators. That means that '+'
will not subtract, '*' will not divide and that the '[]' operator will be an
indexing operator with constant time lookup :)


To quibble slightly:

- [] is validly used in languages with sparse array operations,
with a time that is usually not constant

- [] is used in some languages for tables or associative arrays
(e.g., index by string); indexing for those may be anywhere from
linear time to log time to constant time (e.g., if a perfect hash works)

My quibble is thus that the general expectation is that [] will not
necessarily use constant time, just that it be "fast".

Your point is correct, though, that there can be a disconnect between
"fast" for any one index operation and fast for a repeated
set of operations when the details are buried under the hood.

See my answer in this same thread
 
J

jacob navia

Jorgen Grahn a écrit :
And many or most of them are really based on C interfaces -- modules os,
posix, socket, zlib, ... for many things, they have just been able to say
"lots of people seem to use libfoo -- let's package it as module foo and
make it standard".

Obvious. For instance the statics package and the extended float
package are based on the math library of S. L Moshier, that is also
used by lcc-win32. The difference is that python just wrote an interface
and that lcc-win32 rewrote most part of it in assembly...
 
A

Arthur J. O'Dwyer

You miss or ignore the point again. Operator overloading doesn't add
any functionality to a language, it simply improves the syntax. C++
style constructors and destructors add the ability to manage resources
(any resource, not just memory) with RAII.

Yep.
Apples and oranges. How does GC release a lock when an object goes out
of scope? Memory isn't the only resource that can be managed.


Who said anything about exceptions?

Well, exceptions /are/ the only part of C++ that I can think of
off the top of my head that would make RAII more than syntactic sugar.

void foo() throw(exception&) {
MyLockObject m(&global_lock); // locks the resource
go_throw_an_exception();
// the following line is not executed
do_more_processing();
// oh no, that destructor had better be called anyway!
}

If you don't support exception handling, then AFAICT Jacob's right
that RAII is just syntactic sugar for explicit calls to "constructor"
an "destructor" functions. Of course, it is a great convenience, since
it means you can write

void foo() {
MyLockObject m(&global_lock); // locks the resource
if (condition_holds())
return; // and incidentally call the destructor
do_more_processing();
return; // and incidentally call the destructor
}

instead of needing to insert a 'goto' to a single exit point, or
duplicate the cleanup code at both exit points. IMVHO, this is
a greater convenience to programmers than the ability to write
"a+b" instead of "add(a,b)".

(If anyone wants to have a long discussion about syntactic sugar
in languages other than C, follow-up to c.l.misc or comp.programming,
please.)

-Arthur
 
W

Walter Roberson

Serve Laurijssen a écrit :
The general idea of good handling of operator overloading is that the
operators behave the same as the primitive operators. That means that '+'
will not subtract, '*' will not divide and that the '[]' operator will be an
indexing operator with constant time lookup :)
Why constant time lookup? It depends if you want that feature or not.
The flexibility of having just to change a declaration and all your code
still works if you use a normal array, a list, a flexible array, etc
is worth the inconvenience of having in mind which container is
being used and how fast/slow it is.

Whether something is worth an inconvenience or not is a circumstantial
value judgement. Other people, given exactly the same information,
might arrive at completely different conclusions. You have, though,
written your posting as a statement that the flexibility *is* worth
the inconvenience, which is an absolute statement that the
flexibility is *always* worth the inconvenience; if you wish to
persue that claim, you will have to present some objective data
supporting your position, such as case studies, or references to
papers on cognitive modelling in programming, or references to
books on programming from people who have actively studied defect
reduction.

Considering especially that efficiency is not your prime consideration,
then if you want the flexibility of just making a small modification
then write in functional style the first time around, instead of
using the built-in operators in the body of your code. Then you have
localized the implimentation details to a very small section of code.
And as you like to point out, if that code section is very straight
forward, then there might well not be any efficiency impact at all,
since the compiler might inline the details.
 
S

Serve Laurijssen

jacob navia said:
COMPLEX_INTEGER operator+(COMPLEX_INTEGER *x, int y);

This will not work since addition of pointer + integer is
already defined. That is why is better to abstain from
using pointers in this context.

Yes. It is not C++. You can't do EVERYTHING.

hmmm, theres a big difference between not being able to do anything and
being able to do almost nothing
This will really only work for small structs cos of the pass by value all
the time and yes thats why references were introduced in C++ because no
reference operators are defined already. And when the structs actually get
larger you may want to introduce copy constructors and overloading of
operator=. And if you have copy constructors, well constructors dont hurt
either. And function overloading of course so you can make more than one
constructor and and.....lets just make a C++ compiler without
templates.....they can be added later :)
 
J

jacob navia

Serve Laurijssen a écrit :
hmmm, theres a big difference between not being able to do anything and
being able to do almost nothing
This will really only work for small structs cos of the pass by value all
the time

Operator overloading is best used when the operations are done
with numbers. Numbers are small structures. They can be passed by value.
and yes thats why references were introduced in C++ because no
reference operators are defined already.

Yes, this is an example of not knowing when you should stop.
And when the structs actually get
larger you may want to introduce copy constructors and overloading of
operator=.

You do not need copy constructors. You just copy. Besides, any
HUGE structure can be compressed to the size of a void *...

How?

Like this

typedef struct tagHugeStruct {
... // 1457 fields ellided
} HUGE_STRUCT;

typedef struct tagZippedHugeStruct {
HUGE_STRUCT *pHuge;
};

Finished!

Your operator overloading struct just takes sizeof (void *) bytes!

:)

YOU HAVE TO KNOW WHEN TO STOP!
 
W

Walter Roberson

Operator overloading is best used when the operations are done
with numbers. Numbers are small structures.

I haven't had a chance to look at anything C99 might have
added with respect to rounding modes and what-not, but as far as
C89 is concerned, operations on variables are defined as being
operations on *values*, not operations on representations.
If the representation (internal structure) used on a particular system
happens to implement the C value semantics efficiently, then Good, but if
not then it is up to the implementation to add whatever internal code
it needs so that the C value semantics are preserved, not whatever
semantics happen to be convenient for the internal representation.
 
J

jacob navia

Walter Roberson a écrit :
I haven't had a chance to look at anything C99 might have
added with respect to rounding modes and what-not, but as far as
C89 is concerned, operations on variables are defined as being
operations on *values*, not operations on representations.

I read that sentence several times and I can still not quite parse
it.

In my opinion there are no values in a machine, just representations
in some bits. What actually exists are those bits.

The software implicitely assigns those representations a value.

For instance you assign to 3+Volts the value "1" and to
0.3V the value "0".

Then you group those bits in groups of (say) 32 of them and
you say that the bits are to be understood from right to left
as 1, 2, 4, 8, 16, 32, 64, etc up to 2**31. And you give
the name to that bunch of bits "unsigned int".

Nowhere is any "value". In the machine there are only bits,
and they can be interpreted by the software in many different
ways to give different values.

You can abstract from those representations and speak about "operations
on values", meaning that no matter how "345" is represented and no
matter how "10" is represented, 345+10 MUST give 355.

OK.

But this apparent abstraction breaks down immediately when we see that
in a 16 bit implementation 30 000 + 10 000 gives a negative number.

This breakdown has been incorporated into the language and everybody
agrees that it is the only nomal behavior. The standard claims this
is "undefined behavior" and stops there.

If the representation (internal structure) used on a particular system
happens to implement the C value semantics efficiently, then Good, but if
not then it is up to the implementation to add whatever internal code
it needs so that the C value semantics are preserved, not whatever
semantics happen to be convenient for the internal representation.

Obvious. But what you are saying is just that the implementation must
agree with the standard, what is possible in most machines since C
maps efficientely in most machines.

And so what?

We were speaking of operator overloading and I indicated that it is
well adapted for numeric types, and that other applications like
"abc"+"cde" --> "abccde" are wrong, since it violates some of the basic
rulmes of addition: a + b == b + a. That's why I said that. Not so
explicitely but that was my intention. In c++ this doesn't hold.
 
K

Keith Thompson

Ian Collins said:
You miss or ignore the point again. Operator overloading doesn't add
any functionality to a language, it simply improves the syntax. C++
style constructors and destructors add the ability to manage resources
(any resource, not just memory) with RAII.
[...]

<OT>
I think you're incorrectly assuming that everyone here knows what RAII
means. It stands for "Resource Acquisition Is Initialization".
</OT>
 
I

IanC

jacob said:
Ian Collins a écrit :

You can save yourself any initializtion if it is not
needed. And if you need it you can initialize some fields,
not everything, if you care about speed.

Above all, it is NOT automatic, i.e. the compiler doesn't
FORCE you an initialization!
A constuctor only has to be called if provided or if a member of the
struct has one, so there isn't any forced initialisation.
Mostly, constructores manage memory.
Nonsense.

For the cases when
there is another need, you will have to do that yourself.
Why?
 

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

No members online now.

Forum statistics

Threads
474,432
Messages
2,571,680
Members
48,796
Latest member
Greg L.

Latest Threads

Top