Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Is this kosher wrt Generic?

189 views
Skip to first unread message

Chris M. Thomasson

unread,
Nov 5, 2019, 1:55:58 AM11/5/19
to
An experiment:
_________________________________
#include <stdio.h>


struct foo_0
{
unsigned int blah0;
};

struct foo_1
{
unsigned int blah0;
unsigned int blah1;
};


void foo_0(struct foo_0* self, int a, int b)
{
printf("foo_0(%p, blah0: %u, %u, %u)\n",
(void*)self, self->blah0, a, b);
}

void foo_1(struct foo_1* self, int a, int b)
{
printf("foo_1(%p, blah0: %u, blah1: %u, %u, %u)\n",
(void*)self, self->blah0, self->blah1, a, b);
}


#define foo(a, ...) \
(_Generic((a), \
struct foo_0*: foo_0, \
struct foo_1*: foo_1 \
)((a), __VA_ARGS__))


int main(void)
{
struct foo_0 f0 = { 1 };
struct foo_1 f1 = { 2, 3 };

foo(&f0, 4, 5); // Calls foo_0
foo(&f1, 4, 5); // Calls foo_1

return 0;
}
_________________________________

foo seems pretty nice to me. :^)

Bonita Montero

unread,
Nov 5, 2019, 2:12:08 AM11/5/19
to
Forget generic programming in C; that't ultimatively ugly!

Thiago Adams

unread,
Nov 5, 2019, 6:20:04 AM11/5/19
to
The only thing I don't like is that we need to add all types in
one place instead of being able to declare separately.
This makes difficult to add new types. For instance, to add food_3.


Thiago Adams

unread,
Nov 5, 2019, 6:52:19 AM11/5/19
to
On Tuesday, November 5, 2019 at 5:12:08 AM UTC-2, Bonita Montero wrote:
> Forget generic programming in C; that't ultimatively ugly!

This feature replaces c++ function overloading.
The problem in to add function overloading in C, is that this make
the compiler much more complicated because it has to add a lot of
rules about type deduction.

Instead of saying that C is ugly you could try to understand better
what is behind C language. After this, you can compare with C++ and
also try to improve C.

Bonita Montero

unread,
Nov 5, 2019, 7:12:14 AM11/5/19
to
> The problem in to add function overloading in C, is that this make
> the compiler much more complicated because it has to add a lot of
> rules about type deduction.

Use C++.

> Instead of saying that C is ugly you could try to understand better
> what is behind C language. ...

The simplicity-paradigma o C isn't modern.

Thiago Adams

unread,
Nov 5, 2019, 7:24:40 AM11/5/19
to
In my view, simplicity is always modern.

Some of my favorite quotes.

"Simplicity is a great virtue but it requires hard work to
achieve it and education to appreciate it. And to make matters
worse: complexity sells better.”
― Edsger Wybe Dijkstra

The only thing I would change in this quote is instead of "education
to appreciate it" I would put "experience to appreciate it".

"Complexity sells better" works until some point. C++ has passed
this point and it complexity is playing against it.


"Simplicity is prerequisite for reliability."
― Edsger W. Dijkstra



Bonita Montero

unread,
Nov 5, 2019, 7:33:50 AM11/5/19
to
>> The simplicity-paradigma o C isn't modern.

> In my view, simplicity is always modern.

When a language has a simplicity that leads to error-prone programming
and implementing a problem takes much more work than in other languages,
this language isn't appropriate.

> "Complexity sells better" works until some point. C++ has
> passed this point and it complexity is playing against it.

C++ is of course not appropriate for beginners, but you
don't need to be a top-developer to handle toe language.

Thiago Adams

unread,
Nov 5, 2019, 7:57:22 AM11/5/19
to
On Tuesday, November 5, 2019 at 10:33:50 AM UTC-2, Bonita Montero wrote:
> >> The simplicity-paradigma o C isn't modern.
>
> > In my view, simplicity is always modern.
>
> When a language has a simplicity that leads to error-prone programming
> and implementing a problem takes much more work than in other languages,
> this language isn't appropriate.

When a language becomes too complex, it creates drag for the
programmers. It removes the focus from the problem and put
it in a language. You don't learn how to solve problems or
algorithms you learn how to deal with the language details.
This is not a good investment of your time, because when you have
experience you will see that C can solve the problems for you in
a much simpler way.
When I watch C++ conferences like cppcon, people are talking about
C++ performance problems or C++ pitfalls or about new features that
will bring new performance problem and pitfalls for the next year.
Do you know what I think nowadays? I don't have any of these problems
because they just doesn't exist in C.

C is dangerous but it is simple. When you know the tool you
know exactly what the compiler is doing and you know from a long
time what is important to do and check. How to deal with the
details.

C++ is complex and nobody knows happens under the hood. This is why
site that shows the compiler output are so useful nowadays for C++
community.

C is not perfect and C++ is not so bad. Personally I wish I could
select some features from both and add some extra ones.
This is why I created my own C front end.









Bonita Montero

unread,
Nov 5, 2019, 8:08:55 AM11/5/19
to
> When a language becomes too complex, it creates drag for the
> programmers. It removes the focus from the problem and put
> it in a language.

That's not true. More features don't make a language harder to handle,
i.e. it would take more time to solve an issue. In C++ you sometimes
fiddle around with some language-features because you find some new
details, but the language and the standdard-lib is so powerful that
it takes usually much less time than to do the same thing in C and
it's less error-prone.

> When I watch C++ conferences like cppcon, people are talking about
> C++ performance problems or C++ pitfalls or about new features that
> will bring new performance problem and pitfalls for the next year.

There are some pifalls, but the most discussed pitfalls don't have a
practical relevance.

> C is dangerous but it is simple.

It's frustrating to program in C because even simple things take a
lot of time. And whereas in C it's harder to understand the syntax
and semantics of the language than in C, in C it's extremely easy
to shoot you in the foot at runtime.

> C++ is complex and nobody knows happens under the hood.

That's not true. The core-language language-features are designed
for simplicity except some complex implementation-details for RTTI
and exception-handling.
The standard-library might be internally more complex, f.e. you don't
know a std::map<K, V> internally works, but that's not a drawback but
a feature, since you can be sure that it is implemented as efficient
as the semantics allows.

Thiago Adams

unread,
Nov 5, 2019, 8:12:57 AM11/5/19
to
What is interesting, is that you are posting in a c group:

I believe you have some felling like:

- These programmers use C because they are ignorant about C++.
if they know how C++ is good probably they would not use C.

- These programmers use C because they are not clever enough
to learn C++.

- These programmers stopped in some point in time, and never
updated their knowledge about C++.

But also:

- If my assumptions are not true (I need to check) why someone still
using C? Maybe C is good?

- C is fast.
- C is simple.

:D

Bonita Montero

unread,
Nov 5, 2019, 8:20:49 AM11/5/19
to
> - C is fast.
> - C is simple.
The language is simple, but it isn't simple to handle complex problems
in C because of the simplicity of C.

Thiago Adams

unread,
Nov 5, 2019, 8:21:44 AM11/5/19
to
On Tuesday, November 5, 2019 at 11:08:55 AM UTC-2, Bonita Montero wrote:
> > When a language becomes too complex, it creates drag for the
> > programmers. It removes the focus from the problem and put
> > it in a language.
>
> That's not true. More features don't make a language harder to handle,
> i.e. it would take more time to solve an issue. In C++ you sometimes
> fiddle around with some language-features because you find some new
> details, but the language and the standdard-lib is so powerful that
> it takes usually much less time than to do the same thing in C and
> it's less error-prone.

I can select some compiler errors from C++ that can keep a beginner
busy for hours. This is drag.
Some parametric containers and algorithms of C++ can save time.
This is true.

> > When I watch C++ conferences like cppcon, people are talking about
> > C++ performance problems or C++ pitfalls or about new features that
> > will bring new performance problem and pitfalls for the next year.
>
> There are some pifalls, but the most discussed pitfalls don't have a
> practical relevance.
>
> > C is dangerous but it is simple.
>
> It's frustrating to program in C because even simple things take a
> lot of time. And whereas in C it's harder to understand the syntax
> and semantics of the language than in C, in C it's extremely easy
> to shoot you in the foot at runtime.

Maybe you are trying to use C in the same way of C++.

> > C++ is complex and nobody knows happens under the hood.
>
> That's not true. The core-language language-features are designed
> for simplicity except some complex implementation-details for RTTI
> and exception-handling.
> The standard-library might be internally more complex, f.e. you don't
> know a std::map<K, V> internally works, but that's not a drawback but
> a feature, since you can be sure that it is implemented as efficient
> as the semantics allows.

Have you seen this?
CppCon 2019: Chandler Carruth “There Are No Zero-cost Abstractions”
https://youtu.be/rHIkrotSwcc

Chandler Carruth shows a optimization problem from a very simple
unique_ptr that should disappear completely but this is not.




Bonita Montero

unread,
Nov 5, 2019, 8:27:58 AM11/5/19
to
> I can select some compiler errors from C++ that can keep a beginner
> busy for hours. This is drag.

Of course C++ is nothing for beginners.
But C isn't also. Whereas it is hard for a beginner to understand
C++ it's hard in C to write programs that are beyond simplicity.

>> It's frustrating to program in C because even simple things take a
>> lot of time. And whereas in C it's harder to understand the syntax
>> and semantics of the language than in C, in C it's extremely easy
>> to shoot you in the foot at runtime.

