C needs a BOOST

J

jacob navia

Ian said:
struct HoldSomething
{
Something held;

HoldSomething() { held = AcquireSomething(); }
~HoldSomething() { Release Something( held ); }
};

Something can be any resource to be held by the scope. It is released
when the HoldSomething object goes out of scope.

Fine. But why can't you write exactly the same code in
your new_foo() function???

You can do ALL that in new_foo(). The *only* difference is that is NOT
called automatically!

The same for the destructor destroy_foo(). You can release the resource
JUST AS you do in your C++ destructor!

The only differences are some syntax and the fact that the
compiler will not call it for you!

And remember the problems you have with the copy constructors,
that end being called at odd places where you do not want them called,
and the dozens of recipes C++ books give us on how to avoid
calling the copy constructor implicitly, etc.

1) Constructors and destructors exist in C. They are just not
called automatically by the compiler but by the developer
when he deems necessary.
2) This simplifies the language and the compiler.
3) Since they are just *conventions* they are much more
flexible than in C++.
 
K

Keith Thompson

jacob navia said:
One example is the "addition" of strings instead of concatenation as
a function call. When A and B are strings, A+B != B+A, what is not at
all clear to me. It is better to use a function call to do that
operation.
[...]

That's a matter of taste. Personally, given a language that supports
operator overloading, I have no problem with using "+" for string
concatenation. The fact that it's not commutative just doesn't bother
me. The intended meaning of
"hello" + " " + "world"
seems perfectly obvious. It's a convenient notation; mathematical
purity isn't required.

Then again, I don't particularly mind "<<" and ">>" for I/O.

I've also worked with languages that have a dedicated operator for
string concatenation (Perl's ".", Perl 6's "~", Ada's "&", which is a
general one-dimensional array concatentation operator), but C has
pretty much run out of operator symbols unless you want to use
Unicode.

One minor drawback is that you can't use it to concatenate a string
with a character. For example,
"hell" + 'o'
already has a meaning (one invokes undefined behavior in every
character encoding I've ever seen).
 
I

Ian Collins

jacob said:
Fine. But why can't you write exactly the same code in
your new_foo() function???

You can do ALL that in new_foo(). The *only* difference is that is NOT
called automatically!

The same for the destructor destroy_foo(). You can release the resource
JUST AS you do in your C++ destructor!
NO, you can not. HoldSomething's destructor is called when the object
goes out of scope. So even if you code uses early returns, the resource
is still freed.
The only differences are some syntax and the fact that the
compiler will not call it for you!
A very important difference.
 
J

jacob navia

Keith said:
jacob navia said:
One example is the "addition" of strings instead of concatenation as
a function call. When A and B are strings, A+B != B+A, what is not at
all clear to me. It is better to use a function call to do that
operation.
[...]

That's a matter of taste.

True. The operation "Concatenate this strings" is in
my mind quite different from "Addition" , but maybe is
just a personal problem.

More serious misuses of operator overloading are when you try to give
meaning to meaningless operations like adding two dates for instance.

In general mathematical operations should be reserved for
*numbers*. This is a rule I have tried to follow, but
I can imagine that many people will use + to concatenate strings
when is available.

"2"+"2" --> "22"
:)
 
K

Keith Thompson

Ian Collins said:
jacob navia wrote: [...]
What is wrong with

FOO myFoo = new_foo(67,"J. Smith");

I just do not see the difference to C++.

Why is this constructor worst than its C++ counterpart?
Maybe you should read up on RAII.

A common C++ idiom is

struct HoldSomething
{
Something* held;

HoldSomething( Something* s ) { held = s; }
~HoldSomething() { delete held; }
};

or

struct HoldSomething
{
Something held;

HoldSomething() { held = AcquireSomething(); }
~HoldSomething() { Release Something( held ); }
};

Something can be any resource to be held by the scope. It is released
when the HoldSomething object goes out of scope.

In other words, having to invoke the constructor explicitly isn't a
huge deal, since you're declaring the object anyway, but having the
destructor invoked for you when the object ceases to exist is a very
nice convenience. jacob has argued against C++-style destructors
because they're difficult to implement; the point is to make the
compiler, rather than the programmer, do that hard work.
 
J

jacob navia

Keith said:
In other words, having to invoke the constructor explicitly isn't a
huge deal, since you're declaring the object anyway, but having the
destructor invoked for you when the object ceases to exist is a very
nice convenience. jacob has argued against C++-style destructors
because they're difficult to implement; the point is to make the
compiler, rather than the programmer, do that hard work.

This is the way C++ went. It means that every imaginable feature was
added to the language, in a baroque construction that defies
gravity.

No, it is not that I fear implementing constructors, but that I
fear that that feature will make the language more opaque, in the
sense of too much IMPLICIT code.

And that feature needs other features:

struct foo {
int a;
struct foo1 f1;
int b;
};

When we destroy foo, we should call the destructor of foo1
first, isn't it?

And what happens with:

struct foo {
int a;
struct foo1 *pf1;
};

Should we call the destructor of f1 with the pf1 pointer?
Or only when pf1 is not NULL?

This will produce the same complex set of rules that
C++ has.

