Trying to come up to speed on C++ concepts. If they're are useful
why wouldn't they define named requirements as a concept, e.g.
Am 22.09.2024 um 10:06 schrieb [email protected]:
On Sat, 21 Sep 2024 15:43:19 -0400
jseigh <[email protected]> boringly babbled:
Trying to come up to speed on C++ concepts. If they're are useful
why wouldn't they define named requirements as a concept, e.g.
The signal to noise ratio of useful vs pointless features in recent versions >> of C++ is not very good. All they're achieving is making the language so
bloody complicated that new devs take one look at irt and flee for the hills.
Concepts are a very easy to understand feature of C++20.
Am 22.09.2024 um 10:59 schrieb [email protected]:
On Sun, 22 Sep 2024 10:45:01 +0200
Bonita Montero <[email protected]> boringly babbled:
Am 22.09.2024 um 10:06 schrieb [email protected]:
On Sat, 21 Sep 2024 15:43:19 -0400
jseigh <[email protected]> boringly babbled:
Trying to come up to speed on C++ concepts. If they're are useful
why wouldn't they define named requirements as a concept, e.g.
The signal to noise ratio of useful vs pointless features in recent >versions
of C++ is not very good. All they're achieving is making the language so >>>> bloody complicated that new devs take one look at irt and flee for the >hills.
Concepts are a very easy to understand feature of C++20.
They're also yet another solution looking for a problem that add another
dollop of slop to the syntatic and semantic soup that is modern C++.
Without concepts you might have complex errors from inside of a generic >functions. With human readable concept errors which all current C++20 >-compilers support you get easier to read errors which show you which >requirement a generic type missed. That's a real advance over C++17.
Trying to come up to speed on C++ concepts. If they're are useful
why wouldn't they define named requirements as a concept, e.g.
template<typename T>
concept BasicLockable = requires(T a)
{
{ a.lock() } -> std::same_as<void>;
{ a.unlock() } -> std::same_as<void>;
};
template<BasicLockable T>
...
Also if I define some concept for use in function templates,
C++ doesn't like using primative types, just classes.
Regular templates don't have the problem but they
just seem to be glorified macros.
On Sat, 21 Sep 2024 15:43:19 -0400
jseigh <[email protected]> boringly babbled:
Trying to come up to speed on C++ concepts. If they're are useful
why wouldn't they define named requirements as a concept, e.g.
The signal to noise ratio of useful vs pointless features in recent versions of C++ is not very good. All they're achieving is making the language so bloody complicated that new devs take one look at irt and flee for the hills. There's a reason Python - and to a lesser extent these days - Java are used for teaching in univerisities now, not C++. And if you don't get them young you can pretty much forget about getting many of the when they're older.
Am 22.09.2024 um 11:08 schrieb [email protected]:
On Sun, 22 Sep 2024 11:03:18 +0200
Without concepts you might have complex errors from inside of a generic
functions. With human readable concept errors which all current C++20
-compilers support you get easier to read errors which show you which
requirement a generic type missed. That's a real advance over C++17.
Generating appropriate readable errors is a function of the compiler, not
the language. ...
It's part of the language and therefore there are concepts.
incomprehensible gibberish just because of some minor templating error was a >> good idea beats me.
Chose Java if you're overburdened with C++.
On 9/22/24 04:06, [email protected] wrote:
On Sat, 21 Sep 2024 15:43:19 -0400
jseigh <[email protected]> boringly babbled:
Trying to come up to speed on C++ concepts. If they're are useful
why wouldn't they define named requirements as a concept, e.g.
The signal to noise ratio of useful vs pointless features in recent versions >> of C++ is not very good. All they're achieving is making the language so
bloody complicated that new devs take one look at irt and flee for the hills.
There's a reason Python - and to a lesser extent these days - Java are used >> for teaching in univerisities now, not C++. And if you don't get them young >> you can pretty much forget about getting many of the when they're older.Actually I do use Java. It has an insane number of libraries for just
about anything you can think of. And the jit makes it fast enough
for most purposes. Probably faster since it dynamically optimizes
code paths. You have to remember to "bake in" a java program before
you do performance measurements.
Also if I define some concept for use in function templates,
C++ doesn't like using primative types, just classes.
On 09/21/24 12:43 PM, jseigh wrote:
Also if I define some concept for use in function templates,
C++ doesn't like using primative types, just classes.
Er... Can you provide an example?
On 9/22/24 14:43, Andrey Tarasevich wrote:
On 09/21/24 12:43 PM, jseigh wrote:#include <concepts>
Also if I define some concept for use in function templates,
C++ doesn't like using primative types, just classes.
Er... Can you provide an example?
template<typename T>
concept GetRef = requires(T a)
{
{ getref(a)} -> std::same_as<void *>;
};
class test_t {};
static void * getref(int x) { return 0; }
static void * getref(test_t x) { return 0; }
template<GetRef X>
static void getref_concept(X z) {
getref(z);
};
template<typename T>
static void getref_template(T t)
{
getref(t);
}
int main()
{
test_t x = {};
getref_concept(x);
getref_concept(4);
// zz(4);
getref_template(x);
getref_template(4);
return 0;
}
$ g++ -C -std=c++20 -O3 -fconcepts-diagnostics-depth=4 ctest2.cpp
ctest2.cpp: In function ‘int main()’:
ctest2.cpp:31:19: error: no matching function for call to ‘getref_concept(int)’
31 | getref_concept(4);
| ~~~~~~~~~~~~~~^~~
ctest2.cpp:16:13: note: candidate: ‘template<class X> requires
GetRef<X> void getref_concept(X)’
16 | static void getref_concept(X z) {
| ^~~~~~~~~~~~~~
ctest2.cpp:16:13: note: template argument deduction/substitution failed: ctest2.cpp:16:13: note: constraints not satisfied
ctest2.cpp: In substitution of ‘template<class X> requires GetRef<X> void getref_concept(X) [with X = int]’:
ctest2.cpp:31:19: required from here
ctest2.cpp:4:9: required for the satisfaction of ‘GetRef<X>’ [with X =
int]
ctest2.cpp:4:18: in requirements with ‘T a’ [with T = int] ctest2.cpp:6:13: note: the required expression ‘getref(a)’ is invalid, because
6 | { getref(a)} -> std::same_as<void *>;
| ~~~~~~^~~
ctest2.cpp:6:13: error: ‘getref’ was not declared in this scope, and no declarations were found by argument-dependent lookup at the point of instantiation [-fpermissive]
ctest2.cpp:13:15: note: ‘void* getref(test_t)’ declared here, later in the translation unit
13 | static void * getref(test_t x) { return 0; }
| ^~~~~~
No idea except maybe concepts don't like primitives as types.
On Sat, 21 Sep 2024 15:43:19 -0400
jseigh <[email protected]> boringly babbled:
Trying to come up to speed on C++ concepts. If they're are useful
why wouldn't they define named requirements as a concept, e.g.
Am 22.09.2024 um 17:21 schrieb [email protected]:
The main issue with Java IME was memory hogging and the occasional hang while
the garnage collector did its thing. ...
The incremental garbage collector is there for about 20 years. Memory
hogging isn't a problem for what Java is used. It's used in places
where otherwise you'd use scripting languages for the server backend.
Am 22.09.2024 um 17:15 schrieb [email protected]:
Congrats on not understanding what I was saying. It wasn't that complicated.
If you have requirements on a generic type and they're not fulfilled
concepts are the only way to handle this conveniently in C++.
Its too late in my career to change now as I don't fancy starting at the
bottom again, but if I was just starting out on a systems programming career >> right now I'd be learning Rust.
Rust will be popular in 10 - 20 years.
Am 23.09.2024 um 09:39 schrieb [email protected]:
Face <- palm.
Seems you don't undestand concepts.
Am 23.09.2024 um 09:40 schrieb [email protected]:
On Sun, 22 Sep 2024 17:34:29 +0200
Bonita Montero <[email protected]> boringly babbled:
Am 22.09.2024 um 17:21 schrieb [email protected]:
The main issue with Java IME was memory hogging and the occasional hang >while
the garnage collector did its thing. ...
The incremental garbage collector is there for about 20 years. Memory
hogging isn't a problem for what Java is used. It's used in places
where otherwise you'd use scripting languages for the server backend.
Saying something is less worse than the alternatives is not a good argument. >>
Java is good enough for what it's used for.
Memory is extremely cheap so that no one cares for what you say.
Am 23.09.2024 um 11:32 schrieb [email protected]:
On Mon, 23 Sep 2024 10:54:44 +0200
Bonita Montero <[email protected]> boringly babbled:
Am 23.09.2024 um 09:39 schrieb [email protected]:
Face <- palm.
Seems you don't undestand concepts.
And you don't understand english well enough to follow a thread which is
fair enough, its probably not your first language.
Without concepts you've to deal with the implementation
of code where you like to deal only with the interface.
Am 23.09.2024 um 11:33 schrieb [email protected]:
On Mon, 23 Sep 2024 10:55:33 +0200
Java is good enough for what it's used for.
Memory is extremely cheap so that no one cares for what you say.
Spoken like a true windows dev who thinks every system is a windows PC thats >> only ever running 1 app at a time.
Java-developers would agree with me, no matter if they develop
under Windows or Linux.
Am 23.09.2024 um 15:56 schrieb [email protected]:
On Mon, 23 Sep 2024 11:45:15 +0200
Bonita Montero <[email protected]> boringly babbled:
Am 23.09.2024 um 11:32 schrieb [email protected]:
On Mon, 23 Sep 2024 10:54:44 +0200
Bonita Montero <[email protected]> boringly babbled:
Am 23.09.2024 um 09:39 schrieb [email protected]:
Face <- palm.
Seems you don't undestand concepts.
And you don't understand english well enough to follow a thread which is >>>> fair enough, its probably not your first language.
Without concepts you've to deal with the implementation
of code where you like to deal only with the interface.
Stop digging.
C++ is a programming language for people who work with it continuously, >rather than occasionally.
Am 23.09.2024 um 15:59 schrieb [email protected]:
Their opinion is irrelevant as they don't know any better. ..
They make the rules if they decide to program with Java, not you.
Memory and CPU cycles costs money whether its your own system,
a hosted blade or an AWS EC2 instance so a java app cannot assume
its got the machine to itself because these days thats very much
not the case.
Java is the standard for the server-backend.
On 09/22/24 2:00 PM, jseigh wrote:
On 9/22/24 14:43, Andrey Tarasevich wrote:
On 09/21/24 12:43 PM, jseigh wrote:#include <concepts>
Also if I define some concept for use in function templates,
C++ doesn't like using primative types, just classes.
Er... Can you provide an example?
template<typename T>
concept GetRef = requires(T a)
{
{ getref(a)} -> std::same_as<void *>;
};
class test_t {};
static void * getref(int x) { return 0; }
static void * getref(test_t x) { return 0; }
template<GetRef X>
static void getref_concept(X z) {
getref(z);
};
template<typename T>
static void getref_template(T t)
{
getref(t);
}
int main()
{
test_t x = {};
getref_concept(x);
getref_concept(4);
// zz(4);
getref_template(x);
getref_template(4);
return 0;
}
$ g++ -C -std=c++20 -O3 -fconcepts-diagnostics-depth=4 ctest2.cpp
ctest2.cpp: In function ‘int main()’:
ctest2.cpp:31:19: error: no matching function for call to
‘getref_concept(int)’
31 | getref_concept(4);
| ~~~~~~~~~~~~~~^~~
ctest2.cpp:16:13: note: candidate: ‘template<class X> requires
GetRef<X> void getref_concept(X)’
16 | static void getref_concept(X z) {
| ^~~~~~~~~~~~~~
ctest2.cpp:16:13: note: template argument deduction/substitution
failed:
ctest2.cpp:16:13: note: constraints not satisfied
ctest2.cpp: In substitution of ‘template<class X> requires GetRef<X> >> void getref_concept(X) [with X = int]’:
ctest2.cpp:31:19: required from here
ctest2.cpp:4:9: required for the satisfaction of ‘GetRef<X>’ [with X >> = int]
ctest2.cpp:4:18: in requirements with ‘T a’ [with T = int]
ctest2.cpp:6:13: note: the required expression ‘getref(a)’ is invalid, >> because
6 | { getref(a)} -> std::same_as<void *>;
| ~~~~~~^~~
ctest2.cpp:6:13: error: ‘getref’ was not declared in this scope, and
no declarations were found by argument-dependent lookup at the point
of instantiation [-fpermissive]
ctest2.cpp:13:15: note: ‘void* getref(test_t)’ declared here, later in >> the translation unit
13 | static void * getref(test_t x) { return 0; }
| ^~~~~~
No idea except maybe concepts don't like primitives as types.
But this is just a two-phase name lookup problem caused by your order of declarations. It has nothing to do with concepts at all. You will get essentially the same error in the following example:
template <typename T> void foo(T t) { bar(t); }
void bar(int) {}
int main()
{
foo(42);
}
The reason this error happens is that the second-phase of template
lookup is performed in ADL-associated namespaces only. And built-in
types (like `int`) do not have associated namespaces.
For this reason, for built-in types everything you refer to from
template code has to be declared _above_ that template code.
So, in your case you have to declare your `getref` functions _above_ the concept declaration and the code will compile. At least the `int`
version of `getref` has to be declared above the concept
https://coliru.stacked-crooked.com/a/2d021dbf666f4f93
Am 22.09.2024 um 13:58 schrieb jseigh:
Actually I do use Java. It has an insane number of libraries for just
about anything you can think of. And the jit makes it fast enough
for most purposes. Probably faster since it dynamically optimizes
code paths. ...
It executes each function once interpreteted and makes then its
conclusions about the code. That's not sufficient.
In the Computer Languages Benchmark Game Java is about 50% the
Performance of C++.
I use C/C++ for different reasons. The C++ concepts look like they
could be used in some cases like Rust traits which I like better
than Java interfaces since they can be added without having to
modify the base class or add a wrapper class.
Java hasn't real gericity.
Am 22.09.2024 um 23:50 schrieb Andrey Tarasevich:
template <typename T> void foo(T t)
requires convertible_to<T, int>
{ bar(t); }
Am 23.09.2024 um 09:39 schrieb [email protected]:
Face <- palm.
Seems you don't undestand concepts.
Rust will be popular in 10 - 20 years.
Or maybe it'll fail altogether like D. However it seems a reasonable halfway >> house between C and C++ with built in memory safety and much less of the
syntatic and semantic bollocks that comes with C++.
The memory-safety can be reached while debugging through iterator-debug- >ging. I consequently use iterator debugging, f.e. through spans on raw >memory, and this way out of bounds errors are easy to find. Even for
s.o. who doesn't know the code.
Rust will be popular in 10 - 20 years.
Or maybe it'll fail altogether like D. However it seems a reasonable halfway >house between C and C++ with built in memory safety and much less of the >syntatic and semantic bollocks that comes with C++.
On 9/23/2024 7:02 AM, Bonita Montero wrote:
Am 23.09.2024 um 15:59 schrieb [email protected]:
Their opinion is irrelevant as they don't know any better. ..
They make the rules if they decide to program with Java, not you.
Memory and CPU cycles costs money whether its your own system,
a hosted blade or an AWS EC2 instance so a java app cannot assume
its got the machine to itself because these days thats very much
not the case.
Java is the standard for the server-backend.
Well, I would not use Java to implement a high performance server... :^)
On 9/23/2024 1:24 PM, jseigh wrote:
On 9/23/24 15:55, Chris M. Thomasson wrote:
Anyway, it isn't like nobody uses java for high performance
stuff. More a personal preference of the implementers.
Well, I have written some servers in the past, very early 2000's was the
last time, they were pretty good. 50,000 concurrent connections using
IOCP on windows, then aio over on the linux side. Not much experience
with AIO as IOCP.
I used C way more back then wrt C++... But, how do I access the
following API's directly in Java and not have to worry about any GC
bullshit?
https://learn.microsoft.com/en-us/windows/win32/api/ioapiset/nf-ioapiset-getqueuedcompletionstatus
Back in nt 4. Then the "newer" one, the Ex version... Ahhh windows:
https://learn.microsoft.com/en-us/windows/win32/api/ioapiset/nf-ioapiset-getqueuedcompletionstatusex
https://learn.microsoft.com/en-us/windows/win32/api/mswsock/nf-mswsock-acceptex
https://learn.microsoft.com/en-us/windows/win32/api/mswsock/nc-mswsock-lpfn_connectex
https://learn.microsoft.com/en-us/windows/win32/api/mswsock/nc-mswsock-lpfn_transmitpackets
https://learn.microsoft.com/en-us/windows/win32/api/mswsock/nf-mswsock-transmitfile
Btw, there is an interesting limitation on the transmitfile and iirc lpfn_transmitpackets functions wrt the version of windows. Server or
Client iirc. Last time I checked only two of them can be in flight at
any one time wrt overlapped io.
Anyway, it isn't like nobody uses java for high performance
stuff. More a personal preference of the implementers.
[email protected] writes:
Rust will be popular in 10 - 20 years.
Or maybe it'll fail altogether like D. However it seems a reasonable halfway >>house between C and C++ with built in memory safety and much less of the >>syntatic and semantic bollocks that comes with C++.
So far. But then C++ didn't have all the syntactic and semantic bollocks >when it was first developed; just C with classes for data encapsulation.
Am 24.09.2024 um 09:20 schrieb [email protected]:
True. One can hope that whatever team develops Rust knows when to leave well >> alone and only add important features going forward, not whatever half-arsed >> idea one of them farts out on the toilet that morning which seems to be how >C++
is "designed" these days. Would also help if they avoided idiotic syntatic >> hacks such as off the top of my head:
This is said by someone who thinks concepts, which are a rather simple >feature, are too complex.
Am 24.09.2024 um 09:23 schrieb [email protected]:
On Mon, 23 Sep 2024 16:24:58 -0400
jseigh <[email protected]> boringly babbled:
Anyway, it isn't like nobody uses java for high performance
stuff. More a personal preference of the implementers.
Anyone who wants to know how hopeless java can be for large applications
only needs to use Jira or Eclipse for 5 minutes.
I'm using IntellIJ Idea
on a AMD 7950X and it's nearly as fast as
Visual Studio.
Unfortunately the standard libary isn't concepted currently, f.e. if
you modify an element inside the function-object given to std::sort
you won't get an error. A concept could enforce that you take const
Am 24.09.2024 um 10:11 schrieb [email protected]:
So you don't believe those are hacks? Fair enough. And there's a difference >> between too complex and pointless.
Concepts aren't a hack and make APIs for those who use it simpler
Am 24.09.2024 um 12:52 schrieb [email protected]:
On Tue, 24 Sep 2024 11:46:26 +0200
Bonita Montero <[email protected]> boringly babbled:
Unfortunately the standard libary isn't concepted currently, f.e. if
you modify an element inside the function-object given to std::sort
you won't get an error. A concept could enforce that you take const
Bummer. So don't modify them inside the function object.
Yes, but this can be safely enforced.
F.e. if you wrote = instead of == inside such a lambda you get an error.
Am 24.09.2024 um 12:59 schrieb [email protected]:
Arguments to that type of function object should be const anyway ...
That's always true, mostly false.
Seems like a re-invention of the wheel in that particular example.
No, it gives more meaningful errors. That's not for me, I understand
the errors from inside a <algorithm>-function or whatever. That's for
less C++-skilled people like you.
Am 24.09.2024 um 09:23 schrieb [email protected]:
On Mon, 23 Sep 2024 16:24:58 -0400
jseigh <[email protected]> boringly babbled:
Anyway, it isn't like nobody uses java for high performance
stuff. More a personal preference of the implementers.
Anyone who wants to know how hopeless java can be for large applications
only needs to use Jira or Eclipse for 5 minutes.
I'm using IntellIJ Idea on a AMD 7950X and it's nearly as fast as
Visual Studio.
On 9/23/24 17:43, Chris M. Thomasson wrote:
On 9/23/2024 1:24 PM, jseigh wrote:
On 9/23/24 15:55, Chris M. Thomasson wrote:
Anyway, it isn't like nobody uses java for high performance
stuff. More a personal preference of the implementers.
Well, I have written some servers in the past, very early 2000's was
the last time, they were pretty good. 50,000 concurrent connections
using IOCP on windows, then aio over on the linux side. Not much
experience with AIO as IOCP.
I used C way more back then wrt C++... But, how do I access the
following API's directly in Java and not have to worry about any GC
bullshit?
https://learn.microsoft.com/en-us/windows/win32/api/ioapiset/nf-ioapiset-getqueuedcompletionstatus
Back in nt 4. Then the "newer" one, the Ex version... Ahhh windows:
https://learn.microsoft.com/en-us/windows/win32/api/ioapiset/nf-ioapiset-getqueuedcompletionstatusex
https://learn.microsoft.com/en-us/windows/win32/api/mswsock/nf-mswsock-acceptex
https://learn.microsoft.com/en-us/windows/win32/api/mswsock/nc-mswsock-lpfn_connectex
https://learn.microsoft.com/en-us/windows/win32/api/mswsock/nc-mswsock-lpfn_transmitpackets
https://learn.microsoft.com/en-us/windows/win32/api/mswsock/nf-mswsock-transmitfile
Btw, there is an interesting limitation on the transmitfile and iirc
lpfn_transmitpackets functions wrt the version of windows. Server or
Client iirc. Last time I checked only two of them can be in flight at
any one time wrt overlapped io.
Java tries to be platform agnostic so you won't see any platform
specific stuff in the standard java libraries.
The current fast io in linux is io_uring which I haven't used
yet because I don't have anything I need to use it on.
Anything i/o related will end up c code w/ native methods.
There might be 3rd party stuff that does that.
For i/o in java you can use java.nio.ByteBuffer which
has a reusable internal byte array so no gc.
java.nio.channels.SocketChannel uses ByteBuffers.
There's also MappedByteBuffer. You could probably
do some crazy stuff with that to share non java
heap memory with a non java process. I'd let some
3rd party lib work that out though.
Am 24.09.2024 um 13:07 schrieb [email protected]:
On Tue, 24 Sep 2024 13:05:10 +0200
Bonita Montero <[email protected]> boringly babbled:
Am 24.09.2024 um 12:59 schrieb [email protected]:
Arguments to that type of function object should be const anyway ...
That's always true, mostly false.
What?
In most cases references as function-parameters are non-const if
you include r-value-references (which can be const altough it doesn't
make sense).
I think you have an overrated sense of your own abilities.
No, I've worked a long time with C++ before concepts and I learned
to live with the Swahili errors from the compiler.
You may have better knowledge of the language than most but your
ability to write clear and simple code is badly lacking as you
frequently demonstrate.
Clear and simple in a sense that it's understandable for someone
having only 10 hours experience with C++ is impossible. It's a
language for people which are constantly working with.
Am 24.09.2024 um 15:51 schrieb [email protected]:
Those errors are a function of the compiler, not the language. ...
They arise from the standard.
On Tue, 24 Sep 2024 15:56:31 +0200
Bonita Montero <[email protected]> boringly babbled:
Am 24.09.2024 um 15:51 schrieb [email protected]:
Those errors are a function of the compiler, not the language. ...
They arise from the standard.
What are you talking about? The compiler writers can choose to output errors in any way and format they like.
Am 24.09.2024 um 17:43 schrieb [email protected]:
On Tue, 24 Sep 2024 15:56:31 +0200
Bonita Montero <[email protected]> boringly babbled:
Am 24.09.2024 um 15:51 schrieb [email protected]:
Those errors are a function of the compiler, not the language. ...
They arise from the standard.
What are you talking about? The compiler writers can choose to output errors >> in any way and format they like.
Warnings are compiler-specific, errors directly arise from standard >violations.
looks up. The error message is likely to be complex because the
compiler doesn't know where you made the mistake, and tries to list
Using concepts, however, means the error is found on the first attempt
to use the type "A<T>" - the compiler can give far clearer information.
It can say "On line x, you tried to make A<T>, but type T does not
fulfil the requirement you gave on line y when you defined class A".
Competition is a great thing). And you sometimes want more information, >sometimes less. But concepts give the programmer and the compiler a far >better starting point.
They also allow neater and clearer code with constrained auto
declarations, letting you specify something between "this /exact/ type"
and "whatever seems to fit".
On Wed, 25 Sep 2024 09:49:49 +0200
David Brown <[email protected]> boringly babbled:
looks up. The error message is likely to be complex because the
compiler doesn't know where you made the mistake, and tries to list
It knows what line invoked the error so all it needs to do is print that.
Using concepts, however, means the error is found on the first attempt
to use the type "A<T>" - the compiler can give far clearer information.
It can say "On line x, you tried to make A<T>, but type T does not
fulfil the requirement you gave on line y when you defined class A".
Tbh if you want to massage templating that much then don't use it in the first place, just hard code the functions.
Competition is a great thing). And you sometimes want more information,
sometimes less. But concepts give the programmer and the compiler a far
better starting point.
This is where compiler command line switches should come into play.
They also allow neater and clearer code with constrained auto
declarations, letting you specify something between "this /exact/ type"
and "whatever seems to fit".
Don't use auto in those scenarios then. Its always nicer when people put
the specific type in the code anyway.
On Wed, 25 Sep 2024 09:49:49 +0200
David Brown <[email protected]> boringly babbled:
looks up. The error message is likely to be complex because the
compiler doesn't know where you made the mistake, and tries to list
It knows what line invoked the error so all it needs to do is print
that.
Using concepts, however, means the error is found on the first
attempt to use the type "A<T>" - the compiler can give far clearer >information. It can say "On line x, you tried to make A<T>, but type
T does not fulfil the requirement you gave on line y when you
defined class A".
Tbh if you want to massage templating that much then don't use it in
the first place, just hard code the functions.
Competition is a great thing). And you sometimes want more
information, sometimes less. But concepts give the programmer and
the compiler a far better starting point.
This is where compiler command line switches should come into play.
They also allow neater and clearer code with constrained auto
declarations, letting you specify something between "this /exact/
type" and "whatever seems to fit".
Don't use auto in those scenarios then. Its always nicer when people
put the specific type in the code anyway.
Below is an example of even more likely user mistake.
gcc generated 63 lines of errors, MSVC produced 69 lines and clang
had beaten both of them with 217 lines!
Even if, by chance, one or two of the messages make sense, they are
well hidden in piles of garbage.
void my_sort3(uint8_t* buf, int buflen, const uint8_t* trans_tab)
{
struct {
const uint8_t* tab;
bool operator() (uint8_t* a, uint8_t *b) {
return tab[*a] < tab[*b];
}
} cmp;
cmp.tab = trans_tab;
std::sort(buf, buf+buflen, cmp);
}
------ Build started: Project: ConsoleTest2022, Configuration: Debugx64 ------
main.cpp*,uint8_t *)': cannot convert argument 1 from 'unsigned char' to 'uint8_t *' 1>C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.40.33807\include\algorithm(8068,16): error C2664: 'bool my_sort3::<unnamed-type-cmp>::operator ()(uint8_t
C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.40.33807\include\xutility(1501,14): error C2664: 'bool my_sort3::<unnamed-type-cmp>::operator ()(uint8_t
Done building project "ConsoleTest2022.vcxproj" -- FAILED.========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ========== ========== Build completed at 13:49 and took 00,390 seconds ==========
Below is example of very simple function with very small
template-related bug of sort that people sometimes do in real life.
I tried 3 compilers. Each one produces more than 20 lines of error
messages none of which was enlightening.
#include <cstdint>
#include <algorithm>
void my_sort2(uint8_t* buf, int buflen, const uint8_t* trans_tab)
{
struct {
const uint8_t* tab;
bool operator() (uint8_t a, uint8_t b) {
return tab[a] < tab[b];
}
} cmp;
cmp.tab = trans_tab;
std::sort(buf, buf+buflen, &cmp);
}
I don't know if with conceptized <algorithm> error reporting would
work better. But it is worth a try, because current state is not >satisfactory.
On Wed, 25 Sep 2024 08:24:19 -0000 (UTC)
[email protected] wrote:
On Wed, 25 Sep 2024 09:49:49 +0200
David Brown <[email protected]> boringly babbled:
looks up. The error message is likely to be complex because the
compiler doesn't know where you made the mistake, and tries to list
It knows what line invoked the error so all it needs to do is print
that.
Using concepts, however, means the error is found on the first
attempt to use the type "A<T>" - the compiler can give far clearer >information. It can say "On line x, you tried to make A<T>, but
type T does not fulfil the requirement you gave on line y when you >defined class A".
Tbh if you want to massage templating that much then don't use it in
the first place, just hard code the functions.
Competition is a great thing). And you sometimes want more
information, sometimes less. But concepts give the programmer and
the compiler a far better starting point.
This is where compiler command line switches should come into play.
They also allow neater and clearer code with constrained auto >declarations, letting you specify something between "this /exact/
type" and "whatever seems to fit".
Don't use auto in those scenarios then. Its always nicer when people
put the specific type in the code anyway.
Below is example of very simple function with very small
template-related bug of sort that people sometimes do in real life.
I tried 3 compilers. Each one produces more than 20 lines of error
messages none of which was enlightening.
#include <cstdint>
#include <algorithm>
void my_sort2(uint8_t* buf, int buflen, const uint8_t* trans_tab)
{
struct {
const uint8_t* tab;
bool operator() (uint8_t a, uint8_t b) {
return tab[a] < tab[b];
}
} cmp;
cmp.tab = trans_tab;
std::sort(buf, buf+buflen, &cmp);
}
I don't know if with conceptized <algorithm> error reporting would
work better. But it is worth a try, because current state is not satisfactory.
On 25.09.2024 13:31, Michael S wrote:
Below is an example of even more likely user mistake.
gcc generated 63 lines of errors, MSVC produced 69 lines and clang
had beaten both of them with 217 lines!
Even if, by chance, one or two of the messages make sense, they are
well hidden in piles of garbage.
void my_sort3(uint8_t* buf, int buflen, const uint8_t* trans_tab)
{
struct {
const uint8_t* tab;
bool operator() (uint8_t* a, uint8_t *b) {
return tab[*a] < tab[*b];
}
} cmp;
cmp.tab = trans_tab;
std::sort(buf, buf+buflen, cmp);
}
VStudio 2022 gives pretty clear error messages: cannot convert from
an integer to a pointer, when calling operator():
Build started at 13:49...
------ Build started: Project: ConsoleTest2022, Configuration:x64 ------
Debug
main.cpp'uint8_t *'
C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.40.33807\include\xutility(1501,14): error C2664: 'bool my_sort3::<unnamed-type-cmp>::operator ()(uint8_t *,uint8_t *)': cannot convert argument 1 from 'unsigned char' to
C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.40.33807\include\algorithm(8068,16):error C2664: 'bool my_sort3::<unnamed-type-cmp>::operator ()(uint8_t *,uint8_t *)': cannot convert argument 1 from 'uint8_t' to 'uint8_t *' 1>C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.40.33807\include\algorithm(8072,76):
error C2664: 'bool my_sort3::<unnamed-type-cmp>::operator ()(uint8_t *,uint8_t *)': cannot convert argument 1 from 'uint8_t' to 'uint8_t *' 1>C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.40.33807\include\algorithm(8083,24):
error C2664: 'bool my_sort3::<unnamed-type-cmp>::operator ()(uint8_t *,uint8_t *)': cannot convert argument 1 from 'uint8_t' to 'uint8_t *' 1>C:\Program Files\Microsoft Visual Studio\2022\Professional\VC\Tools\MSVC\14.40.33807\include\algorithm(8097,24):
error C2664: 'bool my_sort3::<unnamed-type-cmp>::operator ()(uint8_t *,uint8_t *)': cannot convert argument 1 from 'uint8_t' to 'uint8_t *'
Done building project "ConsoleTest2022.vcxproj" -- FAILED.========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped
========== ========== Build completed at 13:49 and took 00,390
seconds ==========
Am 25.09.2024 um 10:24 schrieb [email protected]:
Tbh if you want to massage templating that much then don't use it in the
first place, just hard code the functions.
To avoid concepts ? LOL.
This is where compiler command line switches should come into play.
Without concepts the compiler has no other choice than giving Swahili
error messages from inside the implementation.
Don't use auto in those scenarios then. Its always nicer when people put
the specific type in the code anyway.
auto-parameters *with* concepts become much cleaner than without.
There's no choice to hardcode sth. then because you want to be generic.
On Wed, 25 Sep 2024 13:11:44 +0300
Michael S <[email protected]> boringly babbled:
Below is example of very simple function with very small
template-related bug of sort that people sometimes do in real life.
I tried 3 compilers. Each one produces more than 20 lines of error
messages none of which was enlightening.
#include <cstdint>
#include <algorithm>
void my_sort2(uint8_t* buf, int buflen, const uint8_t* trans_tab)
{
struct {
const uint8_t* tab;
bool operator() (uint8_t a, uint8_t b) {
return tab[a] < tab[b];
}
} cmp;
cmp.tab = trans_tab;
std::sort(buf, buf+buflen, &cmp);
}
I don't know if with conceptized <algorithm> error reporting would
work better. But it is worth a try, because current state is not >satisfactory.
My version of Clang produced 305 lines of output. Yet right at the
top:
t.cc:13:3: warning: template argument uses local type '(unnamed
struct at t.cc:6 :3)' [-Wlocal-type-template-args]
std::sort(buf, buf+buflen, &cmp);
Which is all it needed to print. The other 304 lines of garbage could
have been reserved for the -Wall option.
For some reason Bonita finds this concept (pun intended) had to
understand.
Am 25.09.2024 um 12:11 schrieb Michael S:
#include <cstdint>
#include <algorithm>
void my_sort2(uint8_t* buf, int buflen, const uint8_t* trans_tab)
{
struct {
const uint8_t* tab;
bool operator() (uint8_t a, uint8_t b) {
return tab[a] < tab[b];
}
} cmp;
cmp.tab = trans_tab;
std::sort(buf, buf+buflen, &cmp);
}
How about this ?
void my_sort2 ( uint8_t *buf, int buflen, const uint8_t *trans_tab )
{
sort( buf, buf + buflen, [tab = trans_tab]( uint8_t l,
uint8_t r ) { return tab[l] < tab[r]; } );
}
On Wed, 25 Sep 2024 11:32:56 +0200
Bonita Montero <[email protected]> boringly babbled:
Am 25.09.2024 um 10:24 schrieb [email protected]:
Tbh if you want to massage templating that much then don't use it in the >>> first place, just hard code the functions.
To avoid concepts ? LOL.
This is where compiler command line switches should come into play.
Without concepts the compiler has no other choice than giving Swahili
error messages from inside the implementation.
Don't use auto in those scenarios then. Its always nicer when people put >>> the specific type in the code anyway.
auto-parameters *with* concepts become much cleaner than without.
There's no choice to hardcode sth. then because you want to be generic.
Generic is only needed if you're writing a library for others to use. If you're writing a self contained program its nothing more than a shortcut so in that instance if it causes issues then hard code the functions.
On Wed, 25 Sep 2024 08:24:19 -0000 (UTC)
[email protected] wrote:
On Wed, 25 Sep 2024 09:49:49 +0200
David Brown <[email protected]> boringly babbled:
looks up. The error message is likely to be complex because the
compiler doesn't know where you made the mistake, and tries to list
It knows what line invoked the error so all it needs to do is print
that.
Using concepts, however, means the error is found on the first
attempt to use the type "A<T>" - the compiler can give far clearer
information. It can say "On line x, you tried to make A<T>, but type
T does not fulfil the requirement you gave on line y when you
defined class A".
Tbh if you want to massage templating that much then don't use it in
the first place, just hard code the functions.
Competition is a great thing). And you sometimes want more
information, sometimes less. But concepts give the programmer and
the compiler a far better starting point.
This is where compiler command line switches should come into play.
They also allow neater and clearer code with constrained auto
declarations, letting you specify something between "this /exact/
type" and "whatever seems to fit".
Don't use auto in those scenarios then. Its always nicer when people
put the specific type in the code anyway.
Below is example of very simple function with very small
template-related bug of sort that people sometimes do in real life.
I tried 3 compilers. Each one produces more than 20 lines of error
messages none of which was enlightening.
#include <cstdint>
#include <algorithm>
void my_sort2(uint8_t* buf, int buflen, const uint8_t* trans_tab)
{
struct {
const uint8_t* tab;
bool operator() (uint8_t a, uint8_t b) {
return tab[a] < tab[b];
}
} cmp;
cmp.tab = trans_tab;
std::sort(buf, buf+buflen, &cmp);
}
I don't know if with conceptized <algorithm> error reporting would
work better. But it is worth a try, because current state is not satisfactory.
I was testing with MSVC compiler/headers from VS2019
It produces this message as well, but only on 28th line. In my
experience it means that typical reader will give up earlier.
In your part of the world it could be different :-)
Am 25.09.2024 um 13:38 schrieb Michael S:
On Wed, 25 Sep 2024 13:33:50 +0200
Bonita Montero <[email protected]> wrote:
Am 25.09.2024 um 12:11 schrieb Michael S:
#include <cstdint>
#include <algorithm>
void my_sort2(uint8_t* buf, int buflen, const uint8_t* trans_tab)
{
struct {
const uint8_t* tab;
bool operator() (uint8_t a, uint8_t b) {
return tab[a] < tab[b];
}
} cmp;
cmp.tab = trans_tab;
std::sort(buf, buf+buflen, &cmp);
}
How about this ?
void my_sort2 ( uint8_t *buf, int buflen, const uint8_t *trans_tab )
{
sort( buf, buf + buflen, [tab = trans_tab]( uint8_t l,
uint8_t r ) { return tab[l] < tab[r]; } );
}
Your post is off topic.
To remind you, the topic is unsatisfactory error reporting of "classic"
non-conceptualized templates, esp. of those in STL.
The topic develops with each step in the thread.
That's true for most of those large threads.
On Wed, 25 Sep 2024 11:12:45 -0000 (UTC)
[email protected] wrote:
On Wed, 25 Sep 2024 13:11:44 +0300
Michael S <[email protected]> boringly babbled:
Below is example of very simple function with very small
template-related bug of sort that people sometimes do in real life.
I tried 3 compilers. Each one produces more than 20 lines of error
messages none of which was enlightening.
#include <cstdint>
#include <algorithm>
void my_sort2(uint8_t* buf, int buflen, const uint8_t* trans_tab)
{
struct {
const uint8_t* tab;
bool operator() (uint8_t a, uint8_t b) {
return tab[a] < tab[b];
}
} cmp;
cmp.tab = trans_tab;
std::sort(buf, buf+buflen, &cmp);
}
I don't know if with conceptized <algorithm> error reporting would
work better. But it is worth a try, because current state is not
satisfactory.
My version of Clang produced 305 lines of output. Yet right at the
top:
t.cc:13:3: warning: template argument uses local type '(unnamed
struct at t.cc:6 :3)' [-Wlocal-type-template-args]
std::sort(buf, buf+buflen, &cmp);
Which is all it needed to print. The other 304 lines of garbage could
have been reserved for the -Wall option.
First, it's warning, not error.
Second, I don't know about you, but I personally have no idea what
is 'local type' and why it possibly could be a problem.
Even if the warning is somehow related to actual bug, which I doubt, the relationship is far from obvious.
For some reason Bonita finds this concept (pun intended) had to
understand.
On 25/09/2024 12:11, Michael S wrote:
On Wed, 25 Sep 2024 08:24:19 -0000 (UTC)
[email protected] wrote:
On Wed, 25 Sep 2024 09:49:49 +0200
David Brown <[email protected]> boringly babbled:
looks up. The error message is likely to be complex because the
compiler doesn't know where you made the mistake, and tries to
list
It knows what line invoked the error so all it needs to do is print
that.
Using concepts, however, means the error is found on the first
attempt to use the type "A<T>" - the compiler can give far clearer
information. It can say "On line x, you tried to make A<T>, but
type T does not fulfil the requirement you gave on line y when you
defined class A".
Tbh if you want to massage templating that much then don't use it
in the first place, just hard code the functions.
Competition is a great thing). And you sometimes want more
information, sometimes less. But concepts give the programmer and
the compiler a far better starting point.
This is where compiler command line switches should come into play.
They also allow neater and clearer code with constrained auto
declarations, letting you specify something between "this /exact/
type" and "whatever seems to fit".
Don't use auto in those scenarios then. Its always nicer when
people put the specific type in the code anyway.
Below is example of very simple function with very small
template-related bug of sort that people sometimes do in real life.
I tried 3 compilers. Each one produces more than 20 lines of error
messages none of which was enlightening.
#include <cstdint>
#include <algorithm>
void my_sort2(uint8_t* buf, int buflen, const uint8_t* trans_tab)
{
struct {
const uint8_t* tab;
bool operator() (uint8_t a, uint8_t b) {
return tab[a] < tab[b];
}
} cmp;
cmp.tab = trans_tab;
std::sort(buf, buf+buflen, &cmp);
}
I don't know if with conceptized <algorithm> error reporting would
work better. But it is worth a try, because current state is not satisfactory.
This is a rough attempt at using concepts here. I made an "xsort"
template that checks (at compile time, obviously) a requirement and
otherwise passes things on to std::sort. The
"ComparisonFunctionObject" concept checks that you can call the
object with two values of a given type and get a result that can be
converted to a bool. The requirement for the Compare parameter to
xsort is that this concept must be satisfied with the type you get
from dereferencing the iterators.
The error messages you get are a lot shorter and clearer. There's
still room for improvement, as always, but it is a /big/ step forward.
If you think a particular kind of error - such as using a pointer to
the comparison function object instead of the object itself - is
common, you can add extra checks to make the errors even clearer.
<https://godbolt.org/z/jzqeh8enG>
#include <concepts>
#include <type_traits>
#include <cstdint>
#include <algorithm>
template <class T>
concept Not_pointer = !std::is_pointer_v<T>;
template<class Compare, class T>
concept ComparisonFunctionObject = requires (T a, T b)
{
{ Compare()(a, b) } -> std::convertible_to<bool>;
};
template< class RandomIt, class Compare >
requires
#if 1 // <- Change this
Not_pointer<Compare> &&
#endif
ComparisonFunctionObject<Compare,
std::remove_pointer_t<RandomIt> > void xsort( RandomIt first,
RandomIt last, Compare comp ) { std::sort(first, last, comp);
}
void my_sort2(uint8_t* buf, int buflen, const uint8_t* trans_tab)
{
struct {
const uint8_t* tab;
bool operator() (uint8_t a, uint8_t b) {
return tab[a] < tab[b];
}
} cmp;
cmp.tab = trans_tab;
xsort(buf, buf+buflen, cmp);
xsort(buf, buf+buflen, &cmp);
}
Am 25.09.2024 um 13:49 schrieb David Brown:
template<class Compare, class T>
concept ComparisonFunctionObject = requires (T a, T b)
{
{ Compare()(a, b) } -> std::convertible_to<bool>;
};
This may fail with a lambda with capture, since the lambda isn't default-constructible but just moveable or copyable. So it should
look like this:
template<class Compare, class T>
concept ComparisonFunctionObject = requires (Compare cmp, T a, T b)
{
{ cmp(a, b) } -> std::convertible_to<bool>;
};
On 25/09/2024 13:15, [email protected] wrote:
Generic is only needed if you're writing a library for others to use. If
you're writing a self contained program its nothing more than a shortcut so >> in that instance if it causes issues then hard code the functions.
I use generic code regularly in my small-systems embedded programming.
Rather than using run-time class hierarchies with virtual functions and >run-time polymorphic code, I use compile-time class hierarchies and
generic functions. This means more information can be tied to the types
of objects, resulting in minimal object code and maximal efficiency
without having to duplicate the source code.
On Wed, 25 Sep 2024 11:12:45 -0000 (UTC)
[email protected] wrote:
My version of Clang produced 305 lines of output. Yet right at the
top:
t.cc:13:3: warning: template argument uses local type '(unnamed
struct at t.cc:6 :3)' [-Wlocal-type-template-args]
std::sort(buf, buf+buflen, &cmp);
Which is all it needed to print. The other 304 lines of garbage could
have been reserved for the -Wall option.
First, it's warning, not error.
Second, I don't know about you, but I personally have no idea what
is 'local type' and why it possibly could be a problem.
Am 25.09.2024 um 13:15 schrieb [email protected]:
Generic is only needed if you're writing a library for others to use.
I often write generic code that I use myself in different ways,
each specialized to a set of generic types. I think that's much
more common than that someone implements a generic API for use
by the public.
F.e. this code is a function which concatenates anything which
can be converted to a basic_string_view to a corrsponding string
with same char- and traits-tyme.
On Wed, 25 Sep 2024 13:49:26 +0200
David Brown <[email protected]> wrote:
On 25/09/2024 12:11, Michael S wrote:
On Wed, 25 Sep 2024 08:24:19 -0000 (UTC)
[email protected] wrote:
On Wed, 25 Sep 2024 09:49:49 +0200
David Brown <[email protected]> boringly babbled:
looks up. The error message is likely to be complex because the
compiler doesn't know where you made the mistake, and tries to
list
It knows what line invoked the error so all it needs to do is print
that.
Using concepts, however, means the error is found on the first
attempt to use the type "A<T>" - the compiler can give far clearer
information. It can say "On line x, you tried to make A<T>, but
type T does not fulfil the requirement you gave on line y when you
defined class A".
Tbh if you want to massage templating that much then don't use it
in the first place, just hard code the functions.
Competition is a great thing). And you sometimes want more
information, sometimes less. But concepts give the programmer and
the compiler a far better starting point.
This is where compiler command line switches should come into play.
They also allow neater and clearer code with constrained auto
declarations, letting you specify something between "this /exact/
type" and "whatever seems to fit".
Don't use auto in those scenarios then. Its always nicer when
people put the specific type in the code anyway.
Below is example of very simple function with very small
template-related bug of sort that people sometimes do in real life.
I tried 3 compilers. Each one produces more than 20 lines of error
messages none of which was enlightening.
#include <cstdint>
#include <algorithm>
void my_sort2(uint8_t* buf, int buflen, const uint8_t* trans_tab)
{
struct {
const uint8_t* tab;
bool operator() (uint8_t a, uint8_t b) {
return tab[a] < tab[b];
}
} cmp;
cmp.tab = trans_tab;
std::sort(buf, buf+buflen, &cmp);
}
I don't know if with conceptized <algorithm> error reporting would
work better. But it is worth a try, because current state is not
satisfactory.
This is a rough attempt at using concepts here. I made an "xsort"
template that checks (at compile time, obviously) a requirement and
otherwise passes things on to std::sort. The
"ComparisonFunctionObject" concept checks that you can call the
object with two values of a given type and get a result that can be
converted to a bool. The requirement for the Compare parameter to
xsort is that this concept must be satisfied with the type you get
from dereferencing the iterators.
The error messages you get are a lot shorter and clearer. There's
still room for improvement, as always, but it is a /big/ step forward.
If you think a particular kind of error - such as using a pointer to
the comparison function object instead of the object itself - is
common, you can add extra checks to make the errors even clearer.
<https://godbolt.org/z/jzqeh8enG>
#include <concepts>
#include <type_traits>
#include <cstdint>
#include <algorithm>
template <class T>
concept Not_pointer = !std::is_pointer_v<T>;
template<class Compare, class T>
concept ComparisonFunctionObject = requires (T a, T b)
{
{ Compare()(a, b) } -> std::convertible_to<bool>;
};
template< class RandomIt, class Compare >
requires
#if 1 // <- Change this
Not_pointer<Compare> &&
#endif
ComparisonFunctionObject<Compare,
std::remove_pointer_t<RandomIt> > void xsort( RandomIt first,
RandomIt last, Compare comp ) { std::sort(first, last, comp);
}
void my_sort2(uint8_t* buf, int buflen, const uint8_t* trans_tab)
{
struct {
const uint8_t* tab;
bool operator() (uint8_t a, uint8_t b) {
return tab[a] < tab[b];
}
} cmp;
cmp.tab = trans_tab;
xsort(buf, buf+buflen, cmp);
xsort(buf, buf+buflen, &cmp);
}
Interesting attempt, but not good enough. It fails in most common
use case where Compare is function rather than object.
Callback functions are a C thing. In C++ we have functors and lambdas.
Over the years I have removed all ancient callback functions from my
code and replaced by functors or lambdas, so your "most common use case"
In this example (a one-line sort helper) a lambda is the superior
solution anyway.
On Wed, 25 Sep 2024 13:49:26 +0200
David Brown <[email protected]> wrote:
On 25/09/2024 12:11, Michael S wrote:
On Wed, 25 Sep 2024 08:24:19 -0000 (UTC)
[email protected] wrote:
On Wed, 25 Sep 2024 09:49:49 +0200
David Brown <[email protected]> boringly babbled:
looks up. The error message is likely to be complex because the
compiler doesn't know where you made the mistake, and tries to
list
It knows what line invoked the error so all it needs to do is print
that.
Using concepts, however, means the error is found on the first
attempt to use the type "A<T>" - the compiler can give far clearer
information. It can say "On line x, you tried to make A<T>, but
type T does not fulfil the requirement you gave on line y when you
defined class A".
Tbh if you want to massage templating that much then don't use it
in the first place, just hard code the functions.
Competition is a great thing). And you sometimes want more
information, sometimes less. But concepts give the programmer and
the compiler a far better starting point.
This is where compiler command line switches should come into play.
They also allow neater and clearer code with constrained auto
declarations, letting you specify something between "this /exact/
type" and "whatever seems to fit".
Don't use auto in those scenarios then. Its always nicer when
people put the specific type in the code anyway.
Below is example of very simple function with very small
template-related bug of sort that people sometimes do in real life.
I tried 3 compilers. Each one produces more than 20 lines of error
messages none of which was enlightening.
#include <cstdint>
#include <algorithm>
void my_sort2(uint8_t* buf, int buflen, const uint8_t* trans_tab)
{
struct {
const uint8_t* tab;
bool operator() (uint8_t a, uint8_t b) {
return tab[a] < tab[b];
}
} cmp;
cmp.tab = trans_tab;
std::sort(buf, buf+buflen, &cmp);
}
I don't know if with conceptized <algorithm> error reporting would
work better. But it is worth a try, because current state is not
satisfactory.
This is a rough attempt at using concepts here. I made an "xsort"
template that checks (at compile time, obviously) a requirement and
otherwise passes things on to std::sort. The
"ComparisonFunctionObject" concept checks that you can call the
object with two values of a given type and get a result that can be
converted to a bool. The requirement for the Compare parameter to
xsort is that this concept must be satisfied with the type you get
from dereferencing the iterators.
The error messages you get are a lot shorter and clearer. There's
still room for improvement, as always, but it is a /big/ step forward.
If you think a particular kind of error - such as using a pointer to
the comparison function object instead of the object itself - is
common, you can add extra checks to make the errors even clearer.
<https://godbolt.org/z/jzqeh8enG>
#include <concepts>
#include <type_traits>
#include <cstdint>
#include <algorithm>
template <class T>
concept Not_pointer = !std::is_pointer_v<T>;
template<class Compare, class T>
concept ComparisonFunctionObject = requires (T a, T b)
{
{ Compare()(a, b) } -> std::convertible_to<bool>;
};
template< class RandomIt, class Compare >
requires
#if 1 // <- Change this
Not_pointer<Compare> &&
#endif
ComparisonFunctionObject<Compare,
std::remove_pointer_t<RandomIt> > void xsort( RandomIt first,
RandomIt last, Compare comp ) { std::sort(first, last, comp);
}
void my_sort2(uint8_t* buf, int buflen, const uint8_t* trans_tab)
{
struct {
const uint8_t* tab;
bool operator() (uint8_t a, uint8_t b) {
return tab[a] < tab[b];
}
} cmp;
cmp.tab = trans_tab;
xsort(buf, buf+buflen, cmp);
xsort(buf, buf+buflen, &cmp);
}
Interesting attempt, but not good enough. It fails in most common
use case where Compare is function rather than object.
bool cmp3(uint8_t a, uint8_t b) {
return a < b;
}
void my_sort3(uint8_t* buf, int buflen, const uint8_t* )
{
#if 1
xsort(buf, buf+buflen, cmp3);
#else
std::sort(buf, buf+buflen, cmp3);
#endif
}
STL's insistence on [mis]use of static polymorphism in every possible
and impossible hole strikes again!
Am 25.09.2024 um 14:36 schrieb David Brown:
That's reasonable if you have something useful to say. Telling people
that they can use lambdas here is /not/ useful. We all know we could
use lambdas, but that is totally missing the point of the discussion.
I guess Michael S doesn't know this, otherwrite he would have used
a lambda himself. That's pre-C++11-style and maybe his knowledge is
from then.
On Wed, 25 Sep 2024 14:00:22 +0200
David Brown <[email protected]> boringly babbled:
On 25/09/2024 13:15, [email protected] wrote:
Generic is only needed if you're writing a library for others to use. If >>> you're writing a self contained program its nothing more than a shortcut so >>> in that instance if it causes issues then hard code the functions.
I use generic code regularly in my small-systems embedded programming.
Rather than using run-time class hierarchies with virtual functions and
run-time polymorphic code, I use compile-time class hierarchies and
generic functions. This means more information can be tied to the types
of objects, resulting in minimal object code and maximal efficiency
without having to duplicate the source code.
Can't imagine why you'd need any of that in small systems embedded as the size of programs you can fit into memory constrains the structural complexity.
If you need class inheritance in something that has to fit into 16K or whatever then you're doing it wrong.
Am 25.09.2024 um 14:34 schrieb David Brown:
The "struct {...} cmp" here is a local (anonymous) type. The use of
such types in template arguments might lead to code bloat, as it would
be hard to merge the generated template function instantiations later
on if you have used basically the same local structs multiple times.
This "bloat" actually doesn't hurt since the sort()-function has enough locality in itself.
On 25.09.2024 15:17, Michael S wrote:
Interesting attempt, but not good enough. It fails in most common
use case where Compare is function rather than object.
Callback functions are a C thing. In C++ we have functors and lambdas.
Over the years I have removed all ancient callback functions from my
code and replaced by functors or lambdas, so your "most common use case"
is not everybody's use case.
In this example (a one-line sort helper) a lambda is the superior
solution anyway.
On Wed, 25 Sep 2024 15:49:15 +0300
Paavo Helde <[email protected]> boringly babbled:
Callback functions are a C thing. In C++ we have functors and lambdas.
Over the years I have removed all ancient callback functions from my
code and replaced by functors or lambdas, so your "most common use case"
What for? They usually compile down to the same code at the end and lambdas are only useful if that function is only used in that one place. If its used more than once then a function pointer is more useful and neater.
In this example (a one-line sort helper) a lambda is the superior
solution anyway.
Why?
Am 25.09.2024 um 14:55 schrieb [email protected]:
What for? They usually compile down to the same code at the end and lambdas >> are only useful if that function is only used in that one place. If its used >> more than once then a function pointer is more useful and neater.
Usually you have state along with your function-object, this can't be
attached conveniently to a function-pointer. With lambdas that's easy,
their state can be copied and moved.
On 25/09/2024 14:47, [email protected] wrote:
On Wed, 25 Sep 2024 14:00:22 +0200
David Brown <[email protected]> boringly babbled:
On 25/09/2024 13:15, [email protected] wrote:
Generic is only needed if you're writing a library for others to use. If >>>> you're writing a self contained program its nothing more than a shortcut so
in that instance if it causes issues then hard code the functions.
I use generic code regularly in my small-systems embedded programming.
Rather than using run-time class hierarchies with virtual functions and
run-time polymorphic code, I use compile-time class hierarchies and
generic functions. This means more information can be tied to the types >>> of objects, resulting in minimal object code and maximal efficiency
without having to duplicate the source code.
Can't imagine why you'd need any of that in small systems embedded as the
size of programs you can fit into memory constrains the structural >complexity.
If you need class inheritance in something that has to fit into 16K or
whatever then you're doing it wrong.
That is just because you can't imagine.
On 25.09.2024 15:55, [email protected] wrote:
On Wed, 25 Sep 2024 15:49:15 +0300
Paavo Helde <[email protected]> boringly babbled:
Callback functions are a C thing. In C++ we have functors and lambdas.
Over the years I have removed all ancient callback functions from my
code and replaced by functors or lambdas, so your "most common use case"
What for? They usually compile down to the same code at the end and lambdas >> are only useful if that function is only used in that one place. If its used >> more than once then a function pointer is more useful and neater.
One can pack state with a lambda. With a callback function one needs to
pass state via an extra parameter, which is clumsy, error-prone and is
If you want reuse, you can call a common function from the lambda, it's
not like this is prohibited or something.
In this example (a one-line sort helper) a lambda is the superior
solution anyway.
Why?
Because the sorting criteria is right there in the code where the
sorting happens, and one also avoids one of the two most hard problems
in computer science (naming things - >https://martinfowler.com/bliki/TwoHardThings.html).
Am 25.09.2024 um 15:36 schrieb [email protected]:
If you're writing C++ you're not doing functional programming, period.
C++ supports functional programming since C++11, but doesn't mandate
pure functional programming.
Functional requires polymorphism by value which C++ does not support.
Functional programming in C++ allows for compile-time polymporphism
with generic function-objects and and runtime polymporphism with >std::function<>.
Am 25.09.2024 um 15:24 schrieb [email protected]:
On Wed, 25 Sep 2024 15:08:09 +0200
Bonita Montero <[email protected]> boringly babbled:
Am 25.09.2024 um 14:55 schrieb [email protected]:
What for? They usually compile down to the same code at the end and lambdas
are only useful if that function is only used in that one place. If its >used
more than once then a function pointer is more useful and neater.
Usually you have state along with your function-object, this can't be
Rarely used. Eg sorting functor for STL containers.
I'm using functional programmg all the day and most lambdss I write
have state.
On 25/09/2024 14:39, Bonita Montero wrote:
Am 25.09.2024 um 14:36 schrieb David Brown:
That's reasonable if you have something useful to say. Telling
people that they can use lambdas here is /not/ useful. We all
know we could use lambdas, but that is totally missing the point
of the discussion.
I guess Michael S doesn't know this, otherwrite he would have used
a lambda himself. That's pre-C++11-style and maybe his knowledge is
from then.
Seriously? Sometimes I wonder if you ever bother reading other
people's posts.
Of /course/ Michael knows he could use a lambda there.
On Wed, 25 Sep 2024 15:44:21 +0200
Bonita Montero <[email protected]> boringly babbled:
Am 25.09.2024 um 15:36 schrieb [email protected]:
If you're writing C++ you're not doing functional programming,
period.
C++ supports functional programming since C++11, but doesn't mandate
pure functional programming.
Functional style programming != functional programming.
On Wed, 25 Sep 2024 15:00:20 +0200
David Brown <[email protected]> boringly babbled:
On 25/09/2024 14:47, [email protected] wrote:
On Wed, 25 Sep 2024 14:00:22 +0200complexity.
David Brown <[email protected]> boringly babbled:
On 25/09/2024 13:15, [email protected] wrote:
Generic is only needed if you're writing a library for others to use. If >>>>> you're writing a self contained program its nothing more than a shortcut so
in that instance if it causes issues then hard code the functions.
I use generic code regularly in my small-systems embedded programming. >>>> Rather than using run-time class hierarchies with virtual functions and >>>> run-time polymorphic code, I use compile-time class hierarchies and
generic functions. This means more information can be tied to the types >>>> of objects, resulting in minimal object code and maximal efficiency
without having to duplicate the source code.
Can't imagine why you'd need any of that in small systems embedded as the >>> size of programs you can fit into memory constrains the structural
If you need class inheritance in something that has to fit into 16K or
whatever then you're doing it wrong.
That is just because you can't imagine.
Funny. Misra-C++ is it? No? Then you're just playing with toys, not doing proper embedded.
Am 25.09.2024 um 16:14 schrieb Michael S:
I know that they can be used, but I never use lambdas myself.
That's nearly the same as what I've said.
Why I am not using lambdas myself? Because I think that lambda with
captures makes code harder to follow and to understand (...).
Lambdas are much less code and for me are therefore more readable.
And lambda without captures does not provide enough of advantageNamed functions haven't state.
over named functions to bother with mastering new concept.
On Wed, 25 Sep 2024 16:22:43 +0300
Paavo Helde <[email protected]> boringly babbled:
On 25.09.2024 15:55, [email protected] wrote:
On Wed, 25 Sep 2024 15:49:15 +0300
Paavo Helde <[email protected]> boringly babbled:
Callback functions are a C thing. In C++ we have functors and lambdas. >>>> Over the years I have removed all ancient callback functions from myWhat for? They usually compile down to the same code at the end and lambdas >>> are only useful if that function is only used in that one place. If its used
code and replaced by functors or lambdas, so your "most common use case" >>>
more than once then a function pointer is more useful and neater.
One can pack state with a lambda. With a callback function one needs to
pass state via an extra parameter, which is clumsy, error-prone and is
You think that makes any difference at the assembler level?
If you want reuse, you can call a common function from the lambda, it's
not like this is prohibited or something.
I'm not saying lambdas shouldn't be used, but I've seen a fair amount of
code with the same lambda code being used in different functions instead of just writing one function and passing a function pointer. Its messy.
In this example (a one-line sort helper) a lambda is the superior
solution anyway.
Why?
Because the sorting criteria is right there in the code where the
sorting happens, and one also avoids one of the two most hard problems
in computer science (naming things -
https://martinfowler.com/bliki/TwoHardThings.html).
Thats not why, thats "because I prefer it".
Am 25.09.2024 um 16:11 schrieb [email protected]:
On Wed, 25 Sep 2024 15:44:21 +0200
Bonita Montero <[email protected]> boringly babbled:
Am 25.09.2024 um 15:36 schrieb [email protected]:
If you're writing C++ you're not doing functional programming, period.
C++ supports functional programming since C++11, but doesn't mandate
pure functional programming.
Functional style programming != functional programming.
You make up your own definitions so that
you somehow feel like you were right.
Which part of polymorphism by value did you not understand?
I understand both types of polymorphism, you nothing.
Am 25.09.2024 um 17:32 schrieb [email protected]:
I understand both types of polymorphism, you nothing.
You don't have clue. ...
And you have your own definition of polymorphism.
On 25/09/2024 15:22, [email protected] wrote:
Funny. Misra-C++ is it? No? Then you're just playing with toys, not doing
proper embedded.
If you think MISRA is required for embedded development, or even if you
think it is a good idea that promotes safe and reliable coding, then you
have little concept of the field. There are certainly markets where
On Wed, 25 Sep 2024 17:12:15 +0200
David Brown <[email protected]> boringly babbled:
On 25/09/2024 15:22, [email protected] wrote:
Funny. Misra-C++ is it? No? Then you're just playing with toys, not doing >>> proper embedded.
If you think MISRA is required for embedded development, or even if you
think it is a good idea that promotes safe and reliable coding, then you
have little concept of the field. There are certainly markets where
I didn't embedded in the UK defense industry so I probably have a bit more of a clue than you piddling around with your arduino or PIC writing code for
a toaster.
Why I am not using lambdas myself? Because I think that lambda with
captures makes code harder to follow and to understand (that's my 1st
hand experience from reading big corpus of Ada83 code. Ada83 does not
have lambdas, but it has nested procedures that can access parent's
variable, similarly to lambda with [&] default capture).
On Wed, 25 Sep 2024 16:22:43 +0300
Paavo Helde <[email protected]> boringly babbled:
On 25.09.2024 15:55, [email protected] wrote:
On Wed, 25 Sep 2024 15:49:15 +0300
Paavo Helde <[email protected]> boringly babbled:
Callback functions are a C thing. In C++ we have functors and lambdas. >>>> Over the years I have removed all ancient callback functions from myWhat for? They usually compile down to the same code at the end and lambdas >>> are only useful if that function is only used in that one place. If its used
code and replaced by functors or lambdas, so your "most common use case" >>>
more than once then a function pointer is more useful and neater.
One can pack state with a lambda. With a callback function one needs to
pass state via an extra parameter, which is clumsy, error-prone and is
You think that makes any difference at the assembler level?
Trying to come up to speed on C++ concepts. If they're are useful
why wouldn't they define named requirements as a concept, e.g.
template<typename T>
concept BasicLockable = requires(T a)
{
{ a.lock() } -> std::same_as<void>;
{ a.unlock() } -> std::same_as<void>;
};
template<BasicLockable T>
...
On 25/09/2024 17:35, [email protected] wrote:
I didn't embedded in the UK defense industry so I probably have a bit more of
a clue than you piddling around with your arduino or PIC writing code for
a toaster.
You "didn't embedded in the UK defense industry" - that I believe.
It's harder to believe that you /did/ work with embedded systems related
to the UK defence industry. From my own experience, I know there are
priority than actually trying to do good development - "You can't sue us
for writing bad software, because we use industry standard MISRA rules".
Am 25.09.2024 um 17:49 schrieb [email protected]:
Thanks for proving my point. A proper functional language has
value polymorphism (which clearly you don't understand) ...
You've probably never dealt with std::function<> objects. That's
value polymorphism! I use that all the dayd, f.e. with this function:
You can pass a lambda to this function and the type-polymorphism
becomes a value-polymophism.
On 25.09.2024 16:34, [email protected] wrote:
On Wed, 25 Sep 2024 16:22:43 +0300
Paavo Helde <[email protected]> boringly babbled:
On 25.09.2024 15:55, [email protected] wrote:
On Wed, 25 Sep 2024 15:49:15 +0300
Paavo Helde <[email protected]> boringly babbled:
Callback functions are a C thing. In C++ we have functors and lambdas. >>>>> Over the years I have removed all ancient callback functions from my >>>>> code and replaced by functors or lambdas, so your "most common use case" >>>>What for? They usually compile down to the same code at the end and lambdas
are only useful if that function is only used in that one place. If its >used
more than once then a function pointer is more useful and neater.
One can pack state with a lambda. With a callback function one needs to
pass state via an extra parameter, which is clumsy, error-prone and is
You think that makes any difference at the assembler level?
Why should it make any difference at the assembler level? Programming >languages are meant for people, both for writing and reading the code.
I agree that if someone is not familiar with C++ lambdas, one might feel >themselves lost as they do not immediately understand what the code
does. But this can be easily alleviated by spending 30 minutes for
actually learning the lambda syntax.
On Thu, 2024-09-26 at 10:20 +0200, Bonita Montero wrote:
Am 26.09.2024 um 09:34 schrieb [email protected]:
On Wed, 25 Sep 2024 17:53:58 +0200
Bonita Montero <[email protected]> boringly babbled:
Am 25.09.2024 um 17:49 schrieb [email protected]:
Thanks for proving my point. A proper functional language has
value polymorphism (which clearly you don't understand) ...
You've probably never dealt with std::function<> objects. That's
value polymorphism! I use that all the dayd, f.e. with this
function:
[Usual unreadable mess snipped]
My collegue calls my code "too beautiful".
I would call "too academic" or too much brain-washed by c++std stuff.
From the last time I saw your ring_deque, I found you don't understand
what the ring buffer is. And, a for loop caught my attention, it
should be like this (just a few from my memory, because I had some memory C/C++ beginners like to write 'smart' programs):
for(size_t i=arr.capacity(); i--; ) {
// access all element of arr
}
Normally, the for loop should be like "for(size_t i=0 ; i>=0; --i) {
/* stuff */ } (I forgot to chk whether 'capacity' is correct or not)
Am 26.09.2024 um 09:34 schrieb [email protected]:
On Wed, 25 Sep 2024 17:53:58 +0200
Bonita Montero <[email protected]> boringly babbled:
Am 25.09.2024 um 17:49 schrieb [email protected]:
Thanks for proving my point. A proper functional language has
value polymorphism (which clearly you don't understand) ...
You've probably never dealt with std::function<> objects. That's
value polymorphism! I use that all the dayd, f.e. with this function:
[Usual unreadable mess snipped]
My collegue calls my code "too beautiful".
You can pass a lambda to this function and the type-polymorphism
becomes a value-polymophism.
Oh dear. Thats not value polymorphism, thats a very complicated way of doing >> if-then.
You can consider it like that, but it's sill polymorphism because theres >state and a pointer-dispatched function which is caalled on this state.
Am 26.09.2024 um 12:03 schrieb [email protected]:
Value polymorphism requires the value to be in the function prototype, not >> buried in some code underneath. Like all polymorphism its just a syntatic
and semantic nicety as it can simply be done using if-then style code.
Calling a function<>-object is always value polymorphism.
On Fri, 27 Sep 2024 02:27:52 -0700
Tim Rentsch <[email protected]> boringly babbled:
[email protected] writes:
Thanks for proving my point. A proper functional language has value
polymorphism (which clearly you don't understand) as well as type
polymorphism.
https://chrisdone.com/posts/value-polymorphism/
Neither "value polymorphism" nor "polymorphism by value" is a
common term.
Because not many languages support it.
They mean different things.
Not in the version of english I speak they don't.
Polymorphism by value was discussed in a 1993 paper by Xavier
Leroy.
Who?
Value polymorphism, as it is described in the cited web page, is
simply overloading of variables rather than functions.
(Overloading is often called "ad hoc polymorphism".)
Thats his opinion.
Neither overloading of functions nor overloading of variables has
anything to do with functional programming. Overloading might be
Without it many of the standard examples of functional programming simply won't run.
that don't have facilities for polymorphism, as for example the
original Lisp.
Since when was Lisp a functional language? Scheme maybe.
[email protected] writes:
On Fri, 27 Sep 2024 02:27:52 -0700
Tim Rentsch <[email protected]> boringly babbled:
[email protected] writes:
Thanks for proving my point. A proper functional language has value
polymorphism (which clearly you don't understand) as well as type
polymorphism.
https://chrisdone.com/posts/value-polymorphism/
Neither "value polymorphism" nor "polymorphism by value" is a
common term.
Because not many languages support it.
They mean different things.
Not in the version of english I speak they don't.
Polymorphism by value was discussed in a 1993 paper by Xavier
Leroy.
Who?
Value polymorphism, as it is described in the cited web page, is
simply overloading of variables rather than functions.
(Overloading is often called "ad hoc polymorphism".)
Thats his opinion.
Neither overloading of functions nor overloading of variables has
anything to do with functional programming. Overloading might be
Without it many of the standard examples of functional programming simply
won't run.
that don't have facilities for polymorphism, as for example the
original Lisp.
Since when was Lisp a functional language? Scheme maybe.
Judging by these responses, it seems you have no real interest in
having a useful conversation.
On Fri, 27 Sep 2024 18:24:11 +0200
David Brown <[email protected]> gabbled:
On 27/09/2024 17:25, [email protected] wrote:
That applies to all types of polymorphism. All decisional code
however its
written eventually ends up as some kind of assembler cmp/jmp
combination.
Might be 2 opcodes, might be 1, doesn't matter, same thing.
The key point of polymorphism - as I see it - is that the source code
doing the dispatching does not know the details of the possible
matches or targets. Thus C++ virtual methods support polymorphism - a
function
Not sure how that differs with value polymorphism.
call call "p->foo()" for "p" of type "T*" without knowing anything
about how many descendent types of "T" exist or how they different.
All it needs to know is that the base class "T" has a virtual method
"foo".
This cannot be done with a series of cmp/jmp instructions - it ends up
as an indirect call instruction.
It could quite easily, but yes, a call is more likely. Point still stands however.
I am pretty confident that what you seem to be talking about (AFAIUI)
is not something many people would refer to as "polymorphism"
When you say "people" you seem to be talking about people in the C++ world
or perhaps C-type language world. Polymorphism is more than just type
and virtual functions.
On 28/09/2024 11:12, [email protected] wrote:
On Fri, 27 Sep 2024 18:24:11 +0200
Not sure how that differs with value polymorphism.
I explained that - look at my answer below for another attempt.
It could quite easily, but yes, a call is more likely. Point still stands
however.
No, it could not be done - thus your point does /not/ stand.
That's run-time polymorphism using class inheritance and virtual
functions. When compiling doLotsOfFoo(), there is no way for the
compiler to know what types it might be called with - there is no way it >could be compiled as a selection of comparisons. Baring full-program >optimisation, indirect calls are the only way to handle it.
On Sat, 28 Sep 2024 17:33:46 +0200
David Brown <[email protected]> gabbled:
On 28/09/2024 11:12, [email protected] wrote:
On Fri, 27 Sep 2024 18:24:11 +0200
Not sure how that differs with value polymorphism.
I explained that - look at my answer below for another attempt.
No you didn't because like Bonita you have no idea what value
polymorphism is.
It could quite easily, but yes, a call is more likely. Point still
stands
however.
No, it could not be done - thus your point does /not/ stand.
Really? What exactly do you think an assembler call instruction is other
than a single opcode stack push and jump?
That's run-time polymorphism using class inheritance and virtual
functions. When compiling doLotsOfFoo(), there is no way for the
compiler to know what types it might be called with - there is no way
it could be compiled as a selection of comparisons. Baring
full-program optimisation, indirect calls are the only way to handle it.
And you don't think indirect jump instructions exist?
https://en.wikipedia.org/wiki/Indirect_branch
We can add assembler to the list of things you don't understand.
On 28/09/2024 11:12, [email protected] wrote:
On Fri, 27 Sep 2024 18:24:11 +0200
David Brown <[email protected]> gabbled:
This cannot be done with a series of cmp/jmp instructions - it
ends up as an indirect call instruction.
It could quite easily, but yes, a call is more likely. Point still
stands however.
No, it could not be done - thus your point does /not/ stand.
On 28/09/2024 17:47, [email protected] wrote:
On Sat, 28 Sep 2024 17:33:46 +0200
David Brown <[email protected]> gabbled:
On 28/09/2024 11:12, [email protected] wrote:
On Fri, 27 Sep 2024 18:24:11 +0200
Not sure how that differs with value polymorphism.
I explained that - look at my answer below for another attempt.
No you didn't because like Bonita you have no idea what value
polymorphism is.
The term "polymorphism" is used in reference to /types/, not
/values/. I am not new to programming, and have used a large variety
of languages (including functional programming languages), but have
not heard the phrase "value polymorphism" before. So I am trying to
get /you/ to say what /you/ mean by it. Maybe it's just a name you
and a few other people use for pattern matching.
On Sat, 28 Sep 2024 17:33:46 +0200
David Brown <[email protected]> wrote:
On 28/09/2024 11:12, [email protected] wrote:
On Fri, 27 Sep 2024 18:24:11 +0200
David Brown <[email protected]> gabbled:
This cannot be done with a series of cmp/jmp instructions - it
ends up as an indirect call instruction.
It could quite easily, but yes, a call is more likely. Point still
stands however.
No, it could not be done - thus your point does /not/ stand.
It could be done.
It could be done relatively easily with whole program compiler.
Without whole program compiler it still could be done, but would need
some co-operation from linker that would have to glue code segments.
I'd guess, majority of linkers already can do something of this sort
for other purposes.
Of course, if class hierarchy is sufficiently big then implementation
based on comparisons, branches and direct calls would be rather
inefficient relatively to indirect call (actually dual-indirect call in
most typical C++ implementations).
They point that you were probably missing when claiming that it is
impossible is that every instance of given class can carry its Class Id.
On 28/09/2024 17:47, [email protected] wrote:
On Sat, 28 Sep 2024 17:33:46 +0200
David Brown <[email protected]> gabbled:
On 28/09/2024 11:12, [email protected] wrote:
On Fri, 27 Sep 2024 18:24:11 +0200
Not sure how that differs with value polymorphism.
I explained that - look at my answer below for another attempt.
No you didn't because like Bonita you have no idea what value
polymorphism is.
The term "polymorphism" is used in reference to /types/, not /values/.
I am not new to programming, and have used a large variety of languages >(including functional programming languages), but have not heard the
phrase "value polymorphism" before. So I am trying to get /you/ to say
what /you/ mean by it. Maybe it's just a name you and a few other
people use for pattern matching.
Really? What exactly do you think an assembler call instruction is other
than a single opcode stack push and jump?
Yes, that's what a call instruction sequence is on many (but certainly
not all) processors. But that's not what you wrote. You wrote that
"all types of polymorphism" are just conditionals that boil down to
"cmp/jmp" instructions.
On Sun, 29 Sep 2024 13:41:27 +0200
David Brown <[email protected]> wrote:
On 28/09/2024 17:47, [email protected] wrote:
On Sat, 28 Sep 2024 17:33:46 +0200
David Brown <[email protected]> gabbled:
On 28/09/2024 11:12, [email protected] wrote:
On Fri, 27 Sep 2024 18:24:11 +0200
Not sure how that differs with value polymorphism.
I explained that - look at my answer below for another attempt.
No you didn't because like Bonita you have no idea what value
polymorphism is.
The term "polymorphism" is used in reference to /types/, not
/values/. I am not new to programming, and have used a large variety
of languages (including functional programming languages), but have
not heard the phrase "value polymorphism" before. So I am trying to
get /you/ to say what /you/ mean by it. Maybe it's just a name you
and a few other people use for pattern matching.
May be, 'polymorphism by type of return value' is what he has in mind?
fenris$ cat testprogs/functional3.fl
fenris$ fling < progs/functional3.fl
Michael S <[email protected]> writes:
def main(argv) ->
func("",["hello","cruel","world","this","is","a","test"])
: 0
;
def func(_,[]) -> "";
def func(_,[head<tail]) ->
printnl head,",",tail : func(head[0],tail);
def func("world",[head<tail]) ->
printnl "GOT WORLD" : func(head,tail);
It's not clear what programming language is intended here. Based on
my experience with other functional languages, it looks like this
example is (a) defined wrongly, (b) wouldn't compile because of a
type mismatch, and (c) is not polymorphic. But of course we can't
be sure without knowing more about what language is intended.
The web page does use the phrase "value polymorphism", but it's
My guess is Muttley is out of his depth and doesn't really know what
Michael S <[email protected]> writes:
On Sun, 29 Sep 2024 13:41:27 +0200
David Brown <[email protected]> wrote:
On 28/09/2024 17:47, [email protected] wrote:
On Sat, 28 Sep 2024 17:33:46 +0200
David Brown <[email protected]> gabbled:
On 28/09/2024 11:12, [email protected] wrote:
On Fri, 27 Sep 2024 18:24:11 +0200
Not sure how that differs with value polymorphism.
I explained that - look at my answer below for another attempt.
No you didn't because like Bonita you have no idea what value
polymorphism is.
The term "polymorphism" is used in reference to /types/, not
/values/. I am not new to programming, and have used a large variety
of languages (including functional programming languages), but have
not heard the phrase "value polymorphism" before. So I am trying to
get /you/ to say what /you/ mean by it. Maybe it's just a name you
and a few other people use for pattern matching.
May be, 'polymorphism by type of return value' is what he has in mind?
In a previous posting Muttley cited this url:
https://chrisdone.com/posts/value-polymorphism/
In a different previous posting Muttley gave this example (slightly reformatted for line length):
def main(argv) ->
func("",["hello","cruel","world","this","is","a","test"])
: 0
;
def func(_,[]) -> "";
def func(_,[head<tail]) ->
printnl head,",",tail : func(head[0],tail);
def func("world",[head<tail]) ->
printnl "GOT WORLD" : func(head,tail);
It's not clear what programming language is intended here. Based on
my experience with other functional languages, it looks like this
example is (a) defined wrongly, (b) wouldn't compile because of a
type mismatch, and (c) is not polymorphic. But of course we can't
be sure without knowing more about what language is intended.
The web page does use the phrase "value polymorphism", but it's
about something else altogether. The programming language Haskell
has a facility that might be called "variable overloading". It's
like what C or C++ would be if they allowed, for example:
int poly;
double poly[10];
and later on in the program a declaration such as
double *p = poly;
would know to use the double array 'poly', because the other 'poly'
doesn't type check.
My guess is Muttley is out of his depth and doesn't really know what
he means by these different phrases. But I found it impossible to
tell because in his postings he doesn't interact in any meaningful
way.
On Sun, 29 Sep 2024 11:23:51 -0700
Tim Rentsch <[email protected]> boring babbled:
But of course we can't
be sure without knowing more about what language is intended.
It was a language used in
a specialist task, it wasn't made public.
On Sun, 29 Sep 2024 13:41:27 +0200
David Brown <[email protected]> wrote:
On 28/09/2024 17:47, [email protected] wrote:
On Sat, 28 Sep 2024 17:33:46 +0200
David Brown <[email protected]> gabbled:
On 28/09/2024 11:12, [email protected] wrote:
On Fri, 27 Sep 2024 18:24:11 +0200
Not sure how that differs with value polymorphism.
I explained that - look at my answer below for another attempt.
No you didn't because like Bonita you have no idea what value
polymorphism is.
The term "polymorphism" is used in reference to /types/, not
/values/. I am not new to programming, and have used a large variety
of languages (including functional programming languages), but have
not heard the phrase "value polymorphism" before. So I am trying to
get /you/ to say what /you/ mean by it. Maybe it's just a name you
and a few other people use for pattern matching.
May be, 'polymorphism by type of return value' is what he has in mind?
On Sun, 29 Sep 2024 13:41:27 +0200
David Brown <[email protected]> wrote:
On 28/09/2024 17:47, [email protected] wrote:
On Sat, 28 Sep 2024 17:33:46 +0200
David Brown <[email protected]> gabbled:
On 28/09/2024 11:12, [email protected] wrote:
On Fri, 27 Sep 2024 18:24:11 +0200
Not sure how that differs with value polymorphism.
I explained that - look at my answer below for another attempt.
No you didn't because like Bonita you have no idea what value
polymorphism is.
The term "polymorphism" is used in reference to /types/, not
/values/. I am not new to programming, and have used a large variety
of languages (including functional programming languages), but have
not heard the phrase "value polymorphism" before. So I am trying to
get /you/ to say what /you/ mean by it. Maybe it's just a name you
and a few other people use for pattern matching.
May be, 'polymorphism by type of return value' is what he has in mind?
On 30/09/2024 09:26, [email protected] wrote:
On Sun, 29 Sep 2024 11:23:51 -0700
Tim Rentsch <[email protected]> boring babbled:
But of course we can't
be sure without knowing more about what language is intended.
It was a language used in
a specialist task, it wasn't made public.
Oh, that makes it /much/ clearer.
Time to drop this, I think.
Certainly that is an established kind of polymorphism, and pretty much
the only hit when googling "value polymorphism" is "return value >polymorphism". It is found in Ada, Haskell, and Rust (I think). You
can also do it in C++, at least to some extent, by making types with >conversion operators to different types. (It can be used in expression >templates, for example.)
But that does not at all fit with the examples he gave in his
"specialist" language, which was just pattern matching. It fits better
with the link he gave, though that was more like C++ template variables.
I think Tim's conclusion that Muttley is confused is more likely.
void func(int i = 1) { cout << "One\n"; }
On Mon, 30 Sep 2024 15:21:22 +0200
David Brown <[email protected]> boring babbled:
Certainly that is an established kind of polymorphism, and pretty
much the only hit when googling "value polymorphism" is "return
value polymorphism". It is found in Ada, Haskell, and Rust (I
think). You can also do it in C++, at least to some extent, by
making types with conversion operators to different types. (It can
be used in expression templates, for example.)
But that does not at all fit with the examples he gave in his
"specialist" language, which was just pattern matching. It fits
better with the link he gave, though that was more like C++ template >variables.
I think Tim's conclusion that Muttley is confused is more likely.
Please explain what you find so complicated about a polymorphic
function call being based on the VALUE of one or more parameters
passed to it rather than the type of parameter? Imagine in C++ if you
could do:
void func(int i = 1) { cout << "One\n"; }
void func(int i = 2) { cout << "Two\n"; }
void func(string s = "hello") { cout << "Hello!\n"; }
void func(int i) { cout << "Int default\n"; }
void func(string s) { cout << "String default\n"; }
int main()
{
for(int i=0;i < 5;++i) func(i);
func("hello");
func("world");
}
I'm sure even a thickheads like you and Rensch can figure how that
would run now it has your familiar comfy C++ syntax.
On Mon, 30 Sep 2024 15:21:22 +0200
David Brown <[email protected]> boring babbled:
Certainly that is an established kind of polymorphism, and pretty much
the only hit when googling "value polymorphism" is "return value
polymorphism". It is found in Ada, Haskell, and Rust (I think). You
can also do it in C++, at least to some extent, by making types with
conversion operators to different types. (It can be used in expression
templates, for example.)
But that does not at all fit with the examples he gave in his
"specialist" language, which was just pattern matching. It fits better
with the link he gave, though that was more like C++ template variables.
I think Tim's conclusion that Muttley is confused is more likely.
Please explain what you find so complicated about a polymorphic function
call being based on the VALUE of one or more parameters passed to it rather than the type of parameter?
Imagine in C++ if you could do:
void func(int i = 1) { cout << "One\n"; }
void func(int i = 2) { cout << "Two\n"; }
void func(string s = "hello") { cout << "Hello!\n"; }
void func(int i) { cout << "Int default\n"; }
void func(string s) { cout << "String default\n"; }
int main()
{
for(int i=0;i < 5;++i) func(i);
func("hello");
func("world");
}
I'm sure even a thickheads like you and Rensch can figure how that would run now it has your familiar comfy C++ syntax.
On Mon, 30 Sep 2024 15:21:22 +0200
David Brown <[email protected]> boring babbled:
Certainly that is an established kind of polymorphism, and pretty
much the only hit when googling "value polymorphism" is "return
value polymorphism". It is found in Ada, Haskell, and Rust (I
think). You can also do it in C++, at least to some extent, by
making types with conversion operators to different types. (It can
be used in expression templates, for example.)
But that does not at all fit with the examples he gave in his
"specialist" language, which was just pattern matching. It fits
better with the link he gave, though that was more like C++
template variables.
I think Tim's conclusion that Muttley is confused is more likely.
Please explain what you find so complicated about a polymorphic
function call being based on the VALUE of one or more parameters
passed to it rather than the type of parameter? Imagine in C++ if
you could do:
void func(int i = 1) { cout << "One\n"; }
void func(int i = 2) { cout << "Two\n"; }
void func(string s = "hello") { cout << "Hello!\n"; }
void func(int i) { cout << "Int default\n"; }
void func(string s) { cout << "String default\n"; }
int main()
{
for(int i=0;i < 5;++i) func(i);
func("hello");
func("world");
}
I'm sure even a thickheads like you and Rensch can figure how that
would run now it has your familiar comfy C++ syntax.
On Mon, 30 Sep 2024 16:00:02 -0000 (UTC)
[email protected] wrote:
void func(int i = 1) { cout << "One\n"; }
void func(int i = 2) { cout << "Two\n"; }
void func(string s = "hello") { cout << "Hello!\n"; }
void func(int i) { cout << "Int default\n"; }
void func(string s) { cout << "String default\n"; }
int main()
{
for(int i=0;i < 5;++i) func(i);
func("hello");
func("world");
}
I'm sure even a thickheads like you and Rensch can figure how that
would run now it has your familiar comfy C++ syntax.
You forgot to mention what you expect as an output.
Please explain what you find so complicated about a polymorphic
function call being based on the VALUE of one or more parameters
passed to it rather than the type of parameter? Imagine in C++ if
you could do:
void func(int i = 1) { cout << "One\n"; }
void func(int i = 2) { cout << "Two\n"; }
void func(string s = "hello") { cout << "Hello!\n"; }
void func(int i) { cout << "Int default\n"; }
void func(string s) { cout << "String default\n"; }
int main()
{
for(int i=0;i < 5;++i) func(i);
func("hello");
func("world");
}
I'm sure even a thickheads like you and Rensch can figure how that
would run now it has your familiar comfy C++ syntax.
Until you describe what you mean in terms of an existing, public
language, such as Haskell, and without any imaginary extensions, and
with an example that can be compiled as is and run successfully,
there is no reason to pay attention to anything you say.
On 30/09/2024 18:00, [email protected] wrote:
Please explain what you find so complicated about a polymorphic function
call being based on the VALUE of one or more parameters passed to it rather >> than the type of parameter?
Nobody finds the concept of pattern matching difficult to understand.
Nor is your private functional programming language example difficult to >follow - unless there is something non-obvious that we can't see,
because it is a private language whose details are unknown.
Remember, you are talking to people who are more knowledgeable than you
in C++ (since you have pretty much given up anything beyond C++98), and
they write code, and not understanding the concepts they use. Bonita's >knowledge of C++ far exceeds yours, and Tim's knowledge of many
programming languages and language paradigms exceeds that of most people
I have come across.
We know what the code you wrote does. We disagree that it is
"polymorphism", or that "value polymorphism" is anything but a rather
obscure term that has no wide-spread meaning.
Yes, it's obvious what you intend it to mean.
But that's not polymorphism.
Now you have pattern matching - a switch, with a default case. The
twist (and it might be an interesting feature in a programming language,
but I am not sure it would be worth having) is that you allow the
definition of "func" to be split up.
C++ already allows this for compile-time constants:
On Mon, 30 Sep 2024 19:43:43 +0300
Michael S <[email protected]> boring babbled:
On Mon, 30 Sep 2024 16:00:02 -0000 (UTC)
[email protected] wrote:
void func(int i = 1) { cout << "One\n"; }
void func(int i = 2) { cout << "Two\n"; }
void func(string s = "hello") { cout << "Hello!\n"; }
void func(int i) { cout << "Int default\n"; }
void func(string s) { cout << "String default\n"; }
int main()
{
for(int i=0;i < 5;++i) func(i);
func("hello");
func("world");
}
I'm sure even a thickheads like you and Rensch can figure how that
would run now it has your familiar comfy C++ syntax.
You forgot to mention what you expect as an output.
You seriously can't figure it out?
Am 01.10.2024 um 09:36 schrieb [email protected]:
Given his apparently inability to understand simple example code
I very much doubt that. He probably struggles with something like
SQL being so completely different to C++.
Do you mean me with "he" ? I've got a OCP-DBA for Oracle and I've
been a DBA two years.
You don't even try to read my code, telling that my code doesn't
make sense and it's too complicated.
Big deal. This is runtime, not compile time.
Tell me which popular language implements this "value-polymorphism".
It's a word Wikipedia doesn't know.
On 01/10/2024 10:00, Bonita Montero wrote:
Am 01.10.2024 um 09:36 schrieb [email protected]:
Given his apparently inability to understand simple example code
I very much doubt that. He probably struggles with something like
SQL being so completely different to C++.
Do you mean me with "he" ? I've got a OCP-DBA for Oracle and I've
been a DBA two years.
You don't even try to read my code, telling that my code doesn't
make sense and it's too complicated.
Big deal. This is runtime, not compile time.
Tell me which popular language implements this "value-polymorphism".
It's a word Wikipedia doesn't know.
Of course Wikipedia doesn't know it, since it is his own made-up
term. (Well, his and one other person.)
The word "polymorphism" means "multiple kinds" or "multiple shapes" -
it is a term for code that can handle a parameter that can be of many different types. So in C++ terms (since this is comp.lang.c++, even
though Muttley seems to think it is comp.lang.made-up-stuff), you can
have run-time polymorphism using pointers to base classes and virtual functions, and compile-time polymorphism using generics and templates.
What you can't have is "value" polymorphism - it's an oxymoron.
The
common term for code that can handle parameters taking multiple
different values is "function".
Am 01.10.2024 um 09:36 schrieb [email protected]:
Given his apparently inability to understand simple example code
I very much doubt that. He probably struggles with something like
SQL being so completely different to C++.
Do you mean me with "he" ? I've got a OCP-DBA for Oracle and I've
Am 01.10.2024 um 10:01 schrieb Michael S:
On Tue, 1 Oct 2024 07:31:51 -0000 (UTC)
[email protected] wrote:
On Mon, 30 Sep 2024 19:43:43 +0300
Michael S <[email protected]> boring babbled:
On Mon, 30 Sep 2024 16:00:02 -0000 (UTC)
[email protected] wrote:
void func(int i = 1) { cout << "One\n"; }
void func(int i = 2) { cout << "Two\n"; }
void func(string s = "hello") { cout << "Hello!\n"; }
void func(int i) { cout << "Int default\n"; }
void func(string s) { cout << "String default\n"; }
int main()
{
for(int i=0;i < 5;++i) func(i);
func("hello");
func("world");
}
I'm sure even a thickheads like you and Rensch can figure how that
would run now it has your familiar comfy C++ syntax.
You forgot to mention what you expect as an output.
You seriously can't figure it out?
No, I can't.
I see two possibilities and don't know which one was chosen by
definitions of your private language.
There's no output but may compiler errors because the code defines
three identical overloads for the int call and twi identical overloads
for the string call.
On Tue, 1 Oct 2024 07:31:51 -0000 (UTC)
[email protected] wrote:
On Mon, 30 Sep 2024 19:43:43 +0300
Michael S <[email protected]> boring babbled:
On Mon, 30 Sep 2024 16:00:02 -0000 (UTC)
[email protected] wrote:
void func(int i = 1) { cout << "One\n"; }
void func(int i = 2) { cout << "Two\n"; }
void func(string s = "hello") { cout << "Hello!\n"; }
void func(int i) { cout << "Int default\n"; }
void func(string s) { cout << "String default\n"; }
int main()
{
for(int i=0;i < 5;++i) func(i);
func("hello");
func("world");
}
I'm sure even a thickheads like you and Rensch can figure how that
would run now it has your familiar comfy C++ syntax.
You forgot to mention what you expect as an output.
You seriously can't figure it out?
No, I can't.
I see two possibilities and don't know which one was chosen by
definitions of your private language.
What you can't have is "value" polymorphism - it's an oxymoron. The
common term for code that can handle parameters taking multiple
different values is "function".
Muttley doesn't even seem to know what he means by the term. He has
The feature appears useful in 'pure functional' languages like C++
template sub-language. Of course, it is less useful, and probably not >desirable in mixed functional/imperative environments where you can
achieve the same result in several simpler ways.
On Tue, 1 Oct 2024 12:53:32 +0300
Michael S <[email protected]> boring babbled:
The feature appears useful in 'pure functional' languages like C++
template sub-language. Of course, it is less useful, and probably not >desirable in mixed functional/imperative environments where you can
achieve the same result in several simpler ways.
All polymorphism can be achieved in simpler/cruder ways. Its simply a syntatic nicety.
On Tue, 1 Oct 2024 10:42:06 +0200
David Brown <[email protected]> wrote:
On 01/10/2024 10:00, Bonita Montero wrote:
Am 01.10.2024 um 09:36 schrieb [email protected]:
Given his apparently inability to understand simple example code
I very much doubt that. He probably struggles with something like
SQL being so completely different to C++.
Do you mean me with "he" ? I've got a OCP-DBA for Oracle and I've
been a DBA two years.
You don't even try to read my code, telling that my code doesn't
make sense and it's too complicated.
Big deal. This is runtime, not compile time.
Tell me which popular language implements this "value-polymorphism".
It's a word Wikipedia doesn't know.
Of course Wikipedia doesn't know it, since it is his own made-up
term. (Well, his and one other person.)
The word "polymorphism" means "multiple kinds" or "multiple shapes" -
it is a term for code that can handle a parameter that can be of many
different types. So in C++ terms (since this is comp.lang.c++, even
though Muttley seems to think it is comp.lang.made-up-stuff), you can
have run-time polymorphism using pointers to base classes and virtual
functions, and compile-time polymorphism using generics and templates.
What you can't have is "value" polymorphism - it's an oxymoron.
I don't think so.
IMHO, C++ does have features that can be called compile-time value polymorphism without overstretching definition of the word
'polymorphism'.
You demonstrated it yourself few posts ago using relatively modern
C++ features.
But for class templates (as opposed to function
templates) something that can be reasonably called 'value polymorphism' existed for as long as C++ has templates (1990 ?).
The feature appears useful in 'pure functional' languages like C++
template sub-language.
Of course, it is less useful, and probably not
desirable in mixed functional/imperative environments where you can
achieve the same result in several simpler ways.
The
common term for code that can handle parameters taking multiple
different values is "function".
On Tue, 1 Oct 2024 10:42:06 +0200
David Brown <[email protected]> boring babbled:
What you can't have is "value" polymorphism - it's an oxymoron. The
No it isn't, your brain just can't understand the concept even with now
2 pieces of example code.
common term for code that can handle parameters taking multiple
different values is "function".
So C++ can differentiate a functions with the same name based on the
passed value at runtime can it? Not last time I checked.
Muttley doesn't even seem to know what he means by the term. He has
I know perfectly well what it means, you're just dim.
tl;dr
Am 01.10.2024 um 11:58 schrieb [email protected]:
Because its not valid C++! I was using modified C++ syntax to keep people >happy
since they couldn't figure out the syntax of the functional language I used.
Talk about real languages an not languages you've invented ad-hoc.
The code you've shown doesn't make sense at all.
On Tue, 1 Oct 2024 10:02:48 -0000 (UTC)
[email protected] wrote:
On Tue, 1 Oct 2024 12:53:32 +0300
Michael S <[email protected]> boring babbled:
The feature appears useful in 'pure functional' languages like C++
template sub-language. Of course, it is less useful, and probably not
desirable in mixed functional/imperative environments where you can
achieve the same result in several simpler ways.
All polymorphism can be achieved in simpler/cruder ways. Its simply a
syntatic nicety.
The point is that if your language is not 'pure functional' then those
other ways (if, switch) are not 'cruder'. They are finer.
The selection written as if/switch is both shorter and easier to follow
than selection based on 'value polymorphism'.
On 01/10/2024 12:01, [email protected] wrote:
On Tue, 1 Oct 2024 10:42:06 +0200
David Brown <[email protected]> boring babbled:
What you can't have is "value" polymorphism - it's an oxymoron. The
No it isn't, your brain just can't understand the concept even with now
2 pieces of example code.
I know exactly what you mean by your example. But it is not
"polymorphism" of any sort.
On Tue, 1 Oct 2024 13:11:41 +0300
Michael S <[email protected]> boring babbled:
On Tue, 1 Oct 2024 10:02:48 -0000 (UTC)
[email protected] wrote:
On Tue, 1 Oct 2024 12:53:32 +0300
Michael S <[email protected]> boring babbled:
The feature appears useful in 'pure functional' languages like C++
template sub-language. Of course, it is less useful, and probably
not desirable in mixed functional/imperative environments where
you can achieve the same result in several simpler ways.
All polymorphism can be achieved in simpler/cruder ways. Its
simply a syntatic nicety.
The point is that if your language is not 'pure functional' then
those other ways (if, switch) are not 'cruder'. They are finer.
The selection written as if/switch is both shorter and easier to
follow than selection based on 'value polymorphism'.
Personally I prefer seperate function names instead of overloaded
functions and I don't often like operator overloading but they're
part of C++. Presumably you feel the same way? Can't have it both
ways mate.
And best (for me, personally, YMMV) solution in pure functional
languges is to avoid programming in such languages.
Michael S <[email protected]> writes:
And best (for me, personally, YMMV) solution in pure functional
languges is to avoid programming in such languages.
What do you count as a pure functional language?
On Tue, 1 Oct 2024 16:13:29 -0000 (UTC)
[email protected] wrote:
Personally I prefer seperate function names instead of overloaded
functions and I don't often like operator overloading but they're
part of C++. Presumably you feel the same way? Can't have it both
ways mate.
I do feel the same way, but that's completely differrent issue.
The best C++ solution (actually, it is the best solution not just in C++
but in any language that is not pure functional) for your 'value >polimorphism' example is not separte function names, but a single
function with switch in the body.
And best (for me, personally, YMMV) solution in pure functional
languges is to avoid programming in such languages.
Am 01.10.2024 um 18:14 schrieb [email protected]:
On Tue, 1 Oct 2024 12:58:32 +0200
Bonita Montero <[email protected]> boring babbled:
Am 01.10.2024 um 11:58 schrieb [email protected]:
Because its not valid C++! I was using modified C++ syntax to keep people >>> happy
since they couldn't figure out the syntax of the functional language I >used.
Talk about real languages an not languages you've invented ad-hoc.
The code you've shown doesn't make sense at all.
You're little more than a dictionary. You know the C++ syntax inside
out but you don't really understand computer science concepts.
I've studied applied computer sciences in Germany / Iserlohn in the 90s.
Michael S <[email protected]> writes:
And best (for me, personally, YMMV) solution in pure functional
languges is to avoid programming in such languages.
What do you count as a pure functional language?
Callback functions are a C thing. In C++ we have functors and lambdas.
Over the years I have removed all ancient callback functions from my
code and replaced by functors or lambdas, so your "most common use case"
is not everybody's use case.
On Wed, 25 Sep 2024 17:33:41 +0200
Bonita Montero <[email protected]> boringly babbled:
Am 25.09.2024 um 17:32 schrieb [email protected]:
I understand both types of polymorphism, you nothing.
You don't have clue. ...
And you have your own definition of polymorphism.
Thanks for proving my point. A proper functional language has value polymorphism (which clearly you don't understand) as well as type polymorphism.
https://chrisdone.com/posts/value-polymorphism/
Thanks for proving my point. A proper functional language has value
polymorphism (which clearly you don't understand) as well as type
polymorphism.
https://chrisdone.com/posts/value-polymorphism/
Neither "value polymorphism" nor "polymorphism by value" is a
common term.
They mean different things.
Polymorphism by value was discussed in a 1993 paper by Xavier
Leroy.
Value polymorphism, as it is described in the cited web page, is
simply overloading of variables rather than functions.
(Overloading is often called "ad hoc polymorphism".)
Neither overloading of functions nor overloading of variables has
anything to do with functional programming. Overloading might be
that don't have facilities for polymorphism, as for example the
original Lisp.
On Fri, 27 Sep 2024 02:27:52 -0700
Tim Rentsch <[email protected]> boringly babbled:
[email protected] writes:
Thanks for proving my point. A proper functional language has value
polymorphism (which clearly you don't understand) as well as type
polymorphism.
https://chrisdone.com/posts/value-polymorphism/
Neither "value polymorphism" nor "polymorphism by value" is a
common term.
Neither overloading of functions nor overloading of variables has
anything to do with functional programming. Overloading might be
Without it many of the standard examples of functional programming simply won't run.
On 27/09/2024 13:11, [email protected] wrote:
On Fri, 27 Sep 2024 02:27:52 -0700<snip>
Tim Rentsch <[email protected]> boringly babbled:
[email protected] writes:
Thanks for proving my point. A proper functional language has value
polymorphism (which clearly you don't understand) as well as type
polymorphism.
https://chrisdone.com/posts/value-polymorphism/
Neither "value polymorphism" nor "polymorphism by value" is a
common term.
Neither overloading of functions nor overloading of variables has
anything to do with functional programming. Overloading might be
Without it many of the standard examples of functional programming simply
won't run.
Are you talking about what is more commonly referred to as pattern matching?
factorial 0 = 1
factorial n = n * factorial (n - 1)
That is not what is generally meant by polymorphism, it's just an
alternative (but often very neat) way to write a conditional :
On Fri, 27 Sep 2024 16:14:29 +0200
David Brown <[email protected]> boringly babbled:
On 27/09/2024 13:11, [email protected] wrote:
On Fri, 27 Sep 2024 02:27:52 -0700<snip>
Tim Rentsch <[email protected]> boringly babbled:
[email protected] writes:
Thanks for proving my point. A proper functional language has value >>>>> polymorphism (which clearly you don't understand) as well as type
polymorphism.
https://chrisdone.com/posts/value-polymorphism/
Neither "value polymorphism" nor "polymorphism by value" is a
common term.
Neither overloading of functions nor overloading of variables has
anything to do with functional programming. Overloading might be
Without it many of the standard examples of functional programming simply >>> won't run.
Are you talking about what is more commonly referred to as pattern matching?
I've already posted a link to an article about it. Whatever you want to call it, the polymorphic function called is based on the value passed to it, not the type.
factorial 0 = 1
factorial n = n * factorial (n - 1)
That is not what is generally meant by polymorphism, it's just an
alternative (but often very neat) way to write a conditional :
That applies to all types of polymorphism. All decisional code however its written eventually ends up as some kind of assembler cmp/jmp combination. Might be 2 opcodes, might be 1, doesn't matter, same thing.
On 27/09/2024 17:25, [email protected] wrote:
That applies to all types of polymorphism. All decisional code however its >> written eventually ends up as some kind of assembler cmp/jmp combination.
Might be 2 opcodes, might be 1, doesn't matter, same thing.
The key point of polymorphism - as I see it - is that the source code
doing the dispatching does not know the details of the possible matches
or targets. Thus C++ virtual methods support polymorphism - a function
call call "p->foo()" for "p" of type "T*" without knowing anything about
how many descendent types of "T" exist or how they different. All it
needs to know is that the base class "T" has a virtual method "foo".
This cannot be done with a series of cmp/jmp instructions - it ends up
as an indirect call instruction.
I am pretty confident that what you seem to be talking about (AFAIUI) is
not something many people would refer to as "polymorphism"
On 02/10/2024 04:06, Tim Rentsch wrote:
Michael S <[email protected]> writes:
And best (for me, personally, YMMV) solution in pure functional
languges is to avoid programming in such languages.
What do you count as a pure functional language?
F# (a.k.a F sharp)
User <[email protected]> writes:
On 02/10/2024 04:06, Tim Rentsch wrote:
Michael S <[email protected]> writes:
And best (for me, personally, YMMV) solution in pure functional
languges is to avoid programming in such languages.
What do you count as a pure functional language?
F# (a.k.a F sharp)
F# has mutable* variables, mutable* record fields, mutable arrays,
mutable hash tables, for loops, while loops, and no doubt other
imperative constructs such as I/O and sequencing (where the *
means mutable is an optional attribute).
On Tue, 01 Oct 2024 20:06:20 -0700
Tim Rentsch <[email protected]> wrote:
Michael S <[email protected]> writes:
And best (for me, personally, YMMV) solution in pure functional
languges is to avoid programming in such languages.
What do you count as a pure functional language?
C++ templates sub-languges. I.e. approximately compile-time part of
C++ taken in isolation from run-time part and without C++11 and later additions.
Michael S <[email protected]> writes:
On Tue, 01 Oct 2024 20:06:20 -0700
Tim Rentsch <[email protected]> wrote:
Michael S <[email protected]> writes:
And best (for me, personally, YMMV) solution in pure functional
languges is to avoid programming in such languages.
What do you count as a pure functional language?
C++ templates sub-languges. I.e. approximately compile-time part of
C++ taken in isolation from run-time part and without C++11 and
later additions.
If that's your only example then not very much is excluded.
It's like saying you don't want to program Turing Machines.
Are there any mainstream languages you would count as purely
functional? (I think it's fair to say that the C++ template
sub-language is not a mainstream language.)
On Fri, 04 Oct 2024 06:57:32 -0700
Tim Rentsch <[email protected]> wrote:
Michael S <[email protected]> writes:
On Tue, 01 Oct 2024 20:06:20 -0700
Tim Rentsch <[email protected]> wrote:
Michael S <[email protected]> writes:
And best (for me, personally, YMMV) solution in pure functional
languges is to avoid programming in such languages.
What do you count as a pure functional language?
C++ templates sub-languges. I.e. approximately compile-time part of
C++ taken in isolation from run-time part and without C++11 and
later additions.
If that's your only example then not very much is excluded.
Since I don't like functional programming it seems to me rather natural
that I am positioned as far as it goes from being an expert in the
field.
On Fri, 04 Oct 2024 06:57:32 -0700
Tim Rentsch <[email protected]> wrote:
Michael S <[email protected]> writes:
On Tue, 01 Oct 2024 20:06:20 -0700
Tim Rentsch <[email protected]> wrote:
Michael S <[email protected]> writes:
And best (for me, personally, YMMV) solution in pure functional
languges is to avoid programming in such languages.
What do you count as a pure functional language?
C++ templates sub-languges. I.e. approximately compile-time part of
C++ taken in isolation from run-time part and without C++11 and
later additions.
If that's your only example then not very much is excluded.
Since I don't like functional programming it seems to me rather
natural that I am positioned as far as it goes from being an
expert in the field.
So far I found only one field of computer programming that I like
even less - regular expressions.
Michael S <[email protected]> writes:
On Fri, 04 Oct 2024 06:57:32 -0700
Tim Rentsch <[email protected]> wrote:
Michael S <[email protected]> writes:
On Tue, 01 Oct 2024 20:06:20 -0700
Tim Rentsch <[email protected]> wrote:
Michael S <[email protected]> writes:
And best (for me, personally, YMMV) solution in pure functional
languges is to avoid programming in such languages.
What do you count as a pure functional language?
C++ templates sub-languges. I.e. approximately compile-time part
of C++ taken in isolation from run-time part and without C++11 and
later additions.
If that's your only example then not very much is excluded.
Since I don't like functional programming it seems to me rather
natural that I am positioned as far as it goes from being an
expert in the field.
Sorry, I think my comment came across wrongly. The point of my
question is not to criticize your reactions but to understand
just what things you are reacting to. Different people have
different ideas of what "functional programming" means. What
language qualities make a language one you would rather avoid?
Or conversely, what attributes tend to make a language attractive
or one that you would prefer?
So far I found only one field of computer programming that I like
even less - regular expressions.
I admit to being puzzled by this reaction. Regular expressions tend
not to be very useful outside of a rather narrow niche, but inside
their domain of application they are pretty handy. Are you saying
that you would rather not use regular expressions even in situations
where they are convenient?
Or is it that you don't find such cases
coming up in your regular programming activities?
I don't use lambda expressions as argument for 'compare' parameter in std::sort or std::nth_element. If it was possible, I wouldn't use
object of class with operator() for such parameter either. Instead I
would prefer pointer to normal named member function.
I prefer to see sequence of steps. Or, at least to have some idea about
it in 'as if' fashion.
On 07.10.2024 22:53, Michael S wrote:
I don't use lambda expressions as argument for 'compare' parameter
in std::sort or std::nth_element. If it was possible, I wouldn't use
object of class with operator() for such parameter either. Instead I
would prefer pointer to normal named member function.
I prefer to see sequence of steps. Or, at least to have some idea
about it in 'as if' fashion.
You can easily use a normal named member function for comparison, you
just need a small lambda for calling it.
#include <vector>
#include <algorithm>
#include <iostream>
class A {
public:
int key_;
int type_;
bool MyLess(const A& other) const {
return key_ < other.key_ || (key_ == other.key_ &&
type_ < other.type_); }
};
int main() {
std::vector<A> vv{ {12, 50}, {13,12}, {3,7}, {12, 49} };
std::sort(vv.begin(), vv.end(), [](const A& x, const A& y)
{return x.MyLess(y); });
for (A& ref : vv) {
std::cout << ref.key_ << " " << ref.type_ << "\n";
}
}
I like lambda because here I "see the sequence of steps". The earlier std::bind1st et al were horrific, never used them. Lambda is much
better.
On Tue, 8 Oct 2024 18:11:40 +0300
Paavo Helde <[email protected]> wrote:
On 07.10.2024 22:53, Michael S wrote:
I don't use lambda expressions as argument for 'compare' parameter
in std::sort or std::nth_element. If it was possible, I wouldn't use
object of class with operator() for such parameter either. Instead I
would prefer pointer to normal named member function.
I prefer to see sequence of steps. Or, at least to have some idea
about it in 'as if' fashion.
You can easily use a normal named member function for comparison, you
just need a small lambda for calling it.
#include <vector>
#include <algorithm>
#include <iostream>
class A {
public:
int key_;
int type_;
bool MyLess(const A& other) const {
return key_ < other.key_ || (key_ == other.key_ &&
type_ < other.type_); }
};
int main() {
std::vector<A> vv{ {12, 50}, {13,12}, {3,7}, {12, 49} };
std::sort(vv.begin(), vv.end(), [](const A& x, const A& y)
{return x.MyLess(y); });
for (A& ref : vv) {
std::cout << ref.key_ << " " << ref.type_ << "\n";
}
}
I like lambda because here I "see the sequence of steps". The earlier
std::bind1st et al were horrific, never used them. Lambda is much
better.
You has shown an easy case of context free comparison and how to avoid operator<() in this easy case.
I was talking about less trivial case of context-aware comparison and
about my desire to avoid operator().
Much better(IMHO) solution for your simple case was available since day
one of STL:
#include <algorithm>
#include <cstdio>
class A {
public:
int key_;
int type_;
static inline bool MyLess(const A& a, const A& b) {
return a.key_ < b.key_ || (a.key_ == b.key_ && a.type_
< b.type_); }
};
int main() {
A vv[] = { {12, 50}, {13,12}, {3,7}, {12, 49} };
std::sort(&vv[0], &vv[4], A::MyLess);
for (int i = 0; i < 4; ++i)
printf("%d %d\n", vv[i].key_, vv[i].type_);
}
Here is an example of more interesting sort that demonstrates what I was talking about and where I would like to use bwt_cmp_t::less directly
instead of via operator():
#include <cstdint>
#include <cstring>
#include <algorithm>
// BWT sort - sort in lexicographic order with circular shift
void bwt_sort(int* dst, const uint8_t* src, int srclen)
{
for (int i = 0; i < srclen; ++i)
dst[i] = i;
struct bwt_cmp_t {
const uint8_t* src;
int srclen;
bool less(int a, int b) const {
if (a >= b) {
if (a > b)
return !less(b, a);
return false; // a == b
}
// a < b;
int diff = memcmp(&src[a], &src[b], srclen-b);
if (diff == 0) {
diff = memcmp(&src[a+srclen-b], &src[0], b-a);
if (diff == 0) {
diff = memcmp(&src[0], &src[b-a], a);
if (diff == 0)
return true; // stable sort
}
}
return diff < 0;
}
bool operator() (int a, int b) const {
return less(a, b);
}
} cmp;
cmp.src = src;
cmp.srclen = srclen;
std::sort(dst, dst+srclen, cmp);
}
The context state is the bread and butter of lambda. If all you want
is to avoid operator(), this can be done easily:
On Wed, 9 Oct 2024 00:12:25 +0300
Paavo Helde <[email protected]> wrote:
The context state is the bread and butter of lambda. If all you want
is to avoid operator(), this can be done easily:
I want neither operator() nor lambda.
I want a way to feed std::sort with honest old [pointer to] object +
[pointer to] its honest old member function. And I am not obsessed
with idea that it has to be a single prameter at all cost; I see nothing wrong if two separate parameters needed to do the job.
Am 09.10.2024 um 07:25 schrieb Paavo Helde:
Not quite sure why you might want this, but here it goes:
// Ugly lambda hidden in a common header
namespace std2 {
template<class ELEM, class COMP>
void sort(ELEM* begin, ELEM* end, const COMP& comp, bool (COMP::* >> member)(ELEM, ELEM) const) {
std::sort(begin, end, [&comp, member](const ELEM& a, const >> ELEM& b) {return (comp.*member)(a, b); });
}
}
Use iterators !
On 09.10.2024 09:28, Bonita Montero wrote:
Am 09.10.2024 um 07:25 schrieb Paavo Helde:
Not quite sure why you might want this, but here it goes:
// Ugly lambda hidden in a common header
namespace std2 {
template<class ELEM, class COMP>
void sort(ELEM* begin, ELEM* end, const COMP& comp, bool (COMP::* >>> member)(ELEM, ELEM) const) {
std::sort(begin, end, [&comp, member](const ELEM& a, const
ELEM& b) {return (comp.*member)(a, b); });
}
}
Use iterators !
Iterators, concepts and any needed type deduction is left as an exercise
for the reader ;-)
On 09.10.2024 01:06, Michael S wrote:
On Wed, 9 Oct 2024 00:12:25 +0300
Paavo Helde <[email protected]> wrote:
The context state is the bread and butter of lambda. If all you
want is to avoid operator(), this can be done easily:
I want neither operator() nor lambda.
I want a way to feed std::sort with honest old [pointer to] object + [pointer to] its honest old member function. And I am not obsessed
with idea that it has to be a single prameter at all cost; I see
nothing wrong if two separate parameters needed to do the job.
Not quite sure why you might want this, but here it goes:
// Ugly lambda hidden in a common header
namespace std2 {
template<class ELEM, class COMP>
void sort(ELEM* begin, ELEM* end, const COMP& comp, bool
(COMP::* member)(ELEM, ELEM) const) {
std::sort(begin, end, [&comp, member](const ELEM& a, const
ELEM& b) {return (comp.*member)(a, b); });
}
}
// BWT sort - sort in lexicographic order with circular shift
void bwt_sort(int* dst, const uint8_t* src, int srclen) {
for (int i = 0; i < srclen; ++i)
dst[i] = i;
struct bwt_cmp_t {
const uint8_t* src;
int srclen;
bool less(int a, int b) const {
if (a >= b) {
if (a > b)
return !less(b, a);
return false; // a == b
}
// a < b;
int diff = memcmp(&src[a], &src[b], srclen - b);
if (diff == 0) {
diff = memcmp(&src[a + srclen - b], &src[0], b - a);
if (diff == 0) {
diff = memcmp(&src[0], &src[b - a], a);
if (diff == 0)
return true; // stable sort
}
}
return diff < 0;
}
} cmp;
cmp.src = src;
cmp.srclen = srclen;
// Usage without lambda as desired
std2::sort(dst, dst + srclen, cmp, &bwt_cmp_t::less);
}
On Wed, 9 Oct 2024 08:25:20 +0300
Paavo Helde <[email protected]> wrote:
On 09.10.2024 01:06, Michael S wrote:
On Wed, 9 Oct 2024 00:12:25 +0300
Paavo Helde <[email protected]> wrote:
The context state is the bread and butter of lambda. If all you
want is to avoid operator(), this can be done easily:
I want neither operator() nor lambda.
I want a way to feed std::sort with honest old [pointer to] object +
[pointer to] its honest old member function. And I am not obsessed
with idea that it has to be a single prameter at all cost; I see
nothing wrong if two separate parameters needed to do the job.
Not quite sure why you might want this, but here it goes:
// Ugly lambda hidden in a common header
namespace std2 {
template<class ELEM, class COMP>
void sort(ELEM* begin, ELEM* end, const COMP& comp, bool
(COMP::* member)(ELEM, ELEM) const) {
std::sort(begin, end, [&comp, member](const ELEM& a, const
ELEM& b) {return (comp.*member)(a, b); });
}
}
// BWT sort - sort in lexicographic order with circular shift
void bwt_sort(int* dst, const uint8_t* src, int srclen) {
for (int i = 0; i < srclen; ++i)
dst[i] = i;
struct bwt_cmp_t {
const uint8_t* src;
int srclen;
bool less(int a, int b) const {
if (a >= b) {
if (a > b)
return !less(b, a);
return false; // a == b
}
// a < b;
int diff = memcmp(&src[a], &src[b], srclen - b);
if (diff == 0) {
diff = memcmp(&src[a + srclen - b], &src[0], b - a);
if (diff == 0) {
diff = memcmp(&src[0], &src[b - a], a);
if (diff == 0)
return true; // stable sort
}
}
return diff < 0;
}
} cmp;
cmp.src = src;
cmp.srclen = srclen;
// Usage without lambda as desired
std2::sort(dst, dst + srclen, cmp, &bwt_cmp_t::less);
}
Unfortunately, it is not the same.
Unlike all previous variant, compiler implements this one via dynamic dispatch (indirect call).
At least that's what I see with gcc compiler both at -O2 and at -O3.
On 09.10.2024 13:58, Michael S wrote:
On Wed, 9 Oct 2024 08:25:20 +0300
Paavo Helde <[email protected]> wrote:
On 09.10.2024 01:06, Michael S wrote:
On Wed, 9 Oct 2024 00:12:25 +0300
Paavo Helde <[email protected]> wrote:
The context state is the bread and butter of lambda. If all you
want is to avoid operator(), this can be done easily:
I want neither operator() nor lambda.
I want a way to feed std::sort with honest old [pointer to]
object + [pointer to] its honest old member function. And I am
not obsessed with idea that it has to be a single prameter at all
cost; I see nothing wrong if two separate parameters needed to do
the job.
Not quite sure why you might want this, but here it goes:
// Ugly lambda hidden in a common header
namespace std2 {
template<class ELEM, class COMP>
void sort(ELEM* begin, ELEM* end, const COMP& comp, bool
(COMP::* member)(ELEM, ELEM) const) {
std::sort(begin, end, [&comp, member](const ELEM& a,
const ELEM& b) {return (comp.*member)(a, b); });
}
}
// BWT sort - sort in lexicographic order with circular shift
void bwt_sort(int* dst, const uint8_t* src, int srclen) {
for (int i = 0; i < srclen; ++i)
dst[i] = i;
struct bwt_cmp_t {
const uint8_t* src;
int srclen;
bool less(int a, int b) const {
if (a >= b) {
if (a > b)
return !less(b, a);
return false; // a == b
}
// a < b;
int diff = memcmp(&src[a], &src[b], srclen - b);
if (diff == 0) {
diff = memcmp(&src[a + srclen - b], &src[0], b -
a); if (diff == 0) {
diff = memcmp(&src[0], &src[b - a], a);
if (diff == 0)
return true; // stable sort
}
}
return diff < 0;
}
} cmp;
cmp.src = src;
cmp.srclen = srclen;
// Usage without lambda as desired
std2::sort(dst, dst + srclen, cmp, &bwt_cmp_t::less);
}
Unfortunately, it is not the same.
Unlike all previous variant, compiler implements this one via
dynamic dispatch (indirect call).
At least that's what I see with gcc compiler both at -O2 and at
-O3.
That's right, one reason for preferring functional style functors and
lambdas over bare function pointers is that they can be optimized
better by the compiler, in general. But beauty requires sacrifice, so
you have to live with that!
On Wed, 9 Oct 2024 14:39:34 +0300
Paavo Helde <[email protected]> wrote:
On 09.10.2024 13:58, Michael S wrote:
On Wed, 9 Oct 2024 08:25:20 +0300
Paavo Helde <[email protected]> wrote:
On 09.10.2024 01:06, Michael S wrote:
On Wed, 9 Oct 2024 00:12:25 +0300
Paavo Helde <[email protected]> wrote:
The context state is the bread and butter of lambda. If all you
want is to avoid operator(), this can be done easily:
I want neither operator() nor lambda.
I want a way to feed std::sort with honest old [pointer to]
object + [pointer to] its honest old member function. And I am
not obsessed with idea that it has to be a single prameter at all
cost; I see nothing wrong if two separate parameters needed to do
the job.
Not quite sure why you might want this, but here it goes:
// Ugly lambda hidden in a common header
namespace std2 {
template<class ELEM, class COMP>
void sort(ELEM* begin, ELEM* end, const COMP& comp, bool
(COMP::* member)(ELEM, ELEM) const) {
std::sort(begin, end, [&comp, member](const ELEM& a,
const ELEM& b) {return (comp.*member)(a, b); });
}
}
// BWT sort - sort in lexicographic order with circular shift
void bwt_sort(int* dst, const uint8_t* src, int srclen) {
for (int i = 0; i < srclen; ++i)
dst[i] = i;
struct bwt_cmp_t {
const uint8_t* src;
int srclen;
bool less(int a, int b) const {
if (a >= b) {
if (a > b)
return !less(b, a);
return false; // a == b
}
// a < b;
int diff = memcmp(&src[a], &src[b], srclen - b);
if (diff == 0) {
diff = memcmp(&src[a + srclen - b], &src[0], b -
a); if (diff == 0) {
diff = memcmp(&src[0], &src[b - a], a);
if (diff == 0)
return true; // stable sort
}
}
return diff < 0;
}
} cmp;
cmp.src = src;
cmp.srclen = srclen;
// Usage without lambda as desired
std2::sort(dst, dst + srclen, cmp, &bwt_cmp_t::less);
}
Unfortunately, it is not the same.
Unlike all previous variant, compiler implements this one via
dynamic dispatch (indirect call).
At least that's what I see with gcc compiler both at -O2 and at
-O3.
That's right, one reason for preferring functional style functors and
lambdas over bare function pointers is that they can be optimized
better by the compiler, in general. But beauty requires sacrifice, so
you have to live with that!
Here is a variant without sacrifice. It is less pretty at the call site
- too many template arguments.
The advantage of it that it works the same both with std::sort and for std::nth_element.
This variant uses normal function instead of member function. Not
because it is impossible to do the same with member, but because I am
not fluent with necessary syntax.
#include <cstdint>
#include <cstring>
#include <algorithm>
// Ugly template hidden in a common header
template < typename ELEM, typename Comparison_context,
bool Compare(Comparison_context,ELEM,ELEM) >
class TCompare3 {
const Comparison_context& m_context;
public:
TCompare3(const Comparison_context& a):m_context(a){};
bool operator() (ELEM a, ELEM b) {
return Compare(m_context, a, b);
}
};
// BWT sort - sort in lexicographic order with circular shift
void bwt_sort(int* dst, const uint8_t* src, int srclen)
{
for (int i = 0; i < srclen; ++i)
dst[i] = i;
struct bwt_cmp_t {
const uint8_t* src;
int srclen;
static
inline
bool less(bwt_cmp_t* obj, int a, int b) {
if (a >= b) {
if (a > b)
return !less(obj, b, a);
return false; // a == b
}
// a < b;
const uint8_t* src = obj->src;
const int srclen = obj->srclen;
int diff = memcmp(&src[a], &src[b], srclen - b);
if (diff == 0) {
diff = memcmp(&src[a + srclen - b], &src[0], b - a);
if (diff == 0) {
diff = memcmp(&src[0], &src[b - a], a);
if (diff == 0)
return true; // stable sort
}
}
return diff < 0;
}
} cmp;
cmp.src = src;
cmp.srclen = srclen;
std::sort(dst, dst + srclen,
TCompare3<int, bwt_cmp_t*, bwt_cmp_t::less>(&cmp));
}
The more I look at this, the more I like the lambda!
On Mon, 07 Oct 2024 09:11:29 -0700
Tim Rentsch <[email protected]> wrote:
Michael S <[email protected]> writes:
On Fri, 04 Oct 2024 06:57:32 -0700
Tim Rentsch <[email protected]> wrote:
Michael S <[email protected]> writes:
On Tue, 01 Oct 2024 20:06:20 -0700
Tim Rentsch <[email protected]> wrote:
Michael S <[email protected]> writes:
And best (for me, personally, YMMV) solution in pure
functional languges is to avoid programming in such
languages.
What do you count as a pure functional language?
C++ templates sub-languges. I.e. approximately compile-time
part of C++ taken in isolation from run-time part and without
C++11 and later additions.
If that's your only example then not very much is excluded.
Since I don't like functional programming it seems to me rather
natural that I am positioned as far as it goes from being an
expert in the field.
Sorry, I think my comment came across wrongly. The point of my
question is not to criticize your reactions but to understand
just what things you are reacting to. Different people have
different ideas of what "functional programming" means. What
language qualities make a language one you would rather avoid?
Since we are in C++ group, let's take examples from C++.
I never use std::for_each().
I don't use lambda expressions as argument for 'compare' parameter
in std::sort or std::nth_element. If it was possible, I wouldn't
use object of class with operator() for such parameter either.
Instead I would prefer pointer to normal named member function.
May be, it is possible, but I don't know the syntax. I hate
functional stuff pushed down my throat by <random> and would very
much prefer the same very useful functionality provided in other
way.
Or conversely, what attributes tend to make a language attractive
or one that you would prefer?
I prefer to see sequence of steps. Or, at least to have some idea
about it in 'as if' fashion.
On Wed, 9 Oct 2024 00:12:25 +0300
Paavo Helde <[email protected]> wrote:
The context state is the bread and butter of lambda. If all you
want is to avoid operator(), this can be done easily:
I want neither operator() nor lambda.
I want a way to feed std::sort with honest old [pointer to] object
+ [pointer to] its honest old member function. And I am not
obsessed with idea that it has to be a single prameter at all
cost; I see nothing wrong if two separate parameters needed to do
the job.
Michael S <[email protected]> writes:
On Wed, 9 Oct 2024 00:12:25 +0300
Paavo Helde <[email protected]> wrote:
The context state is the bread and butter of lambda. If all you
want is to avoid operator(), this can be done easily:
I want neither operator() nor lambda.
I want a way to feed std::sort with honest old [pointer to] object
+ [pointer to] its honest old member function. And I am not
obsessed with idea that it has to be a single prameter at all
cost; I see nothing wrong if two separate parameters needed to do
the job.
I think I understand your position. Without meaning to challenge
that, I would like to offer my perspective on the situation.
There are several ways a "stateful function" argument might be
provided; some examples:
pointer to function, along with either a separate context
pointer or a bundled context/function pointer (illustrated
in a recent post in comp.lang.c)
having a parameter type of pointer to abstract superclass,
with a virtual function to accommodate caller-specific
functionality (not sure if a separate pointer-to-member
argument makes sense here)
a pointer-to templated class parameter, using either a
fixed member function name or a separate pointer-to-member
argument
a lambda (which could be either a stateful lambda or a
pure lambda along with a separate context argument)
To me all of these choices are conceptually pretty much the same,
having only minor differences in the details. A plausible
analogy is comparing for() and while(), or if-then-else and
switch().
Because I don't see much difference between them, what I want is
whatever makes it most convenient for the caller. Usually "most
convenient" means the choice that uses the least code in the
caller, although that might be affected by how clunky the code
is for the different cases.
Of course which choice is most convenient depends on what is
going on in the caller. In some cases a lambda is easiest;
in other cases a templated class parameter with a separate
pointer-to-member could be better. Which choice or choices
should be accommodated depends on what the expected use cases
are. The guiding principle remains What offers the easiest
and best fit in the caller (or callers plural)?
So for what it's worth there is my approach to the question.
On Fri, 11 Oct 2024 04:58:29 -0700
Tim Rentsch <[email protected]> wrote:
Michael S <[email protected]> writes:
On Wed, 9 Oct 2024 00:12:25 +0300
Paavo Helde <[email protected]> wrote:
The context state is the bread and butter of lambda. If all you
want is to avoid operator(), this can be done easily:
I want neither operator() nor lambda.
I want a way to feed std::sort with honest old [pointer to] object
+ [pointer to] its honest old member function. And I am not
obsessed with idea that it has to be a single prameter at all
cost; I see nothing wrong if two separate parameters needed to do
the job.
I think I understand your position. Without meaning to challenge
that, I would like to offer my perspective on the situation.
There are several ways a "stateful function" argument might be
provided; some examples:
pointer to function, along with either a separate context
pointer or a bundled context/function pointer (illustrated
in a recent post in comp.lang.c)
having a parameter type of pointer to abstract superclass,
with a virtual function to accommodate caller-specific
functionality (not sure if a separate pointer-to-member
argument makes sense here)
a pointer-to templated class parameter, using either a
fixed member function name or a separate pointer-to-member
argument
a lambda (which could be either a stateful lambda or a
pure lambda along with a separate context argument)
To me all of these choices are conceptually pretty much the same,
having only minor differences in the details. A plausible
analogy is comparing for() and while(), or if-then-else and
switch().
Because I don't see much difference between them, what I want is
whatever makes it most convenient for the caller. Usually "most
convenient" means the choice that uses the least code in the
caller, although that might be affected by how clunky the code
is for the different cases.
Of course which choice is most convenient depends on what is
going on in the caller. In some cases a lambda is easiest;
in other cases a templated class parameter with a separate
pointer-to-member could be better. Which choice or choices
should be accommodated depends on what the expected use cases
are. The guiding principle remains What offers the easiest
and best fit in the caller (or callers plural)?
So for what it's worth there is my approach to the question.
I am looking at it from the point that one of the main promises
of functionality supplied by <algorithm> header is zero cost of
abstraction.
For me it means that comparison routine can be inlined by compiler
when appropriate and called with direct call otherwise. Experiments presented in other posts of this thread demonstrate that [current
crop of major] compilers can't do it with your solution #1.
It seems plausible to assume, even without experiments, that they
can't do it with your solution # 2.
For solution #4 it depends on details. [...]
Solution #3 sort of works,
but too many details, most critically
types of parameters in comparison operations, have to be specified
on the call site. I.e. solution #3 does not satisfy DRY principle,
which is no less important than zero cost of abstraction.
At the end, the original way (class with operator() as a glue) is
practically the best, even if for my taste it feels "too functional".
I consider this outcome as defect in STL API definitions. [...]
Here is an example of more interesting sort that demonstrates what
I was talking about and where I would like to use bwt_cmp_t::less
directly instead of via operator():
#include <cstdint>
#include <cstring>
#include <algorithm>
// BWT sort - sort in lexicographic order with circular shift
void bwt_sort(int* dst, const uint8_t* src, int srclen)
{
for (int i = 0; i < srclen; ++i)
dst[i] = i;
struct bwt_cmp_t {
const uint8_t* src;
int srclen;
bool less(int a, int b) const {
if (a >= b) {
if (a > b)
return !less(b, a);
return false; // a == b
}
// a < b;
int diff = memcmp(&src[a], &src[b], srclen-b);
if (diff == 0) {
diff = memcmp(&src[a+srclen-b], &src[0], b-a);
if (diff == 0) {
diff = memcmp(&src[0], &src[b-a], a);
if (diff == 0)
return true; // stable sort
}
}
return diff < 0;
}
bool operator() (int a, int b) const {
return less(a, b);
}
} cmp;
cmp.src = src;
cmp.srclen = srclen;
std::sort(dst, dst+srclen, cmp);
}
| Sysop: | Keyop |
|---|---|
| Location: | Huddersfield, West Yorkshire, UK |
| Users: | 715 |
| Nodes: | 16 (2 / 14) |
| Uptime: | 157:10:03 |
| Calls: | 12,093 |
| Calls today: | 1 |
| Files: | 15,000 |
| Messages: | 6,517,750 |