> Maybe you are trying to use C in the same way of C++.

That's not possible,

> Have you seen this?
> CppCon 2019: Chandler Carruth “There Are No Zero-cost Abstractions”
> https://youtu.be/rHIkrotSwcc
> Chandler Carruth shows a optimization problem from a very simple
> unique_ptr that should disappear completely but this is not.

Such issues are rare and personally I've not many problems managing
them.

Thiago Adams

unread,
Nov 5, 2019, 8:42:52 AM11/5/19
to
On Tuesday, November 5, 2019 at 11:27:58 AM UTC-2, Bonita Montero wrote:
> > I can select some compiler errors from C++ that can keep a beginner
> > busy for hours. This is drag.
>
> Of course C++ is nothing for beginners.
> But C isn't also. Whereas it is hard for a beginner to understand
> C++ it's hard in C to write programs that are beyond simplicity.

My advice. Let's the beginners use std::string end std::vector and
std::map in one peace of code that is not so important.
Meanwhile teach C and how to program in general.


> >> It's frustrating to program in C because even simple things take a
> >> lot of time. And whereas in C it's harder to understand the syntax
> >> and semantics of the language than in C, in C it's extremely easy
> >> to shoot you in the foot at runtime.
>
> > Maybe you are trying to use C in the same way of C++.
>
> That's not possible,
>
> > Have you seen this?
> > CppCon 2019: Chandler Carruth “There Are No Zero-cost Abstractions”
> > https://youtu.be/rHIkrotSwcc
> > Chandler Carruth shows a optimization problem from a very simple
> > unique_ptr that should disappear completely but this is not.
>
> Such issues are rare and personally I've not many problems managing
> them.

Even when the c++ is not good it is faster. The computers are so faster
nowadays and the c++ alternatives are slow. No one notices.

But if you want something faster, use C.







Bonita Montero

unread,
Nov 5, 2019, 8:51:44 AM11/5/19
to
>> Of course C++ is nothing for beginners.
>> But C isn't also. Whereas it is hard for a beginner to understand
>> C++ it's hard in C to write programs that are beyond simplicity.

> My advice. Let's the beginners use std::string end std::vector
> and std::map in one peace of code that is not so important.
> Meanwhile teach C and how to program in general.

I don't deny that C++ is hard to handle for beginners. But C++ isn't
a beginners language. But with C it's also hard for a beginner to solve
problems that are beyond simplicity.

> Even when the c++ is not good it is faster.

Not much.

Bonita Montero

unread,
Nov 5, 2019, 9:31:50 AM11/5/19
to
> Chandler Carruth shows a optimization problem from a very simple
> unique_ptr that should disappear completely but this is not.

What stupid things is he doing with unique_ptrs?
You don't pass them as temporaries in this way. It's better to

void foo( unique_ptr<string> str )!

With this function you can only do something like this:

#include <iostream>
#include <memory>

using namespace std;

void foo( unique_ptr<string> str )
{
cout << *str << endl;
}

int main()
{
foo( unique_ptr<strign>( new string( "hello world" ) ) );
}

But this absolutely doesn't make sense because the string would
die at the same point where the unique_ptr dies.
So you could simply pass a temporary string object as well:

void foo( unique_ptr<string> str )!

With this function you can only do something like this:

#include <iostream>
#include <memory>

using namespace std;

void foo( string const &str )
{
cout << str << endl;
}

int main()
{
foo( string( "hello world" ) );
}

unique_ptrs are usually kept in a central place and normal pointers
whose lifetime are shorter than that of the unique_ptr are passed
around.

Bart

unread,
Nov 5, 2019, 9:49:35 AM11/5/19
to
On 05/11/2019 13:51, Bonita Montero wrote:
>>> Of course C++ is nothing for beginners.
>>> But C isn't also. Whereas it is hard for a beginner to understand
>>> C++ it's hard in C to write programs that are beyond simplicity.
>
>> My advice. Let's the beginners use std::string end std::vector
>> and std::map in one peace of code that is not so important.
>> Meanwhile teach C and how to program in general.
>
> I don't deny that C++ is hard to handle for beginners. But C++ isn't
> a beginners language.

It isn't a language for anybody. It is just too complex even for
experienced coders. Actually when you have more experience, you
appreciate simplicity and clean code more.

IMO it isn't even a proper language, just a collection of
language-building features, and the results are poor (like trying to
extend C using macros). That's something than many languages just can't
resist (eg. Rust, Nim with their advanced meta-programming).

C and like languages (eg. mine) work because there is a place still for
low level languages (which might be used, for example, to implement
higher level ones, or they are quick to build, their compilers can be
small etc).

My own take on such a language is not much higher level than C (it has,
for example, modules) but is full of small features that make everyday
coding more convenient, and very for beginners too.

I'd wager that:

* Very few of them are in C++ ready-to-use

* If they are, they would use clunky, overwhelming syntax that defeats
the purpose

* If they aren't, they can probably be hacked together with C++'s
afore-mentioned features, but with the same problems with syntax


(I'd list some but here they'd be off-topic.)

Bonita Montero

unread,
Nov 5, 2019, 9:53:58 AM11/5/19
to
>> I don't deny that C++ is hard to handle for beginners. But C++ isn't
>> a beginners language.

> It isn't a language for anybody. It is just too complex even for
> experienced coders. Actually when you have more experience, you
> appreciate simplicity and clean code more.

I'm using C++ since 1993 and I don't have many problems with it.

> C and like languages (eg. mine) work because there is a place still
> for low level languages (which might be used, for example, to implement
> higher level ones, or they are quick to build, their compilers can be
> small etc).

When you are skilled enough to manage C++ you can implement code in less
time in C++ than in C and it's more robust.

Malcolm McLean

unread,
Nov 5, 2019, 10:00:31 AM11/5/19
to
On Tuesday, 5 November 2019 14:49:35 UTC, Bart wrote:
> On 05/11/2019 13:51, Bonita Montero wrote:
> >>> Of course C++ is nothing for beginners.
> >>> But C isn't also. Whereas it is hard for a beginner to understand
> >>> C++ it's hard in C to write programs that are beyond simplicity.
> >
> >> My advice. Let's the beginners use std::string end std::vector
> >> and std::map in one peace of code that is not so important.
> >> Meanwhile teach C and how to program in general.
> >
> > I don't deny that C++ is hard to handle for beginners. But C++ isn't
> > a beginners language.
>
> It isn't a language for anybody. It is just too complex even for
> experienced coders. Actually when you have more experience, you
> appreciate simplicity and clean code more.
>
Most people use a subset of the language. That leads to C++ with very
different flavours. It could be template-heavy, it could use a lot
of lambdas, it could rely heavily on Boost.

That leads to integration and team management problems.

David Brown

unread,
Nov 5, 2019, 10:31:12 AM11/5/19
to
Alternatively, you can say it leads to different people working with
different parts of the code - and that's a good thing. The skills you
need for making reusable libraries, or low-level code, or efficient
utility code, or gui application code, are all different. With C++, you
can remain within the one language and handle these - it covers a far
wider range of coding needs than any other language.

You are right that people typically use only a subset of the language.
That applies to all languages, but more so for bigger languages.
(Stand up if you think Python is an easy language to learn. Sit down
again if you have used metaclasses. Sit down if you have created
generators, decorators, and asynchronous code.) And that is absolutely
fine - you don't need to be an expert on everything.