Look. Let's NOT redo C++. C is simple. If you want
"the whole enchilada"... then program in C++ of course.

C and C++ remain distinct languages. C keeps things simple
and is fast, C++ has other uses, and can be as fast as C
sometimes.

:)
 
J

jxh

C + constructors/destructors + templates = C++ - overloading -
exceptions - inheritance/methods.

If we combine this proposal with jacob navia proposal, we get:
[snip]

My proposal is C + templates. My statement about constructors and
destructors was meant to be orthogonal.
Which doesn't mean that C++ would have been small. I don't
think that the biggest part of C++ isn't in inheritance and
methods. Actually, I would rather think that every component of
C++ participates to the size of the language.

I do not advocate turning C into C++. We already have C++. But
there are certainly constructs in C++ that C programmers would
find useful.
Maybe I'm wrong, and templates + constructors/destructors
are very easy to implement in a few LOC, but you've to give
evidence in order to prove that your proposal is realistic.

I don't know how many lines of code it would be, but template
implementations already exist for C++ compilers. It seems that
C compiler vendors would not be overly burdened to provide the
same feature for C.

-- James
 
J

jacob navia

jxh wrote:

[snip]
I don't know how many lines of code it would be, but template
implementations already exist for C++ compilers. It seems that
C compiler vendors would not be overly burdened to provide the
same feature for C.

You are dreaming.

Template implementations are C++ implementations of templates, that
suppose a C++ object layout, a class hierarchy, traits and many other
features that are absent in C. The compiler people would need to
take out those "features" of C++ from their template implementations
and in those complex mountains of code that would be a work for titans.

There are two ways to do that in C:
1) With an external utility, as I hinted yesterday.
2) Using compile time functions

I have implemented in experimental versions of my compiler system,
"compile time" functions, that are loaded during the compilation
by the compiler and generate code on the fly. The input
of those function is a compiler event (function definition,
function call, or simply a user triggered event like

myList = list<int>;

The list<> compile time function takes a type and generates
C text, that is passed to the compiler in an output buffer.

Using this mechanism I implemented most of the Eiffel
design by contract feature. That was a good test for it,
but at the same time I am still figuring out the consequences.

Should the generated code be passed through the preprocessor again?
Or should it go directly to the compiler?

How can we intermix text and commands? Should we use a lisp
macros syntax?

Etc Etc. Too many open questions, I have had no time to follow
this idea to the end yet.

But you are welcome to participate. For the time being, a small
utility would be nice to have. Just like a preprocessor.
 
I

Ian Collins

jacob said:
This is the way C++ went. It means that every imaginable feature was
added to the language, in a baroque construction that defies
gravity.
You're wandering of into rant land again... The topic was the automatic
calling of destructors.
No, it is not that I fear implementing constructors, but that I
fear that that feature will make the language more opaque, in the
sense of too much IMPLICIT code.
There I would agree with you, don't forget I wasn't proposing adding
them, just putting forward an example of where they can be useful and
enable an idiom not supported by C.
And that feature needs other features:

struct foo {
int a;
struct foo1 f1;
int b;
};

When we destroy foo, we should call the destructor of foo1
first, isn't it?
That's pretty obvious.
And what happens with:

struct foo {
int a;
struct foo1 *pf1;
};

Should we call the destructor of f1 with the pf1 pointer?
Or only when pf1 is not NULL?
Pointers don't have destructors.
This will produce the same complex set of rules that
C++ has.
C++ rules for destructor sequencing are pretty simple and logical.
 
I

Ian Collins

jacob said:
jxh wrote:

[snip]
I don't know how many lines of code it would be, but template
implementations already exist for C++ compilers. It seems that
C compiler vendors would not be overly burdened to provide the
same feature for C.

You are dreaming.

Template implementations are C++ implementations of templates, that
suppose a C++ object layout, a class hierarchy, traits and many other
features that are absent in C. The compiler people would need to
take out those "features" of C++ from their template implementations
and in those complex mountains of code that would be a work for titans.
It probably wouldn't be that bad. C++ treats function templates as
distinct from class templates, so porting the former, maybe as a front
end to a C compiler, might not be too difficult.
 
J

jacob navia

Ian said:
You're wandering of into rant land again... The topic was the automatic
calling of destructors.

There I would agree with you, don't forget I wasn't proposing adding
them, just putting forward an example of where they can be useful and
enable an idiom not supported by C.

That's pretty obvious.

Maybe for you, but C has no classes, and it could be that we call
the destructor of foo first, that can decide if it calls the
foo1 destructor before doing something, or after, or not at all.

Here the rules are not the same, even if we could design similar
rules.
Pointers don't have destructors.


Same as above. Pretty obvious but why not? Does this means
that destructors can't be called ever with the result of malloc?

Ahh but wait... we should introduce "new" then, to do the
allocaton that will need cleanup.

You see the problems?

A feature leads to another feature, that needs yet another feature,
and people say:

"Let the compiler do the work"

and the language grows and grows
and swallows everyone of the implementors alive because
feature xyz interfers with feature zyx, the rules go for
pages and pages and pages.

The rules of operator resolution
go for something like 6-7 PAGES of incomprehensible specs
that can't be followed by a brain but only by a CPU.

Have you tried to read the C++ standard? I did.

C++ rules for destructor sequencing are pretty simple and logical.

Yes, but it has the class hierarchy rules, that C doesn't have.

In C the rules can't be the same. We would need a new set of rules,
and some years of testing of those rules in the field, etc.
 
I

Ian Collins

Same as above. Pretty obvious but why not? Does this means
that destructors can't be called ever with the result of malloc?

Ahh but wait... we should introduce "new" then, to do the
allocaton that will need cleanup.

You see the problems?
Didn't you see the bit above where I said I agreed with you?
Have you tried to read the C++ standard? I did.
I paid my $18 and downloaded it the day it was released. Not light
reading by any means, but a good reference document.
 
K

Keith Thompson

jacob navia said:
Keith said:
jacob navia said:
One example is the "addition" of strings instead of concatenation as
a function call. When A and B are strings, A+B != B+A, what is not at
all clear to me. It is better to use a function call to do that
operation.
[...]
That's a matter of taste.

True. The operation "Concatenate this strings" is in
my mind quite different from "Addition" , but maybe is
just a personal problem.

More serious misuses of operator overloading are when you try to give
meaning to meaningless operations like adding two dates for instance.

In general mathematical operations should be reserved for
*numbers*. This is a rule I have tried to follow, but
I can imagine that many people will use + to concatenate strings
when is available.

"2"+"2" --> "22"
:)