If this leads to integration and team management problems, then the
problem lies with the team management. (Usually the programmers will
get blamed for the problems, of course - but it is management's fault.)

Bart

unread,
Nov 5, 2019, 10:59:45 AM11/5/19
to
But is it readable? It is readable to anyone else?

People have their own preference for different mixes of languages, my
own is to use a low-level static/compiled language in combination with a
higher-level dynamic/interpreted one.

Both are simple. Both compile near-instantly. Both avoid as much
possible macros and meta-programming (features are built-in rather than
implemented via low-level language facilities).

I first came across C++ in 1990s, and wondered what all that OOP stuff
was all about; what could it do for me?

The first examples I saw I think were something to do with first-class
handling of strings and arrays. Something I'd already been using for
years in my script language!

Yes, C++ would normally generate faster programs. But if a simple,
clean, easy to write script language was as fast as C++, who would use
C++? No one. The trick is to get the balance right so as to code in the
easy language as much as possible.

You don't need to write 100% of code in the hard one, but C++ seems to
want to take over everything.

Bonita Montero

unread,
Nov 5, 2019, 11:12:08 AM11/5/19
to
>> When you are skilled enough to manage C++ you can implement
>> code in less time in C++ than in C and it's more robust.

> But is it readable? It is readable to anyone else?

With C you write a lot of code for a small amout of work. In C++ it's
the opposite way. C++'s complexity goes into the depth whereas in C
because of the lot of code you have much breadth. In this sense I
think C++ is a better choice when you know the language haf-way.

> I first came across C++ in 1990s, and wondered what all that
> OOP stuff was all about; what could it do for me?

OOP gives better structuring and is partitially self-documenting
(f.e. because of public and private methods).

> The first examples I saw I think were something to do with first-class
> handling of strings and arrays. Something I'd already been using for
> years in my script language!

Scripting-languages are high-level by nature and therefore have more
convenience.

> Yes, C++ would normally generate faster programs. But if a simple,
> clean, easy to write script language was as fast as C++, ...

https://benchmarksgame-team.pages.debian.net/benchmarksgame/fastest/python3-gpp.html


> who would se C++? No one. The trick is to get the balance
> right so as to code in the easy language as much as possible.

Of course C++ is suitable for things small scripts are writren in.

Malcolm McLean

unread,
Nov 5, 2019, 12:19:28 PM11/5/19
to
On Tuesday, 5 November 2019 16:12:08 UTC, Bonita Montero wrote:
>
> > I first came across C++ in 1990s, and wondered what all that
> > OOP stuff was all about; what could it do for me?
>
> OOP gives better structuring and is partitially self-documenting
> (f.e. because of public and private methods).
>
A lot of people are now much more sceptical about object-based
programming than they were. It can be done well or badly. One of the
best uses I've seen was in 3DS Max. But it is a 3D drawing package,
the elements in the scene are virtual objects, and naturally lend
themselves to that approach. Where the objects represent abstractions
such as "employee list view manager", the approach works less
successfully.

Ian Collins

unread,
Nov 5, 2019, 4:35:01 PM11/5/19
to
On 06/11/2019 01:57, Thiago Adams wrote:
>
> C is dangerous but it is simple. When you know the tool you
> know exactly what the compiler is doing and you know from a long
> time what is important to do and check. How to deal with the
> details.
>
> C++ is complex and nobody knows happens under the hood. This is why
> site that shows the compiler output are so useful nowadays for C++
> community.
>
> C is not perfect and C++ is not so bad. Personally I wish I could
> select some features from both and add some extra ones.
> This is why I created my own C front end.

The complexity in a programming problem is a constant. You can solve it
in the C way with a large number of simple steps, which introduces extra
complexity in making sure they all work together. You can solve it with
fewer step in a higher level language, which introduces extra complexity
to the language. The overall complexity remains constant.

--
Ian.


Bart

unread,
Nov 5, 2019, 5:16:22 PM11/5/19
to
A lot of the complexity in C++ appears to me gratuitous.

Even when it isn't, a considerable amount is exposed to the compiler
(ie. each time it runs, it has to process library source code to
implement features which in another language are compiled once)

In addition that extra complexity is exposed to the user, because when
they gets some minor thing wrong, it doesn't directly report that thing,
but reports instead the error that it invokes in the complex library code.

Advocates for the language like to pretend that it is for experts.

(To illustrate my points:

(1) Basic's PRINT X has to be written as:

std::cout << X << std::endl;

(2) See what happens when you inadvertently type this instead:

std::cout >> X >> std::endl;

)

Ian Collins

unread,
Nov 5, 2019, 5:34:17 PM11/5/19
to
On 06/11/2019 11:16, Bart wrote:
> On 05/11/2019 21:34, Ian Collins wrote:
>> On 06/11/2019 01:57, Thiago Adams wrote:
>>>
>>> C is dangerous but it is simple. When you know the tool you
>>> know exactly what the compiler is doing and you know from a long
>>> time what is important to do and check. How to deal with the
>>> details.
>>>
>>> C++ is complex and nobody knows happens under the hood. This is why
>>> site that shows the compiler output are so useful nowadays for C++
>>> community.
>>>
>>> C is not perfect and C++ is not so bad. Personally I wish I could
>>> select some features from both and add some extra ones.
>>> This is why I created my own C front end.
>>
>> The complexity in a programming problem is a constant.  You can solve it
>> in the C way with a large number of simple steps, which introduces extra
>> complexity in making sure they all work together. You can solve it with
>> fewer step in a higher level language, which introduces extra complexity
>> to the language.  The overall complexity remains constant.
>
> A lot of the complexity in C++ appears to me gratuitous.

You say that about a lot of things.

> Even when it isn't, a considerable amount is exposed to the compiler
> (ie. each time it runs, it has to process library source code to
> implement features which in another language are compiled once)

So?

Even the most basic C++ feature, automatic object destruction, greatly
simplifies and automates resource management which has to be handled
manually in C.

> In addition that extra complexity is exposed to the user, because when
> they gets some minor thing wrong, it doesn't directly report that thing,
> but reports instead the error that it invokes in the complex library code.

Some, much is built in. Compilers aren't psychic, if you use a library
function incorrectly, the compiler will tell you that you have used it
incorrectly.

> Advocates for the language like to pretend that it is for experts.

I don't, often I'd claim the opposite. Creating and using a dynamically
expandable robust array or string in C is much more of an "expert"
problem than it is in C++.

--
Ian.

Jorgen Grahn

unread,
Nov 5, 2019, 5:50:01 PM11/5/19
to
On Tue, 2019-11-05, Thiago Adams wrote:
...
> When I watch C++ conferences like cppcon, people are talking about
> C++ performance problems or C++ pitfalls or about new features that
> will bring new performance problem and pitfalls for the next year.

Don't judge actual C++ programming based on what you see in conference
videos. Most of us are concerned with getting work done, and don't
actively look for trouble. Talking about that would make for a very boring
conference ...

/Jorgen

--
// Jorgen Grahn <grahn@ Oo o. . .
\X/ snipabacken.se> O o .

Bart

unread,
Nov 5, 2019, 6:13:12 PM11/5/19
to
On 05/11/2019 22:34, Ian Collins wrote:
> On 06/11/2019 11:16, Bart wrote:

>>
>> A lot of the complexity in C++ appears to me gratuitous.
>
> You say that about a lot of things.

Maybe it is true about a lot of things! (Eg. auto-conf scripts.)

>> Even when it isn't, a considerable amount is exposed to the compiler
>> (ie. each time it runs, it has to process library source code to
>> implement features which in another language are compiled once)
>
> So?

So it makes the process cumbersome.

> Some, much is built in.  Compilers aren't psychic, if you use a library
> function incorrectly, the compiler will tell you that you have used it
> incorrectly.

But for features which you may not even know is a library function? I
don't actually know which bit of C++ is implemented in a library, and
which part is a built-in core feature.

>> Advocates for the language like to pretend that it is for experts.
>
> I don't, often I'd claim the opposite.  Creating and using a dynamically
> expandable robust array or string in C is much more of an "expert"
> problem than it is in C++.

This is a fragment I came across today:

for (auto i=0; ....)

I assumed i takes its type from the zero, and wondered what would happen
here:

#include <iostream>

int main() {
for (auto i=0; i<=5000000000; i+=100000000) {
std::cout << i << std::endl;
}
}

And I was right: i has type i32, and it will wrap or generate UB. Not
what you intended anyway. That 'auto' gives a false sense of security. A
compiler may report something given enough options, but apparently not
if the limit is a i64 variable.

BTW what happened to "_" separators for those long constants which I
thought C++ had? Neither my g++ nor rextester seems to like them.

I mentioned something about getting basic features right in an earlier
post, a lot of languages don't, yet do have esoteric ones.

Ian Collins

unread,
Nov 5, 2019, 6:29:46 PM11/5/19
to
On 06/11/2019 12:12, Bart wrote:
> On 05/11/2019 22:34, Ian Collins wrote:
>> On 06/11/2019 11:16, Bart wrote:
>
>>>
>>> A lot of the complexity in C++ appears to me gratuitous.
>>
>> You say that about a lot of things.
>
> Maybe it is true about a lot of things! (Eg. auto-conf scripts.)
>
>>> Even when it isn't, a considerable amount is exposed to the compiler
>>> (ie. each time it runs, it has to process library source code to
>>> implement features which in another language are compiled once)
>>
>> So?
>
> So it makes the process cumbersome.

To whom?

>> Some, much is built in.  Compilers aren't psychic, if you use a library
>> function incorrectly, the compiler will tell you that you have used it
>> incorrectly.
>
> But for features which you may not even know is a library function? I
> don't actually know which bit of C++ is implemented in a library, and
> which part is a built-in core feature.

It's no different to C, if you need to include a library header, it's in
a library!

>>> Advocates for the language like to pretend that it is for experts.
>>
>> I don't, often I'd claim the opposite.  Creating and using a dynamically
>> expandable robust array or string in C is much more of an "expert"
>> problem than it is in C++.
>
> This is a fragment I came across today:
>
> for (auto i=0; ....)
>
> I assumed i takes its type from the zero,

You assumed wrong.

> BTW what happened to "_" separators for those long constants which I
> thought C++ had? Neither my g++ nor rextester seems to like them.

Nothing. The digit separator is '.

> I mentioned something about getting basic features right in an earlier
> post, a lot of languages don't, yet do have esoteric ones.

Congratulations on googling a flaw in auto type deduction.

How about the "expert" problem I mentioned? I assume it doesn't fit
your narrative.

--
Ian.

guinne...@gmail.com

unread,
Nov 5, 2019, 7:20:04 PM11/5/19
to
On Tuesday, 5 November 2019 08:12:08 UTC+1, Bonita Montero wrote:
> Forget generic programming in C; that't ultimatively ugly!

The non-word “ultimatively” that you invented is the only ugly thing here.

Chris M. Thomasson

unread,
Nov 6, 2019, 1:38:13 AM11/6/19
to
On 11/5/2019 3:19 AM, Thiago Adams wrote:
> On Tuesday, November 5, 2019 at 4:55:58 AM UTC-2, Chris M. Thomasson wrote:
>> An experiment:
>> _________________________________
[...]
>> #define foo(a, ...) \
>> (_Generic((a), \
>> struct foo_0*: foo_0, \
>> struct foo_1*: foo_1 \
>> )((a), __VA_ARGS__))
>>
>>
>> int main(void)
>> {
>> struct foo_0 f0 = { 1 };
>> struct foo_1 f1 = { 2, 3 };
>>
>> foo(&f0, 4, 5); // Calls foo_0
>> foo(&f1, 4, 5); // Calls foo_1
>>
>> return 0;
>> }
>> _________________________________
>>
>> foo seems pretty nice to me. :^)

> The only thing I don't like is that we need to add all types in
> one place instead of being able to declare separately. > This makes difficult to add new types. For instance, to add food_3.

Yeah. Its not very convenient. To add in a new type:
_______________________________
#include <stdio.h>


struct foo_0
{
unsigned int blah0;
};

struct foo_1
{
unsigned int blah0;
unsigned int blah1;
};


void foo_0(struct foo_0* self, int a, int b)
{
printf("foo_0(%p, blah0: %u, %u, %u)\n",
(void*)self, self->blah0, a, b);
}

void foo_1(struct foo_1* self, int a, int b)
{
printf("foo_1(%p, blah0: %u, blah1: %u, %u, %u)\n",
(void*)self, self->blah0, self->blah1, a, b);
}



struct new_type
{
double v[3];
};

void foo_new_type(struct new_type* self, double a)
{
printf("foo_new_type(%p)\n",
(void*)self);
}

#define foo(a, ...) \
(_Generic((a), \
struct foo_0*: foo_0, \
struct foo_1*: foo_1, \
struct new_type*: foo_new_type \
)((a), __VA_ARGS__))


int main(void)
{
struct foo_0 f0 = { 1 };
struct foo_1 f1 = { 2, 3 };

foo(&f0, 4, 5); // Calls foo_0
foo(&f1, 4, 5); // Calls foo_1

struct new_type nt = { { -1., 1., 0. } };

foo(&nt, 3.14); // Calls foo_new_type

return 0;
}
_______________________________

Chris M. Thomasson

unread,
Nov 6, 2019, 3:06:39 AM11/6/19
to
On 11/4/2019 11:12 PM, Bonita Montero wrote:
> Forget generic programming in C; that't ultimatively ugly!

Its not the most attractive being out there for sure!

Although, for some very strange reason, you kind of remind me of JG over
on sci.math. Humm... Sorry for that. Btw, please, for the love of God,
try to give a proper attribution?

Bonita Montero

unread,
Nov 6, 2019, 4:02:27 AM11/6/19
to
> (To illustrate my points:
> (1) Basic's PRINT X has to be written as:
>     std::cout << X << std::endl;
> (2) See what happens when you inadvertently type this instead:
>     std::cout >> X >> std::endl;

You get an error-message that there isn't any operator >> defined
on std::ostream.

David Brown

unread,
Nov 6, 2019, 4:17:23 AM11/6/19
to
On 06/11/2019 00:12, Bart wrote:
> On 05/11/2019 22:34, Ian Collins wrote:
>> On 06/11/2019 11:16, Bart wrote:
>
>>>
>>> A lot of the complexity in C++ appears to me gratuitous.
>>
>> You say that about a lot of things.
>
> Maybe it is true about a lot of things! (Eg. auto-conf scripts.)

<https://en.wikipedia.org/wiki/Whataboutism>

>
>>> Even when it isn't, a considerable amount is exposed to the compiler
>>> (ie. each time it runs, it has to process library source code to
>>> implement features which in another language are compiled once)
>>
>> So?
>
> So it makes the process cumbersome.
>
>> Some, much is built in.  Compilers aren't psychic, if you use a
>> library function incorrectly, the compiler will tell you that you have
>> used it incorrectly.
>
> But for features which you may not even know is a library function? I
> don't actually know which bit of C++ is implemented in a library, and
> which part is a built-in core feature.

You know little about C++, yet feel qualified to tell people what a
terrible language it is and what its problems are?

It's fine to look at a language and say "that doesn't look like the sort
of language I want to learn and use - it's not a language for me". For
anything beyond that, you simply don't know enough to judge. (Here's a
clue for you - when lots of other people manage to use a language or
tool successfully, and you don't, maybe the problem is /not/ with the
language or tool.)

>
>>> Advocates for the language like to pretend that it is for experts.
>>
>> I don't, often I'd claim the opposite.  Creating and using a
>> dynamically expandable robust array or string in C is much more of an
>> "expert" problem than it is in C++.
>
> This is a fragment I came across today:
>
>    for (auto i=0; ....)
>
> I assumed i takes its type from the zero, and wondered what would happen
> here:
>
>    #include <iostream>
>
>    int main() {
>        for (auto i=0; i<=5000000000; i+=100000000) {
>              std::cout << i << std::endl;
>        }
>    }
>
> And I was right: i has type i32, and it will wrap or generate UB. Not
> what you intended anyway. That 'auto' gives a false sense of security. A
> compiler may report something given enough options, but apparently not
> if the limit is a i64 variable.

"auto" has its uses and its weaknesses, like every other feature in
programming. The trick is to learn the programming language and how to
use it.

>
> BTW what happened to "_" separators for those long constants which I
> thought C++ had? Neither my g++ nor rextester seems to like them.

You are confused - the C++ digit separator is '.

Bonita Montero

unread,
Nov 6, 2019, 4:56:11 AM11/6/19
to
>    int main() {
>        for (auto i=0; i<=5000000000; i+=100000000) {
>              std::cout << i << std::endl;
>        }
>    }

Even in C there are language-features which can be mis-used; but this
doesn't mean that there's a proper use for them. auto in a for-loop
doesn't make sense for me because it hides the type, f.e. in a range
-based for-loop.
But auto is necessary for declaring template-lambdas by name (and not
just as a parameter). And you need it when you have a return-type that
is decuced from other template-parameters.

Bart

unread,
Nov 6, 2019, 5:43:21 AM11/6/19
to
I might do but I can't see it because I get 80+ lines of output that
scrolls off the top of my console window.

That's on g++ 8.1.0. On MSVC on rextester.com, I get:

source_file.cpp(6): error C2784: 'std::basic_istream<_Elem,_Traits>
&std::operator >>(std::basic_istream<_Elem,_Traits> &&,_Ty &)': could
not deduce template argument for 'std::basic_istream<_Elem,_Traits> &&'
from 'std::ostream'
C:\Program Files (x86)\Microsoft Visual Studio
14.0\VC\INCLUDE\istream(1106): note: see declaration of 'std::operator >>'
source_file.cpp(6): error C2784: 'std::basic_istream<char,_Traits>
&std::operator >>(std::basic_istream<char,_Traits> &,unsigned char &)':
could not deduce template argument for 'std::basic_istream<char,_Traits>
&' from 'std::ostream'
C:\Program Files (x86)\Microsoft Visual Studio
14.0\VC\INCLUDE\istream(1096): note: see declaration of 'std::operator >>'
source_file.cpp(6): error C2784: 'std::basic_istream<char,_Traits>
&std::operator >>(std::basic_istream<char,_Traits> &,unsigned char *)':
could not deduce template argument for 'std::basic_istream<char,_Traits>
&' from 'std::ostream'
C:\Program Files (x86)\Microsoft Visual Studio
14.0\VC\INCLUDE\istream(1089): note: see declaration of 'std::operator >>'
source_file.cpp(6): error C2784: 'std::basic_istream<char,_Traits>
&std::operator >>(std::basic_istream<char,_Traits> &,signed char &)':
could not deduce template argument for 'std::basic_istream<char,_Traits>
&' from 'std::ostream'
C:\Program Files (x86)\Microsoft Visual Studio
14.0\VC\INCLUDE\istream(1082): note: see declaration of 'std::operator >>'
source_file.cpp(6): error C2784: 'std::basic_istream<char,_Traits>
&std::operator >>(std::basic_istream<char,_Traits> &,signed char *)':
could not deduce template argument for 'std::basic_istream<char,_Traits>
&' from 'std::ostream'
C:\Program Files (x86)\Microsoft Visual Studio
14.0\VC\INCLUDE\istream(1075): note: see declaration of 'std::operator >>'
source_file.cpp(6): error C2784: 'std::basic_istream<_Elem,_Traits>
&std::operator >>(std::basic_istream<_Elem,_Traits> &,_Elem &)': could
not deduce template argument for 'std::basic_istream<_Elem,_Traits> &'
from 'std::ostream'
C:\Program Files (x86)\Microsoft Visual Studio
14.0\VC\INCLUDE\istream(1050): note: see declaration of 'std::operator >>'
source_file.cpp(6): error C2784: 'std::basic_istream<_Elem,_Traits>
&std::operator >>(std::basic_istream<_Elem,_Traits> &,_Elem *)': could
not deduce template argument for 'std::basic_istream<_Elem,_Traits> &'
from 'std::ostream'
C:\Program Files (x86)\Microsoft Visual Studio
14.0\VC\INCLUDE\istream(1008): note: see declaration of 'std::operator >>'
source_file.cpp(6): error C2676: binary '>>': 'std::ostream' does not
define this operator or a conversion to a type acceptable to the
predefined operator
Microsoft (R) C/C++ Optimizing Compiler Version 19.00.23506 for x64