Since neither "2" nor "22" is a number, I don't see anything
particularly odd about that. (I suppose you could define "+" so
"2"+"2" yields "4", but then you have to deal with "2"+"foobar".)

Pointers aren't numbers, but we can add an integer to a pointer in
standard C.

Similarly, adding two dates doesn't make sense, but adding a date to a
duration to get a date, or subtracting two dates to get a duration, is
quite useful.
 
J

jxh

You are dreaming.

Template implementations are C++ implementations of templates,
that suppose a C++ object layout, a class hierarchy, traits
and many other features that are absent in C. The compiler
people would need to take out those "features" of C++ from
their template implementations and in those complex mountains
of code that would be a work for titans.

Well, the obvious hyperbole aside, the following code:

template <typename T>
struct T_listnode {
T value;
struct T_listnode<T> *next;
};

would work both in C++ and in the hypothetical C template
implementation. So, in principle the C++ implementation could
just stand as is in the C implementation. Everything else you
mentioned is QOI.

-- James
 
K

Keith Thompson

jacob navia said:
This is the way C++ went. It means that every imaginable feature was
added to the language, in a baroque construction that defies
gravity.

You want to add features to C. It seems that every proposed new
feature that you like is necessary, and every proposed new feature
that you don't like is baroque and will destroy the language.

You think operator overloading is important. I think constructors and
destructors, perhaps along with exception handling, are more important
than operator overloading -- though I'm not really convinced either
should be added to the standard.
No, it is not that I fear implementing constructors, but that I
fear that that feature will make the language more opaque, in the
sense of too much IMPLICIT code.

In a sense, all generated code is implicit. Something as simple as a
function call can require substantial generated machine code to
implement it, including deciding when things are allocated and
deallocated. I'm very glad the compiler figures all that out for me,
so I don't have to.
And that feature needs other features:

struct foo {
int a;
struct foo1 f1;
int b;
};

When we destroy foo, we should call the destructor of foo1
first, isn't it?

When we destroy an object of type ``struct foo'', yes, we need to
destroy that object's ``f1'' member, since that member (which is an
object) reaches the end of its lifetime at the same time.
And what happens with:

struct foo {
int a;
struct foo1 *pf1;
};

Should we call the destructor of f1 with the pf1 pointer?
Or only when pf1 is not NULL?

No, no destructor would be implicitly invoked for the pf1 member, any
more than the pointer should be implicitly free()d. That would be
disastrous if the pointed-to object is "owned" by something else. If
the ``struct foo'' really owns the ``struct foo1'' object (which is an
issue of program logic), its destructor can invoke the ``struct foo1''
destructor implicitly.
This will produce the same complex set of rules that
C++ has.

Some of them, but not necessarily all of them. It might be possible
to define a simpler model for constructors and destructors than what
C++ has. (That's not to imply that I have any idea what such a model
would look like.)
Look. Let's NOT redo C++. C is simple. If you want
"the whole enchilada"... then program in C++ of course.

C and C++ remain distinct languages. C keeps things simple
and is fast, C++ has other uses, and can be as fast as C
sometimes.

Then why do you want to change C at all?

Yes, I know, you want to "improve" C without bringing in all of C++.
But it's by no means obvious *which* features are most important.
 
J

jxh

Isn't that just function overloading with a different name?
Any form of templates requires some form of function name
overloading and name mangling.

It is only name mangling, not function name overloading.
Function overloading would not become a feature of C. But the
work of name mangling would be something taken care of by the
compiler in the case of templates (the analog of creating a
unique identifier with the paste operator in C macros).

-- James
 

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
473,796
Messages
2,569,645
Members
45,369
Latest member
Carmen32T6

Latest Threads

Top