Thiago Adams

unread,
Nov 6, 2019, 7:50:48 AM11/6/19
to
In the standard we have the generic function sqrt .
It is implemented with _Generic.
Now imagine we want to add a new type that also
implements sqrt, for instance big int type.

In this we would have to change the code that is
provided by the standard to include the new type?
(this is the problem)

Alternatives?

One workaround is to build the final _Generic expression,
using parts of different sources (libs)

Something like:

#include <stdio.h>

//Lib 1
struct foo_0
{
unsigned int blah0;
};

struct foo_1
{
unsigned int blah0;
unsigned int blah1;
};


void foo_0(struct foo_0* self, int a, int b)
{
printf("foo_0(%p, blah0: %u, %u, %u)\n",
(void*)self, self->blah0, a, b);
}

void foo_1(struct foo_1* self, int a, int b)
{
printf("foo_1(%p, blah0: %u, blah1: %u, %u, %u)\n",
(void*)self, self->blah0, self->blah1, a, b);
}


#define LIB1_FOO_TYPES \
struct foo_0*: foo_0, \
struct foo_1*: foo_1


//Lib2

struct new_type
{
double v[3];
};

void foo_new_type(struct new_type* self, double a)
{
printf("foo_new_type(%p)\n",
(void*)self);
}


#define LIB2_FOO_TYPES \
struct new_type*: foo_new_type


// my program

#define foo(a, ...) \
(_Generic((a), \
LIB1_FOO_TYPES, \
LIB2_FOO_TYPES \

Thiago Adams

unread,
Nov 6, 2019, 7:56:24 AM11/6/19
to
When a function is overloaded and the compiler doesn't find any
match it prints all possibilities.
If we had function overload in C we also would have this kind of
situation.
The error is difficult to read in this case because of the
templates.

Comparing with _Generic, in case we don't find a match it says
one error message:

error: ‘_Generic’ selector of type ‘type’ is not compatible with
any association.

Bonita Montero

unread,
Nov 6, 2019, 8:05:58 AM11/6/19
to
For this code:

#include <iostream>

using namespace std;

int main()
{
cout >> "hello world" >> endl;
}

I get only a single error:

Thiago Adams

unread,
Nov 6, 2019, 8:33:11 AM11/6/19
to
On Wednesday, November 6, 2019 at 9:50:48 AM UTC-3, Thiago Adams wrote:
> On Wednesday, November 6, 2019 at 3:38:13 AM UTC-3, Chris M. Thomasson wrote:
> > On 11/5/2019 3:19 AM, Thiago Adams wrote:
> > > On Tuesday, November 5, 2019 at 4:55:58 AM UTC-2, Chris M. Thomasson wrote:
> > >> An experiment:
[...]
> > >> foo seems pretty nice to me. :^)
> >
> > > The only thing I don't like is that we need to add all types in
> > > one place instead of being able to declare separately. > This makes difficult to add new types. For instance, to add food_3.
> >
> > Yeah. Its not very convenient. To add in a new type:
[...]

> In the standard we have the generic function sqrt .
> It is implemented with _Generic.
> Now imagine we want to add a new type that also
> implements sqrt, for instance big int type.
>
> In this we would have to change the code that is
> provided by the standard to include the new type?
> (this is the problem)
>
> Alternatives?
>
[...]


I already have suggested in this group a feature
to build this generic selection automatically.

The basic idea is to add the concept of 'function tags'.

void foo_new_type(struct new_type* self, double a) : foo;

Function tag is the end part ': food';

This tag can be added at the function declaration.

(
The difference between this tag and C++ function overloading
is that it doesn't require name mangling.
)

There are two variants for this 'function tag' feature.

Option 1: Any function signature

Very generic where we can have the same tag for any
function signature.

for instance:

void foo_new_type(int i) : foo;

this is the way C++ is.

In case you try foo_new_type(1.2f)
it will print the error with all candidates.

Advantage - easy to write.

This model can be used in the sample (foo) because it has
has different arguments for each foo.

Option 2: With restrict signature.
In this case we need a declaration for the 'interface' for foo.
for instance, one name/concept that I would like to use for any
type is destroy. All function destroy have the same signature.

So the generic interface for destroy could be:

void destroy(auto);

and the implementation.

void x_destroy(struct X* p) : destroy;

the implementation must follow the destroy contract.

disadvantage - have to write something else - this contract.








Thiago Adams

unread,
Nov 6, 2019, 8:57:05 AM11/6/19
to
On Wednesday, November 6, 2019 at 10:33:11 AM UTC-3, Thiago Adams wrote:
> Option 2: With restrict signature.
> In this case we need a declaration for the 'interface' for foo.
> for instance, one name/concept that I would like to use for any
> type is destroy. All function destroy have the same signature.
>
> So the generic interface for destroy could be:
>
> void destroy(auto);
>
> and the implementation.
>
> void x_destroy(struct X* p) : destroy;
>
> the implementation must follow the destroy contract.
...


This feature with contract is similar of some C++ operator
overloading.

Some operators in C++ have strict contracts. For instance,
the operator delete must be:

void operator delete(void * p)

if I change the return type for instance:

void* operator delete(void * p)
error: ‘operator delete’ must return type ‘void’


In my view, destroy, init, delete , new, swap, clone etc..
are 'universal operators', just like + , - 0 and they could exist by
default in the C language using this feature of function tags.

Not only this, but some algorithms are just a matter of
composition and destroy and init are samples of that.
In this case the language could generate these operators for
us by composition.

If we think in terms of sqrt we don't want function overloading
with zero or more than one argument.
In this aspect _Generic has advantages over function overloading
where anything is possible.



Bart

unread,
Nov 6, 2019, 9:26:39 AM11/6/19
to
I haven't tried function overloading (where you have to look at all
combinations of parameter and return types to choose the right function,
and there might still be ambiguities).

Class-based (or method-based) is simpler, and the choice is made on the
first parameter only. Take this example in fantasy C:

typedef struct {
void SayMyName(bart &s) {puts("Bart");}
} bart;

typedef struct {
void SayMyName(lisa &s) {puts("Lisa");}
} lisa;

bart B;
lisa L;

B.SayMyName(); // Bart
L.SayMyName(); // Lisa

bart.SayMyName(B); // Bart
lisa.SayMyName(L); // Lisa

Example shows using method-syntax based on an instance of each struct,
and also a conventional call but here you need to know the type.

(My language is supposed to be able to do this, but I've never used the
feature, and the commented-out (with "!") lines don't work ATM with the
static language, although they do with the dynamic one):

----------------------------------
record bart=
proc saymyname(bart &s) =
println "Bart"
end
end

record lisa=
proc saymyname(lisa &s) =
println "Lisa"
end
end

proc start=
var bart B
var lisa L

! B.saymyname()
! L.saymyname()
bart.saymyname(B)
lisa.saymyname(L)
end
----------------------------------

Another issue is that this scheme only works with record/struct types
where you can add a method definition to the type.

But an older idea used an 'extend type' feature to add methods not only
to existing records/structs, but to other types too. Never implemented
though.)

Thiago Adams

unread,
Nov 6, 2019, 12:04:40 PM11/6/19
to
C++ had some proposals to allow both ways.
https://en.wikipedia.org/wiki/Uniform_Function_Call_Syntax


Another alternative I can think of is to create a identifier
at compilation phase.

For instance:

struct X x;
_Join(_ShortName(x), L"_Destroy")(x);

ShortName returns X (without struct) and _Joins
concatenates X and _Destroy resultant in X_Destroy.

This allow us to create the macro 'destroy' solving one place
definition problem of _Generic.

If necessary (to make the compiler simpler) we also can have
just one _MakeIdentifer("%s_Destroy", x);

This would be a complement of _Generic not 100% replacement.


David Brown

unread,
Nov 6, 2019, 4:45:46 PM11/6/19
to
You can get a fair amount of that today in C, though it is a little more
verbose:

#include <stdio.h>

typedef struct bart {
void (*SayMyName)(struct bart*);
} bart;

void bart_SayMyName(struct bart* s) { puts("Bart"); }
#define bart_init { bart_SayMyName }

typedef struct lisa {
void (*SayMyName)(struct lisa*);
} lisa;

void lisa_SayMyName(struct lisa * s) { puts("Lisa"); }
#define lisa_init { lisa_SayMyName }

bart B = bart_init;
lisa L = lisa_init;

void foo(void) {
B.SayMyName(&B);
L.SayMyName(&L);

bart_SayMyName(&B);
lisa_SayMyName(&L);
}


Not perfect, but some people find such constructs useful.

Tim Rentsch

unread,
Nov 9, 2019, 12:04:05 AM11/9/19
to
Mother Goose comes to comp.lang.c.

Ian Collins

unread,
Nov 9, 2019, 2:45:59 PM11/9/19
to
I assume by your recent tone that you have been smoking something...

Take a concrete example: resource management.

In C resource management is transparent but manual, the programmer has
to track what has been allocated and know when to free it. This, along
with the opportunity for error, gets quite complex when a resource is
shared, especially between threads.

In C++, adding some extra complexity in the language (constructors and
automatic object destruction) removes the complexity and the
opportunities for error from the user code.

The code is still doing the same work and may well compile down to
equivalent assembler. The overall complexity remains constant.

--
Ian.

Chris M. Thomasson

unread,
Nov 10, 2019, 2:21:24 AM11/10/19
to
Yes. This should be able to work. Also, you are making me think of
x-macros. The Chaos lib... and such. :^) Thanks. Not convenient, but can
work.

Chris M. Thomasson

unread,
Nov 10, 2019, 2:22:27 AM11/10/19
to
On 11/6/2019 5:56 AM, Thiago Adams wrote:
> On Wednesday, November 6, 2019 at 10:33:11 AM UTC-3, Thiago Adams wrote:
>> Option 2: With restrict signature.
>> In this case we need a declaration for the 'interface' for foo.
>> for instance, one name/concept that I would like to use for any
>> type is destroy. All function destroy have the same signature.
>>
>> So the generic interface for destroy could be:
[...]
> If we think in terms of sqrt we don't want function overloading
> with zero or more than one argument.
> In this aspect _Generic has advantages over function overloading
> where anything is possible.
>
>
>

tgmath.h ...

Tim Rentsch

unread,
Nov 28, 2019, 7:35:55 PM11/28/19
to
Ian Collins <ian-...@hotmail.com> writes:

> On 09/11/2019 18:03, Tim Rentsch wrote:
>
>> Ian Collins <ian-...@hotmail.com> writes:
>>
>>> On 06/11/2019 01:57, Thiago Adams wrote:
>>>
>>>> C is dangerous but it is simple. When you know the tool you
>>>> know exactly what the compiler is doing and you know from a long
>>>> time what is important to do and check. How to deal with the
>>>> details.
>>>>
>>>> C++ is complex and nobody knows happens under the hood. This is why
>>>> site that shows the compiler output are so useful nowadays for C++
>>>> community.
>>>>
>>>> C is not perfect and C++ is not so bad. Personally I wish I could
>>>> select some features from both and add some extra ones.
>>>> This is why I created my own C front end.
>>>
>>> The complexity in a programming problem is a constant. You can solve
>>> it in the C way with a large number of simple steps, which introduces
>>> extra complexity in making sure they all work together. You can solve
>>> it with fewer step in a higher level language, which introduces extra
>>> complexity to the language. The overall complexity remains constant.
>>
>> Mother Goose comes to comp.lang.c.
>
> I assume by your recent tone that you have been smoking something...

Ahh, and here we have another ad hominem remark. When one
doesn't have either the facts or the law on their side, all
that's left is attacking the character of one's opponent.

> Take a concrete example: resource management.
>
> In C resource management is transparent but manual, the programmer has
> to track what has been allocated and know when to free it. This,
> along with the opportunity for error, gets quite complex when a
> resource is shared, especially between threads.
>
> In C++, adding some extra complexity in the language (constructors and
> automatic object destruction) removes the complexity and the
> opportunities for error from the user code.
>
> The code is still doing the same work and may well compile down to
> equivalent assembler. The overall complexity remains constant.

Two problems: this assumes complexity is a scalar, which it
isn't, and that it is simply additive, which it also isn't.

Furthermore it ignores the cost of all the other complexity in
C++ that must be paid whether the program needs it or not. Very
few programs, if any, need all of the complex features that C++
provides. I think it was Tony Hoare who pointed out that a
programming language shouldn't the union of all features needed
by any program, but rather the intersection of features needed
in every program. Otherwise essentially every program is paying
a complexity cost for some unneeded features.

David Brown

unread,
Nov 29, 2019, 6:41:29 AM11/29/19
to
Again - do not complain when people respond to your ad hominem remarks
with ad hominem remarks. If you want people to respond politely, be
polite yourself. (I won't be repeating this again - either you
understand, or you do not.)

>
>> Take a concrete example: resource management.
>>
>> In C resource management is transparent but manual, the programmer has
>> to track what has been allocated and know when to free it. This,
>> along with the opportunity for error, gets quite complex when a
>> resource is shared, especially between threads.
>>
>> In C++, adding some extra complexity in the language (constructors and
>> automatic object destruction) removes the complexity and the
>> opportunities for error from the user code.
>>
>> The code is still doing the same work and may well compile down to
>> equivalent assembler. The overall complexity remains constant.
>
> Two problems: this assumes complexity is a scalar, which it
> isn't, and that it is simply additive, which it also isn't.
>

Complexity is not a simple or well-defined concept. It is unreasonable
to suggest that it is exactly "constant", or to suggest it is or is not
additive, without first defining it rigorously and finding a way to
quantify it.

However, it /is/ reasonable to say it can be moved around a bit,
increased and decreased, split up, and handled in different ways. It
that may increase or decrease the total complexity (however that may be
defined), but at least it will give you modular parts where each has
clearly lower complexity.

> Furthermore it ignores the cost of all the other complexity in
> C++ that must be paid whether the program needs it or not. Very
> few programs, if any, need all of the complex features that C++
> provides.

Very few, if any, /use/ the complex features of C++.

The C++ language is designed to be "zero extra cost" - if you don't need
a feature, you should not pay for it either in terms of understanding
the language, writing the source code, or at run-time size and speed.
It does not met that aim, but it comes reasonably close in many ways.

Thus you can make classes, use RAII for resource control, have
unique_ptr's for memory handling, store data in std::vectors, work with
std::string - all without ever writing or seeing the word "template", or
understanding rvalue references, or getting your head around
"enable_if". Many of C++'s more complex features exist primarily so
that the standard library (and other libraries) can be written, so that
application code writers can get efficient code without having to
understand these complexities.

It doesn't always work out, of course, but that is an aim.

> I think it was Tony Hoare who pointed out that a
> programming language shouldn't the union of all features needed
> by any program, but rather the intersection of features needed
> in every program. Otherwise essentially every program is paying
> a complexity cost for some unneeded features.
>

It was indeed Tony Hoare who said that.

And if you look at the progression of the C++ language, rarely is
something added to the core language if it could alternatively be put in
the library. Core language changes are added if they significantly ease
programming, or if they allow more useful (or more efficient) library code.

As an example, C99 added support for complex numbers to the language.
It is part of the language, whether you use them or not - even though
only a very tiny percentage of programs use them. C++ did not put them
in the language - they put them in a library.

But you can't take features out of a language and retain backwards
compatibility. C and C++ both suffer from that - and users pay the
costs as well as benefiting.

In every programming language, except perhaps Bart's personal language
for his personal use, every programmer and every program pays something
in complexity costs for features they don't need.

Thiago Adams

unread,
Nov 29, 2019, 7:09:10 AM11/29/19
to
On Friday, November 29, 2019 at 8:41:29 AM UTC-3, David Brown wrote:
...
> Thus you can make classes, use RAII for resource control, have
> unique_ptr's for memory handling, store data in std::vectors, work with
> std::string - all without ever writing or seeing the word "template", or
> understanding rvalue references, or getting your head around
> "enable_if". Many of C++'s more complex features exist primarily so
> that the standard library (and other libraries) can be written, so that
> application code writers can get efficient code without having to
> understand these complexities.

Recently, a beginner colleague of mine call me saying:

"Please look at this error..."

The error was really big.

I don't remember the details, but we had an object used included
in some container.

Like this:

class X {
int i;
};


int main() {
std::vector<X> v;
X x;
v.push_back(x);
}

and he was adding a new member at X.

class Y {int i;};

class X {
unique_ptr<Y> sp; // <- Like this
int i;
};

Then the program stop to compile.
If you put this example online (which is much smaller than the real
case) we have these errors:

In file included from /usr/include/x86_64-linux-gnu/c++/6/bits/c++allocator.h:33:0,
from /usr/include/c++/6/bits/allocator.h:46,
from /usr/include/c++/6/string:41,
from /usr/include/c++/6/bits/locale_classes.h:40,
from /usr/include/c++/6/bits/ios_base.h:41,
from /usr/include/c++/6/ios:42,
from /usr/include/c++/6/ostream:38,
from /usr/include/c++/6/iostream:39,
from main.cpp:9:
/usr/include/c++/6/ext/new_allocator.h: In instantiation of ‘void __gnu_cxx::new_allocator<_Tp>::construct(_Up*, _Args&& ...) [with _Up = X; _Args = {const X&}; _Tp = X]’:
/usr/include/c++/6/bits/alloc_traits.h:475:4: required from ‘static void std::allocator_traits<std::allocator<_CharT> >::construct(std::allocator_traits<std::allocator<_CharT> >::allocator_type&, _Up*, _Args&& ...) [with _Up = X; _Args = {const X&}; _Tp = X; std::allocator_traits<std::allocator<_CharT> >::allocator_type = std::allocator<X>]’
/usr/include/c++/6/bits/stl_vector.h:918:30: required from ‘void std::vector<_Tp, _Alloc>::push_back(const value_type&) [with _Tp = X; _Alloc = std::allocator<X>; std::vector<_Tp, _Alloc>::value_type = X]’
<span class="error_line" onclick="ide.gotoLine('main.cpp',28)">main.cpp:28:18</span>: required from here
/usr/include/c++/6/ext/new_allocator.h:120:4: error: use of deleted function ‘X::X(const X&)’
{ ::new((void *)__p) _Up(std::forward<_Args>(__args)...); }
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
main.cpp:17:7: note: ‘X::X(const X&)’ is implicitly deleted because the default definition would be ill-formed:
class X
^
main.cpp:17:7: error: use of deleted function ‘std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = Y; _Dp = std::default_delete]’
In file included from /usr/include/c++/6/memory:81:0,
from main.cpp:11:
/usr/include/c++/6/bits/unique_ptr.h:361:7: note: declared here
unique_ptr(const unique_ptr&) = delete;
^~~~~~~~~~



As we can see, just adding a unique_ptr give us an error that requires
a lot of knowledge about the language to fix it, or at least, understand
why it is not working.
He was not trying to use advanced c++ features.



Bart

unread,
Nov 29, 2019, 8:02:23 AM11/29/19
to
....

> As we can see, just adding a unique_ptr give us an error that requires
> a lot of knowledge about the language to fix it, or at least, understand
> why it is not working.
> He was not trying to use advanced c++ features.

This is a point I sometimes make.

Because these features are implemented in user-code (ie. code written in
C++ rather then being part of the language proper), then error messages
tend to be in terms of violations that are invoked within those large
libraries.

And those are going to be meaningless to someone who only knows about
how to use a feature, not how it's implemented.

Perhaps what is needed is yet another set of features that ring-fence
such libraries, which trap any compiler errors, and turn them into a set
of friendlier, more relevant messages.

Or perhaps not...

(I'm actually working on something related, implementing features
equivalent to std::vector and std::string, but built-in. Those
implementations are also largely aided by libraries written in the same
language. But the libraries are pre-compiled and pre-tested.

No compiler error that arises out of the misuse of 'vector' or 'string'
will refer to its implementation code, but only directly in terms of how
they ought to be used.

Now, here, I don't use techniques such as templates which require all
that implementation code to be recompiled, with user-types plugged in,
and where any errors will expose matters that should not anyway be public.

This is what needs to be addressed, and not by even more layers of
complexity either.)

David Brown

unread,
Nov 29, 2019, 8:31:16 AM11/29/19
to
Error messages from code that uses templates can be a challenge, that is
unfortunately true. Compilers are getting better at this (gcc's
messages improved a lot after people pointed out that clang did a much
better job). But there is a lot of scope for improvement.

The conflict here is that having a "unique_ptr" means you can't copy the
object - the pointer is unique. And "push_back" puts a copy of the
object in the vector. The error message "note: ‘X::X(const X&)’ is
implicitly deleted because the default definition would be ill-formed"
tells you this.

But it would be nice if the error messages could omit all the parts
about allocators. When you use a template that has default parameters,
and leave these parameters unchanged, there is no benefit in mentioning
them in the messages.

I have filed a gcc bug report asking for improved messages here. I
don't know if it is realistic or not, but filing a report is the way to
find out.

<https://gcc.gnu.org/bugzilla/show_bug.cgi?id=92727>

Thiago Adams

unread,
Nov 29, 2019, 9:43:45 AM11/29/19
to
Exactly.

> Perhaps what is needed is yet another set of features that ring-fence
> such libraries, which trap any compiler errors, and turn them into a set
> of friendlier, more relevant messages.
>
> Or perhaps not...
> (I'm actually working on something related, implementing features
> equivalent to std::vector and std::string, but built-in. Those
> implementations are also largely aided by libraries written in the same
> language. But the libraries are pre-compiled and pre-tested.
>
> No compiler error that arises out of the misuse of 'vector' or 'string'
> will refer to its implementation code, but only directly in terms of how
> they ought to be used.
>
> Now, here, I don't use techniques such as templates which require all
> that implementation code to be recompiled, with user-types plugged in,
> and where any errors will expose matters that should not anyway be public.
>
> This is what needs to be addressed, and not by even more layers of
> complexity either.)

Are you trying to add string as extension to C or in our other language?

string type exists in every high level language - there is nothing
to invent here just copy Swift, Kotlin etc..

c string, or a sequence of char* terminated with 0, is part of
the C language and it is compatible with string literals.


On the other hand, C++ tried to expand the type system by library.
It didn't changed the string literal like other high languages did.

When I use C++ I don't consider std::string or smart pointer as a type
that represents strings or pointers, instead I consider both as
container of 1 element - This is what they really are.

In C++, std::string, vector, and smart pointers are trying to mimic
original types overloading operators but they are never 100% compatible
with the original types.










Bart

unread,
Nov 29, 2019, 12:32:22 PM11/29/19
to
On 29/11/2019 14:43, Thiago Adams wrote:
> On Friday, November 29, 2019 at 10:02:23 AM UTC-3, Bart wrote:

>> (I'm actually working on something related, implementing features
>> equivalent to std::vector and std::string, but built-in. Those
>> implementations are also largely aided by libraries written in the same
>> language. But the libraries are pre-compiled and pre-tested.
>>
>> No compiler error that arises out of the misuse of 'vector' or 'string'
>> will refer to its implementation code, but only directly in terms of how
>> they ought to be used.
>>
>> Now, here, I don't use techniques such as templates which require all
>> that implementation code to be recompiled, with user-types plugged in,
>> and where any errors will expose matters that should not anyway be public.
>>
>> This is what needs to be addressed, and not by even more layers of
>> complexity either.)
>
> Are you trying to add string as extension to C or in our other language?

In the non-C one.


> string type exists in every high level language - there is nothing
> to invent here just copy Swift, Kotlin etc..

Adding high-level string types to a low-level language is tricky.

I did manage it earlier this year, but abandoned it as the approach was
too complex to scale to other high level types, which had extra problems.

Now I'm trying to introduce a single dynamic type, one that provides not
only the same high-level facilities for strings, but also for lists
(mixed types), arrays (same types), dicts, sets, big-nums and so on.

The implementation won't be fast, but it will be a starting point, and
will make it easy to code things in the low-level language when
performance doesn't matter (UI and interactive code for example, or for
scripting), without having to use a separate language.

Chris M. Thomasson

unread,
Nov 29, 2019, 5:35:53 PM11/29/19
to
On 11/6/2019 1:45 PM, David Brown wrote:
> On 06/11/2019 15:26, Bart wrote:
>> On 06/11/2019 13:56, Thiago Adams wrote:
>>> On Wednesday, November 6, 2019 at 10:33:11 AM UTC-3, Thiago Adams wrote:
[...]
Indeed, not perfect but can be made to work. Fwiw, here is a hack that I
created in the past:

https://pastebin.com/raw/f52a443b1
_______________________________________
/* Interfaces
____________________________________________________________________*/
#include <stddef.h>


struct object_prv_vtable {
int (*fp_destroy) (void* const);
};


struct device_prv_vtable {
int (*fp_read) (void* const, void*, size_t);
int (*fp_write) (void* const, void const*, size_t);
};


struct device_vtable {
struct object_prv_vtable const object;
struct device_prv_vtable const device;
};


struct device {
struct device_vtable const* vtable;
};


#define object_destroy(mp_self) ( \
(mp_self)->vtable->object.fp_destroy((mp_self)) \
)


#define device_read(mp_self, mp_buf, mp_size) ( \
(mp_self)->vtable->device.fp_read((mp_self), (mp_buf), (mp_size)) \
)


#define device_write(mp_self, mp_buf, mp_size) ( \
(mp_self)->vtable->device.fp_write((mp_self), (mp_buf), (mp_size)) \
)






/* Sample Header (usb_drive.h)
____________________________________________________________________*/
#if ! defined(USB_HEADER_H)
#define USB_HEADER_H


extern int usb_drive_create(struct device** const);


#endif







/* Sample Impl (usb_drive.c)
____________________________________________________________________*/
/* #include "usb_drive.c" */
#include <stdio.h>
#include <stdlib.h>


struct usb_drive {
struct device device;
/* whatever */
};


static int usb_drive_object_destroy(void* const);
static int usb_drive_device_read(void* const, void*, size_t);
static int usb_drive_device_write(void* const, void const*, size_t);


static struct device_vtable const g_table = {
{ /* object */
usb_drive_object_destroy
},

{ /* device */
usb_drive_device_read,
usb_drive_device_write
}
};


int usb_drive_create(
struct device** const pself
) {
struct usb_drive* const self = malloc(sizeof(*self));
if (self) {
self->device.vtable = &g_table;
*pself = &self->device;
return 0;
}
return -1;
}


int usb_drive_object_destroy(
void* const self_
) {
struct usb_drive* const self = self_;
printf("usb_drive_object_destroy(%p)\n", (void*)self);
free(self_);
return 0;
}


int usb_drive_device_read(
void* const self_,
void* buf,
size_t size
) {
struct usb_drive* const self = self_;
printf("usb_drive_device_read(%p, %p, %lu)\n",
(void*)self, buf, (unsigned long)size);
return 0;
}


int usb_drive_device_write(
void* const self_,
void const* buf,
size_t size
) {
struct usb_drive* const self = self_;
printf("usb_drive_device_write(%p, %p, %lu)\n",
(void*)self, buf, (unsigned long)size);
return 0;
}







/* Sample Application
____________________________________________________________________*/
void read_write(
struct device* const self
) {
char buf[100];

device_read(self, buf, 50);

device_write(self, buf, 5);
}


int main(void) {
struct device* a_device;

if (! usb_drive_create(&a_device)) {
read_write(a_device);

object_destroy(a_device);
}

return 0;
}
_______________________________________



Ben Bacarisse

unread,
Nov 29, 2019, 6:23:31 PM11/29/19
to
"Chris M. Thomasson" <chris.m.t...@gmail.com> writes:
<cut>
> struct object_prv_vtable {
> int (*fp_destroy) (void* const);
> };
>
>
> struct device_prv_vtable {
> int (*fp_read) (void* const, void*, size_t);
> int (*fp_write) (void* const, void const*, size_t);
> };

Why so many different uses of const?

In a function prototype (where there is no function body), qualifying a
parameter as const is just noise. The caller knows the function can't
modify any object in the calling context using that parameter since
that's just how call by value works.

Qualifying a pointer /target/ is useful in all cases where it can be
done. It tells the reader something useful about the function, and it
allows the compiler to check it.

<cut>
--
Ben.

Chris M. Thomasson

unread,
Nov 29, 2019, 10:08:39 PM11/29/19
to
On 11/29/2019 3:23 PM, Ben Bacarisse wrote:
> "Chris M. Thomasson" <chris.m.t...@gmail.com> writes:
> <cut>
>> struct object_prv_vtable {
>> int (*fp_destroy) (void* const);
>> };
>>
>>
>> struct device_prv_vtable {
>> int (*fp_read) (void* const, void*, size_t);
>> int (*fp_write) (void* const, void const*, size_t);
>> };
>
> Why so many different uses of const?

The (void* const) wrt the first parameter is meant to sort of act like
the this parameter in C++. It is a constant pointer to a void. The other
const wrt the second parameter in

device_prv_vtable::fp_write

means that this is a pointer to a const void, the buffer shall not be
altered wrt writing the device.

I do it out of a habit. The first parameter points to the self. It is a
constant pointer to the object such that self cannot be altered without
some sort of alarm wrt the compiler. self is frozen, like this.

Chris M. Thomasson

unread,
Nov 29, 2019, 10:12:22 PM11/29/19
to
On 11/29/2019 3:23 PM, Ben Bacarisse wrote:
> "Chris M. Thomasson" <chris.m.t...@gmail.com> writes:
> <cut>
>> struct object_prv_vtable {
>> int (*fp_destroy) (void* const);
>> };
>>
>>
>> struct device_prv_vtable {
>> int (*fp_read) (void* const, void*, size_t);
>> int (*fp_write) (void* const, void const*, size_t);
>> };
>
> Why so many different uses of const?

In a sense, I wanted to sort of define the API through its prototype.
The first parameter is self, is shall not be able to be changed. Reflect
this through saying that said parameter self is void* const. Its a
habit. The buffer being fed into the write shall not be altered,
therefore it is a void const*.

Ben Bacarisse

unread,
Nov 30, 2019, 5:44:37 AM11/30/19
to
"Chris M. Thomasson" <chris.m.t...@gmail.com> writes:

> On 11/29/2019 3:23 PM, Ben Bacarisse wrote:
>> "Chris M. Thomasson" <chris.m.t...@gmail.com> writes:
>> <cut>
>>> struct object_prv_vtable {
>>> int (*fp_destroy) (void* const);
>>> };
>>>
>>>
>>> struct device_prv_vtable {
>>> int (*fp_read) (void* const, void*, size_t);
>>> int (*fp_write) (void* const, void const*, size_t);
>>> };
>>
>> Why so many different uses of const?
>
> In a sense, I wanted to sort of define the API through its
> prototype. The first parameter is self, is shall not be able to be
> changed. Reflect this through saying that said parameter self is void*
> const. Its a habit.

There's nothing wrong with it, it's just odd. A const qualified
parameter in a prototype conveys no technical information as the
parameter may or may not be const qualified in the definition. I was
just curious to know what you wanted to convey with it.

--
Ben.

Öö Tiib

unread,
Nov 30, 2019, 4:16:42 PM11/30/19
to
Makes sense, std::unique_ptr is not copyable (but is movable)
and so a struct containing it can't be copied (but can be
moved) into vector.
Demo: <http://coliru.stacked-crooked.com/a/14ad05993d274ead>
Other option is to add a copy constructor but then also rest
of The Five (plus default constructor for X x;) need to be
added manually to X.

> If you put this example online (which is much smaller than the real
> case) we have these errors:

True. What you suggest how these errors should be laid out?

I would prefer:
Who said that you can push_back non-copyable stuff into
std::vector by value, you morons, like that "v.push_back(x);"?

May be in few years. I would especially like ad hominems and
insults in diagnostics to get into correct mood of using the tool.

> As we can see, just adding a unique_ptr give us an error that requires
> a lot of knowledge about the language to fix it, or at least, understand
> why it is not working.
> He was not trying to use advanced c++ features.

Yes, C++ is decent tool but no way nice, simple or forgiving to use.
Who wants nice tool should perhaps take Rust, Swift or Kotlin.
Trouble is that Rust is in kindergarten, Swift is fruit-specific
and Kotlin runs on Oracle's runtime but may be in few years. ;)

Ian Collins

unread,
Nov 30, 2019, 6:10:30 PM11/30/19
to
Pot, kettle?

>> Take a concrete example: resource management.
>>
>> In C resource management is transparent but manual, the programmer has
>> to track what has been allocated and know when to free it. This,
>> along with the opportunity for error, gets quite complex when a
>> resource is shared, especially between threads.
>>
>> In C++, adding some extra complexity in the language (constructors and
>> automatic object destruction) removes the complexity and the
>> opportunities for error from the user code.
>>
>> The code is still doing the same work and may well compile down to
>> equivalent assembler. The overall complexity remains constant.
>
> Two problems: this assumes complexity is a scalar, which it
> isn't, and that it is simply additive, which it also isn't.

In the example I mentioned, the complexity could be a measure of the
generated object code required to solve the problem. If C and C++
generate the same or very similar object code, it's fair to say the code
is doing the same work and to compare the complexity of (and
opportunities for error in) the source code.

> Furthermore it ignores the cost of all the other complexity in
> C++ that must be paid whether the program needs it or not.

C++ has always had the philosophy of if you don't need it, you don't pay
for it.

> Very few programs, if any, need all of the complex features that C++
> provides.

And very few, if any, use all of the complex features that C++ provides.

> I think it was Tony Hoare who pointed out that a
> programming language shouldn't the union of all features needed
> by any program, but rather the intersection of features needed
> in every program. Otherwise essentially every program is paying
> a complexity cost for some unneeded features.

David has already said all I would have said in replay to this.

--
Ian.

Thiago Adams

unread,
Dec 2, 2019, 6:32:06 AM12/2/19
to
When push_back was written the copy ctor was ok. The programmer wasn't
a moron.

When a new feature was required, the code needed some changes.
The programmer that is making the change is also correct
because in this case, unique_ptr was a good choice of use.




Öö Tiib

unread,
Dec 3, 2019, 2:30:28 AM12/3/19
to
It does not matter. From C++ compiler I would like patronizing, mocking
and unjustly insulting tone of diagnostics to get into correct mood
of using the very programming language. It is pretty good programming
language but it is designed to be very novice-unfriendly. So such
disrespectful attitude would fit well into that situation and even
perhaps reduce confusion.

> When a new feature was required, the code needed some changes.
> The programmer that is making the change is also correct
> because in this case, unique_ptr was a good choice of use.

Yes, unique_ptr is great. I replaced all boost::scoped_ptr and
several boost::shared_ptr with it ASAP when it come out. A raw
pointer at that place would cause defective program to compile.
The unique_ptr does not let it to happen but before one does
not study the nuances of its usage it will be just a pain of
different nature.

Tim Rentsch

unread,
Jan 11, 2020, 12:36:04 PM1/11/20
to
None of my comments here have been ad hominem remarks. I have said
things about what you have said, but nothing of what I have said
has been about you as opposed to what you have said.

>>> Take a concrete example: resource management.
>>>
>>> In C resource management is transparent but manual, the programmer has
>>> to track what has been allocated and know when to free it. This,
>>> along with the opportunity for error, gets quite complex when a
>>> resource is shared, especially between threads.
>>>
>>> In C++, adding some extra complexity in the language (constructors and
>>> automatic object destruction) removes the complexity and the
>>> opportunities for error from the user code.
>>>
>>> The code is still doing the same work and may well compile down to
>>> equivalent assembler. The overall complexity remains constant.
>>
>> Two problems: this assumes complexity is a scalar, which it
>> isn't, and that it is simply additive, which it also isn't.
>
> In the example I mentioned, the complexity could be a measure of the
> generated object code required to solve the problem. If C and C++
> generate the same or very similar object code, it's fair to say the
> code is doing the same work and to compare the complexity of (and
> opportunities for error in) the source code.

If you think the complexity of a programming _problem_ is
constant, then measuring generated object code cannot be a
valid complexity metric.


>> Furthermore it ignores the cost of all the other complexity in
>> C++ that must be paid whether the program needs it or not.
>
> C++ has always had the philosophy of if you don't need it, you don't
> pay for it.

Having a philosophy doesn't mean it has been achieved. The idea
that you don't need to know about language features you don't use
has long been debunked.

>> Very few programs, if any, need all of the complex features that C++
>> provides.
>
> And very few, if any, use all of the complex features that C++ provides.

See above.

>> I think it was Tony Hoare who pointed out that a
>> programming language shouldn't the union of all features needed
>> by any program, but rather the intersection of features needed
>> in every program. Otherwise essentially every program is paying
>> a complexity cost for some unneeded features.
>
> David has already said all I would have said in replay to this.

If you mean David Brown, I didn't see his comments.
0 new messages