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

What a bug

98 views
Skip to first unread message

Bonita Montero

unread,
Sep 1, 2023, 1:35:30 PM9/1/23
to
I checked for some commandline options and I thought it would be the
best to map that through an unordered_map. I extracted this from my
application in the below code.

#include <Windows.h>
#include <iostream>
#include <variant>
#include <unordered_map>

using namespace std;

int wmain( int argc, wchar_t **argv )
{
static unordered_map<wchar_t const *, DWORD>
const opts =
{
{ L"--idle", IDLE_PRIORITY_CLASS },
{ L"--below", BELOW_NORMAL_PRIORITY_CLASS },
{ L"--above", ABOVE_NORMAL_PRIORITY_CLASS },
{ L"--high", HIGH_PRIORITY_CLASS },
{ L"--realtime", REALTIME_PRIORITY_CLASS }
};
auto mappedPrio = opts.find( "--high" );
if( mappedPrio == opts.end() )
return -1;
return 0;
}

Why does my code have a bug and why doesn't mappedPrio
point to end() at the end?

Alf P. Steinbach

unread,
Sep 1, 2023, 5:09:39 PM9/1/23
to
How did you get that to compile?

With the compilation-spoiler fixed and the `-1` replaced with either
`E_FAIL` or `EXIT_FAILURE` (for the latter include `<stdlib.h>`), the
problem you're /probably/ talking about is why it doesn't work with your
setup to compare pointers to string literals. That's because the Holy
Standard does not require string pooling. An easy fix is to use
`wstring_view` as key (for that include `<string_view>`).

- Alf

Chris M. Thomasson

unread,
Sep 1, 2023, 5:50:25 PM9/1/23
to
Try something like:
___________________________
#include <iostream>
#include <unordered_map>
#include <string>
#include <cstdlib>


int main()
{
static std::unordered_map<std::string, long> opts = {
{"--idle", 0 },
{"--below", 1 },
{"--above", 2 },
{"--high", 3 },
{"--realtime", 4 }
};

auto mapped_opt = opts.find("--realtime");

if(mapped_opt == opts.end())
{
std::cout << "ahhh, shit!!!" << std::endl;

return EXIT_FAILURE;
}

std::cout << mapped_opt->first << ", " << mapped_opt->second <<
std::endl;

return EXIT_SUCCESS;
}
___________________________

?

Chris M. Thomasson

unread,
Sep 1, 2023, 5:52:48 PM9/1/23
to
Yup.

Bonita Montero

unread,
Sep 2, 2023, 12:20:02 AM9/2/23
to
Am 01.09.2023 um 23:50 schrieb Chris M. Thomasson:

> Try something like:

I know that I could have done that easier, but I wanted to know
where's the bug and why my example does find the key value anyway.

> ___________________________
> #include <iostream>
> #include <unordered_map>
> #include <string>
> #include <cstdlib>
>
>
> int main()
> {
>     static std::unordered_map<std::string, long> opts = {
>       {"--idle", 0 },
>       {"--below", 1 },
>       {"--above", 2 },
>       {"--high", 3 },
>       {"--realtime", 4 }
>     };
>
>     auto mapped_opt = opts.find("--realtime");

The problem here is that each find creates a string-object,
which is rather slow.

Bonita Montero

unread,
Sep 2, 2023, 12:23:18 AM9/2/23
to
Am 01.09.2023 um 23:09 schrieb Alf P. Steinbach:

> the problem you're /probably/ talking about is why it doesn't work with your
> setup to compare pointers to string literals. That's because the Holy
> Standard does not require string pooling. An easy fix is to use
> `wstring_view` as key (for that include `<string_view>`).

Exactly, that's what I did afterwards. But I first thought
that my code works because the linker has joined my both
"--high" strings to the same address.

Mut...@dastardlyhq.com

unread,
Sep 2, 2023, 6:25:37 AM9/2/23
to
There's nothing in the standard saying that should be the case. I thought
you were a C++ god?

Bonita Montero

unread,
Sep 2, 2023, 6:57:14 AM9/2/23
to
No, but also nothing that forbids that.
And actually the linkers do that.

Try this at home:

#include <iostream>

int main()
{
std::cout << ("xxx" == "xxx") << std::endl;
}

Although MSVC joined the strings in my first example it doesnt
do that in this example. Bug g++ and clang++ do join the strings.

> I thought you were a C++ god?

Sounds you are not.

Bonita Montero

unread,
Sep 2, 2023, 8:36:10 AM9/2/23
to
Am 02.09.2023 um 12:56 schrieb Bonita Montero:

> No, but also nothing that forbids that.
> And actually the linkers do that.
>
> Try this at home:
>
> #include <iostream>
>
> int main()
> {
>     std::cout << ("xxx" == "xxx") << std::endl;
> }

I found a small imcompatibility between MSVC's cl and "MSVC's" clang-cl:

C:\Users\Boni>cl -Ox -MD join.cpp -EHs
...
C:\Users\Boni>join
0
C:\Users\Boni>clang-cl -Ox -MD join.cpp -EHs
...
C:\Users\Boni>join
1

Maybe someone relies on the uniqueness or on the joining.

Bonita Montero

unread,
Sep 2, 2023, 8:41:44 AM9/2/23
to
Am 02.09.2023 um 14:35 schrieb Bonita Montero:

> I found a small imcompatibility between MSVC's cl and "MSVC's" clang-cl:
>
> C:\Users\Boni>cl -Ox -MD join.cpp -EHs
> ...
> C:\Users\Boni>join
> 0
> C:\Users\Boni>clang-cl -Ox -MD join.cpp -EHs
> ...
> C:\Users\Boni>join
> 1
>
> Maybe someone relies on the uniqueness or on the joining.

I found that there is a commandline-option of MSVC and clang-cl that
directs the compiler to join strings with -GF or not with -GF-. For
MSVC the default is not to join the strings, for clang-cl the default
is to join the strings.

Mut...@dastardlyhq.com

unread,
Sep 2, 2023, 11:30:55 AM9/2/23
to
On Sat, 2 Sep 2023 12:56:58 +0200
Bonita Montero <Bonita....@gmail.com> wrote:
>Am 02.09.2023 um 12:25 schrieb Mut...@dastardlyhq.com:
>
>> On Sat, 2 Sep 2023 06:23:02 +0200
>> Bonita Montero <Bonita....@gmail.com> wrote:
>
>>> Exactly, that's what I did afterwards. But I first thought
>>> that my code works because the linker has joined my both
>>> "--high" strings to the same address.
>
>> There's nothing in the standard saying that should be the case.
>
>No, but also nothing that forbids that.

There's nothing forbidding a lot of things but relying on them always being
the case would lead to some interesting bugs when the code is compiled on
a compiler that uses a different approach.

Bonita Montero

unread,
Sep 2, 2023, 12:10:43 PM9/2/23
to
Am 02.09.2023 um 17:30 schrieb Mut...@dastardlyhq.com:

> There's nothing forbidding a lot of things but relying on them always being
> the case would lead to some interesting bugs when the code is compiled on
> a compiler that uses a different approach.

I just wanted to verify what I did wrong with my unordered map, having
a wchar_t-pointer as the key, and accidentally MSVC joined the strings
and the key actually was found because the keys shared the addresses.
So I this was all accidentally and I didn't rely on anything.
But if you're stuck with MS cl or clang-cl and you use the -GF(-) switch
correctly - as I said both have different defaults - you could even rely
on that anyway if you're writing a Windows application.

jak

unread,
Sep 2, 2023, 5:15:32 PM9/2/23
to
Bonita Montero ha scritto:
HI,
you just need to use wchar_t in your search string as well:

from:
auto mappedPrio = opts.find( "--high" );
to:
auto mappedPrio = opts.find( L"--high" );


jak

unread,
Sep 3, 2023, 5:17:33 PM9/3/23
to
jak ha scritto:
mmm. This only works because the compiler uses the same area of memory
as the strings are equal and therefore compares a pointer to itself when
it finds the string. In fact if I replace this:

auto mappedPrio = opts.find( L"--high" );

with this other:

wchar_t what[] = L"--high";
auto mappedPrio = opts.find( what );

the search fails.

I find this sneaky. In my opinion the compiler shouldn't treat two
objects as the same just because they have the same content.

Chris M. Thomasson

unread,
Sep 3, 2023, 6:25:58 PM9/3/23
to
Right. Afaict, there is a way to create the strings on the stack and use
a little adapter logic to make it work with std::unordered_map. So, no
dynamic allocation.

Keith Thompson

unread,
Sep 3, 2023, 8:37:37 PM9/3/23
to
The find() function of unordered_map<> compares keys for *equality*.

Since the code defines the key type as `wchar_t const *`, it does
pointer comparison, which has all the usual problems when you apply it
to string literals; L"foo" and L"foo" may or may not be stored at the
same memory location, and L"foo" and a string L"foo" derived from user
input will certainly not have the same address.

You can specify an equality function if you like.

A far simpler solution is to use std::wstring as the key type.

--
Keith Thompson (The_Other_Keith) Keith.S.T...@gmail.com
Will write code for food.
void Void(void) { Void(); } /* The recursive call of the void */

Bonita Montero

unread,
Sep 3, 2023, 10:53:29 PM9/3/23
to
Am 04.09.2023 um 00:25 schrieb Chris M. Thomasson:

> Right. Afaict, there is a way to create the strings on the stack and use
> a little adapter logic to make it work with std::unordered_map. So, no
> dynamic allocation.

You can simply have a string_view as suggested in this thread.

Chris M. Thomasson

unread,
Sep 3, 2023, 11:01:54 PM9/3/23
to
A view into strings stored on the stack.

Bonita Montero

unread,
Sep 4, 2023, 12:59:38 AM9/4/23
to
Am 04.09.2023 um 05:01 schrieb Chris M. Thomasson:

> A view into strings stored on the stack.

string_view is just a pointer and a size_t or two pointers.
And string_view is often faster since its constructor is
constexpr. If you do a string_view( "hello world" ).length()
that's all compile-time evaluated.

Bonita Montero

unread,
Sep 4, 2023, 5:12:13 AM9/4/23
to
Am 04.09.2023 um 06:59 schrieb Bonita Montero:

> string_view is just a pointer and a size_t or two pointers.
> And string_view is often faster since its constructor is
> constexpr. If you do a string_view( "hello world" ).length()
> that's all compile-time evaluated.


#include <string_view>

size_t fn()
{
return std::string_view( "hello world" ).length();
}

MSVC:

?fn@@YA_KXZ PROC
mov eax, 11
ret 0
?fn@@YA_KXZ ENDP

clang++:

_Z2fnv:
movl $11, %eax
retq

g++:

_Z2fnv:
movl $11, %eax
ret


Scott Lurndal

unread,
Sep 4, 2023, 10:25:11 AM9/4/23
to
Or just use getopt_long() with UTF-8 strings.

Pavel

unread,
Sep 4, 2023, 6:44:22 PM9/4/23
to
If you cared about the speed, why did you use unordered_map?

red floyd

unread,
Sep 4, 2023, 8:37:12 PM9/4/23
to
Yeah. For something done exactly once, with 5 entries, best bet is a
std::array.


Chris M. Thomasson

unread,
Sep 4, 2023, 9:49:12 PM9/4/23
to
Check this out, my little experiment with stacks, used in a per-thread
allocator:

https://pastebin.com/raw/f37a23918

https://groups.google.com/g/comp.lang.c/c/7oaJFWKVCTw/m/sSWYU9BUS_QJ

Bonita Montero

unread,
Sep 4, 2023, 11:50:11 PM9/4/23
to
Am 05.09.2023 um 00:43 schrieb Pavel:

> If you cared about the speed, why did you use unordered_map?

My original example wasn't about speed. And the map is static, so it's
initialized thread-safe on the first run. But if I store strings in the
map, a string object is created with every lookup with a string literal,
which may result in a memory allocation. Depending on the case, this may
be too slow.

jak

unread,
Sep 5, 2023, 5:01:27 AM9/5/23
to
Keith Thompson ha scritto:
HI,
I probably expressed myself badly. I agree with what you answered and
everything is clear to me. What I am contesting is the part where you
say "...may or may not be stored at the same memory location" because
that would mean that a program like this:

#include <iostream>

int main()
{
auto eif = [] (bool t) {
std::cout << ( t ? "equal" : "not equal" ) << std::endl;
};

char const *sp = "hello";
char sl[] = "hello";

eif( sp == sp );

eif( "hello" == sp );

eif( "hello" == "hello" );

eif( "hello" == sl );

return 0;
}

which has this as a result:

equal
equal
equal
not equal

could give different results depending on how the compiler acts when
deciding where to store strings (because here we are comparing
pointers). I simply think that different objects shouldn't be lumped
together just because they contain the same things, otherwise I would
have declared a single string and would have used its pointer around the
code. Pointers are for that too. it is my opinion that the program I
posted should have this as a result:

equal
not equal
not equal
not equal

Bonita Montero

unread,
Sep 5, 2023, 5:12:01 AM9/5/23
to
Am 04.09.2023 um 02:37 schrieb Keith Thompson:

> A far simpler solution is to use std::wstring as the key type.

A string_view is much more perfromant and allows to reference
external strings - if applicable.


Bonita Montero

unread,
Sep 5, 2023, 5:13:13 AM9/5/23
to
Try to make s1 static const, then it may be joined.

Keith Thompson

unread,
Sep 5, 2023, 6:05:04 AM9/5/23
to
jak <nos...@please.ty> writes:
[...]
> I probably expressed myself badly. I agree with what you answered and
> everything is clear to me. What I am contesting is the part where you
> say "...may or may not be stored at the same memory location" because
> that would mean that a program like this:
>
> #include <iostream>
>
> int main()
> {
> auto eif = [] (bool t) {
> std::cout << ( t ? "equal" : "not equal" ) << std::endl;
> };
>
> char const *sp = "hello";
> char sl[] = "hello";
>
> eif( sp == sp );
>
> eif( "hello" == sp );
>
> eif( "hello" == "hello" );
>
> eif( "hello" == sl );
>
> return 0;
> }
>
> which has this as a result:
>
> equal
> equal
> equal
> not equal
>
> could give different results depending on how the compiler acts when
> deciding where to store strings (because here we are comparing
> pointers).

Yes, that's exactly what it means.

> I simply think that different objects shouldn't be lumped
> together just because they contain the same things, otherwise I would
> have declared a single string and would have used its pointer around the
> code. Pointers are for that too. it is my opinion that the program I
> posted should have this as a result:
>
> equal
> not equal
> not equal
> not equal

I suggest the language standard is more relevant than either of our
opinions.

C++17 5.13.5 [lex.string] paragraph 16:

Evaluating a *string-literal* results in a string literal
object with static storage duration, initialized from the given
characters as specified above. Whether all string literals are
distinct (that is, are stored in nonoverlapping objects) and
whether successive evaluations of a *string-literal* yield the
same or a different object is unspecified. [ Note: The effect
of attempting to modify a string literal is undefined. — end
note ]

I believe that all versions of C and C++ have similar rules.

Which means that ("hello" == "hello") may be either true or false, and
an implementation is not required to document which.

In your program above, this:
sp == sp
is guaranteed to be true, these:
"hello" == sp
"hello" == "hello"
may be either true or false, and this:
"hello" == sl
is false because sl is distinct and explicitly defined array object.

If you compile your program with clang++, it prints warnings on all four
comparisons.

red floyd

unread,
Sep 5, 2023, 11:54:03 PM9/5/23
to
On 9/3/2023 2:17 PM, jak wrote:

>> you just need to use wchar_t in your search string as well:
>>
>> from:
>> auto mappedPrio = opts.find( "--high" );
>> to:
>> auto mappedPrio = opts.find( L"--high" );
>>
>>
>
> mmm. This only works because the compiler uses the same area of memory
> as the strings are equal and therefore compares a pointer to itself when
> it finds the string. In fact if I replace this:
>

No, I believe that std::unordered_map has a specialization for both
const char* and const wchar_t*.


Keith Thompson

unread,
Sep 6, 2023, 12:15:11 AM9/6/23
to
I don't believe that's correct. Do you have a reference?

Bonita Montero

unread,
Sep 6, 2023, 12:49:23 AM9/6/23
to
Am 06.09.2023 um 05:53 schrieb red floyd:

> No, I believe that std::unordered_map has a specialization
> for both const char* and const wchar_t*.

Would be nice but actually this isn't true.

Pavel

unread,
Sep 6, 2023, 12:00:40 PM9/6/23
to
Bonita Montero wrote:
> Am 05.09.2023 um 00:43 schrieb Pavel:
>
>> If you cared about the speed, why did you use unordered_map?
>
> My original example wasn't about speed. And the map is static, so it's
> initialized thread-safe on the first run. But if I store strings in the
> map, a string object is created with every lookup with a string literal,
It does not have to be created since C++ 20.

Bonita Montero

unread,
Sep 6, 2023, 1:46:44 PM9/6/23
to
Am 06.09.2023 um 18:00 schrieb Pavel:

> Bonita Montero wrote:

>> My original example wasn't about speed. And the map is static, so it's
>> initialized thread-safe on the first run. But if I store strings in the
>> map, a string object is created with every lookup with a string literal,

> It does not have to be created since C++ 20.

The string-object is created for every lookup with a string-literal
if the key is also a string-object. That has nothing to do with C++20.
The thread-safe initialization of the map, which is usually done with
double-checked locking, is guaranteed since C++11.

Pavel

unread,
Sep 6, 2023, 2:45:10 PM9/6/23
to
Bonita Montero wrote:
> Am 06.09.2023 um 18:00 schrieb Pavel:
>
>> Bonita Montero wrote:
>
>>> My original example wasn't about speed. And the map is static, so it's
>>> initialized thread-safe on the first run. But if I store strings in the
>>> map, a string object is created with every lookup with a string literal,
>
>> It does not have to be created since C++ 20.
>
> The string-object is created for every lookup with a string-literal
> if the key is also a string-object.
False. Read the standard.

> That has nothing to do with C++20.
False. Read the standard, e.g. how to use

template<class Key,
class T,
class Hash = hash<Key>,
class Pred = equal_to<Key>,
class Allocator = allocator<pair<const Key, T>>>
class unordered_map {
template<class K> iterator find(const K& k);
template<class K> const_iterator find(const K& k) const;
};

jak

unread,
Sep 6, 2023, 7:18:42 PM9/6/23
to
Keith Thompson ha scritto:
Thanks for sending the paragraph. Unfortunately I had only sought it
just before your post.
However, it is obvious that it prints the warnings, I extracted the test
intentionally from the function/method, because otherwise the compiler
does not recognize the UB. If I modified my program in this way:

$ cat ./pp.cpp
#include <iostream>

int main()
{
auto eif = [] (char const *a, char const *b) {
std::cout << ( ( a == b ) ? "equal" : "not equal" ) << std::endl;
};
char const *sp = "hello";
char sl[] = "hello";

eif( sp, sp );
eif( "hello", sp );
eif( "hello", "hello" );
eif( "hello", sl );

return 0;
}

$ g++ -Wall -Wpedantic -Werror -std=c++17 ./pp.cpp -o ./pp

$ ./pp
equal
equal
equal
not equal

$ _

The warnings disappear but the UB remain. For this reason I think that
making objects unique because they have the same content is a bad
practice. Furthermore, the fact that it may happen or not makes the fact
even worse.


red floyd

unread,
Sep 6, 2023, 8:52:10 PM9/6/23
to
On 9/5/2023 9:14 PM, Keith Thompson wrote:
> red floyd <no.spa...@its.invalid> writes:
>> On 9/3/2023 2:17 PM, jak wrote:
>>>> you just need to use wchar_t in your search string as well:
>>>>
>>>> from:
>>>> auto mappedPrio = opts.find( "--high" );
>>>> to:
>>>> auto mappedPrio = opts.find( L"--high" );
>>>>
>>> mmm. This only works because the compiler uses the same area of
>>> memory
>>> as the strings are equal and therefore compares a pointer to itself when
>>> it finds the string. In fact if I replace this:
>>
>> No, I believe that std::unordered_map has a specialization for both
>> const char* and const wchar_t*.
>
> I don't believe that's correct. Do you have a reference?
>

Isn't it based on std::less<>?

Just checked. My mistake, forget I said anything.


Bonita Montero

unread,
Sep 6, 2023, 10:51:32 PM9/6/23
to
Am 06.09.2023 um 20:44 schrieb Pavel:

> Bonita Montero wrote:

>> The string-object is created for every lookup with a string-literal
>> if the key is also a string-object.

> False. Read the standard.

Quote the standard.

>> That has nothing to do with C++20.

> False. Read the standard, e.g. how to use

Quote the standard.

> template<class Key,
> class T,
> class Hash = hash<Key>,
> class Pred = equal_to<Key>,
> class Allocator = allocator<pair<const Key, T>>>
> class unordered_map {
> template<class K> iterator find(const K& k);
> template<class K> const_iterator find(const K& k) const;
> };

Theres a templated K-parameter for the loookup, but internally
this parameter needs to be converted to a string-object to be
hashed and to be equality-comparable the same way.
In theory there might be overloaded specializations that take
a string_view or whatever; but that's ratther unlikely.

Pavel

unread,
Sep 6, 2023, 11:54:57 PM9/6/23
to
Bonita Montero wrote:
> Am 06.09.2023 um 20:44 schrieb Pavel:
>
>> Bonita Montero wrote:
>
>>> The string-object is created for every lookup with a string-literal
>>> if the key is also a string-object.
>
>> False. Read the standard.
>
> Quote the standard.
>
>>> That has nothing to do with C++20.
>
>> False. Read the standard, e.g. how to use
>
> Quote the standard.
>
>> template<class Key,
>> class T,
>> class Hash = hash<Key>,
>> class Pred = equal_to<Key>,
>> class Allocator = allocator<pair<const Key, T>>>
>> class unordered_map {
>> template<class K> iterator find(const K& k);
>> template<class K> const_iterator find(const K& k) const;
>> };
>
> Theres a templated K-parameter for the loookup, but internally
> this parameter needs to be converted to a string-object to be
> hashed and to be equality-comparable the same way.
correct

> In theory there might be overloaded specializations that take
> a string_view or whatever;
not needed

> but that's ratther unlikely.
close but imprecise. That's simply not there.

Bonita Montero

unread,
Sep 7, 2023, 12:59:01 AM9/7/23
to
Am 07.09.2023 um 05:54 schrieb Pavel:

>> In theory there might be overloaded specializations that take
>> a string_view or whatever;

> not needed

With out such a specialization a string object needs to be created
inside find to have compatible hashing and equality comparison.

>> but that's ratther unlikely.

> close but imprecise. That's simply not there.

That's a theoretically valid possibility,
so it can't be said that it doesn't exist.

Pavel

unread,
Sep 7, 2023, 11:08:23 AM9/7/23
to
Bonita Montero wrote:
> Am 07.09.2023 um 05:54 schrieb Pavel:
>
>>> In theory there might be overloaded specializations that take
>>> a string_view or whatever;
>
>> not needed
>
> With out such a specialization a string object needs to be created
> inside find to have compatible hashing and equality comparison.

Not true. I was benevolently trying to make you read the standard so you
could become a better C++ programmer but you refused.

I am therefore giving up on helping you. I will become evil and post the
complete standard-compliant example that demonstrates how exactly the
unordered_map::find can be made work on string literal and the map with
the string keys without creating a string object (in the example, the
string is wrapped in StringKey to track constructions easily but you are
welcome to remove the wrapper). The code will work as described under
C++20 but not any previous version of the standard -- and this is the
expected behavior.

You still have a chance to improve your C++ programming skills if you
read the standard and find the explanation for why this code shall
behave like it does.

// ------------- code begin cut here -------------------------
#include <cassert>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <numeric>
#include <string>
#include <unordered_map>

using namespace std;

class StringKey {like
public:
StringKey(const char* cStr): s_(cStr) {
cout << "\n\t(StringKey(" << cStr << ") called)\n";
}
const string& getS() const { return s_; }
private:
string s_;
};

struct StringKeyHash {
typedef void is_transparent;
size_t operator()(const char *s) const {
assert(!!s);
return Hash(s, s + strlen(s));
}
size_t operator()(const StringKey &s) const {
return Hash(s.getS().data(), s.getS().data() + s.getS().size());
}
private:
static size_t Hash(const char* begin, const char* end) {
return accumulate(begin, end, (size_t) 0u,
[](size_t a, char c) -> size_t { return a + (size_t)c; });
}
};

struct StringKeyEq {
typedef void is_transparent;
bool operator()(const StringKey& x, const StringKey& y) const {
return IsEq(x.getS().c_str(), y.getS().c_str());
}
bool operator()(const StringKey& x, const char* y) const {
assert(!!y);
return IsEq(x.getS().c_str(), y);
}
bool operator()(const char* x, const StringKey& y) const {
assert(!!x);
return IsEq(x, y.getS().c_str());
}
private:
static bool IsEq(const char *x, const char *y) {
assert(!!x);
assert(!!y);
for (;; ++x, ++y) {
if (*x == *y) {
if (!*x)
return true;
continue;
}
assert(*x != *y);
return false;
}
}
};

int
main(int, char*[]) {
cout << "*** fill up the unordered map\n";
unordered_map<StringKey, string, StringKeyHash, StringKeyEq> um {
{ "key1", "val1" },
{ "key2", "val2" },
};
const char* key3 = "key3";
const char* key4 = "key4";
const char* key1 = "key1";
cout << "*** now do the the find\n";
const auto i1 = um.find(key1);
const bool r1 = i1 == um.end();
const auto i3 = um.find(key3);
const bool r3 = i3 == um.end();
const auto i4 = um.find(key4);
const bool r4 = i4 == um.end();
cout << "find(" << key1 << "):" << r1 << ' ' <<
"find(" << key4 << "):" << r4 << ' ' <<
"find(" << key3 << "):" << r3 << endl;

return 0;
}

// ------------- code end cut here -------------------------

// --- example run output if compiled by g++ -std=c++20 begin ---
$ ./a.out
*** fill up the unordered map

(StringKey(key1) called)

(StringKey(key2) called)
*** now do the the find
find(key1):0 find(key4):1 find(key3):1
// --- example run output if compiled by g++ -std=c++20 end ---

// --- example run output if compiled by g++ -std=c++17 begin ---
$ ./a.out
*** fill up the unordered map

(StringKey(key1) called)

(StringKey(key2) called)
*** now do the the find

(StringKey(key1) called)

(StringKey(key3) called)

(StringKey(key4) called)
find(key1):0 find(key4):1 find(key3):1
// --- example run output if compiled by g++ -std=c++17 end ---


>
>>> but that's ratther unlikely.
>
>> close but imprecise. That's simply not there.
>
> That's a theoretically valid possibility,
> so it can't be said that it doesn't exist.
This is "theoretically valid" only for those who refuse to read the
standard; the others know the specialization is not there.

Bonita Montero

unread,
Sep 7, 2023, 11:56:15 AM9/7/23
to

Am 07.09.2023 um 17:08 schrieb Pavel:

> Not true. I was benevolently trying to make you read the standard
> so you could become a better C++ programmer but you refused.

You're making a total differnt discussion and don't notce where's my
point. Within find for a string key a string key is generated from K
if the key is a string-object; that's all. What's wrong with that ?
The alternative you're showing below is sth. completely different
and the idea came across my mind when I discovered what's the problem
with my bug. But a string_view could much more performant if you won't
do any additional allocations per inserted node.
But that's not my point. Should I disable just my code debugging with
MSVC and show you the code which converts a string-pointer to a com-
pararable string-object ?

Rest unread.
Idiot !

Bonita Montero

unread,
Sep 7, 2023, 12:04:53 PM9/7/23
to
Am 07.09.2023 um 17:56 schrieb Bonita Montero:

> You're making a total differnt discussion and don't notce where's my
> point. Within find for a string key a string key is generated from K
> if the key is a string-object; that's all. What's wrong with that ?

Here are the declarations for C++20 from en.cppreference.com:

template< class K >
iterator find( const K& x );
(3) (since C++20)
template< class K >
const_iterator find( const K& x ) const;
(4) (since C++20)

MSVC hasn't code for that even I use C++20. I think MS dropped
that because an internal conversion within find() could be also
done externally while calling find(). Smart decision.

Pavel

unread,
Sep 7, 2023, 7:36:14 PM9/7/23
to
Bonita Montero wrote:
> Am 07.09.2023 um 17:56 schrieb Bonita Montero:
>
>> You're making a total differnt discussion and don't notce where's my
>> point. Within find for a string key
what is a "find for a string key"? Earlier you referred to a "lookup
with a string-literal" which is what this discussion is about.

> a string key is generated from K
>> if the key is a string-object; that's all. What's wrong with that ?
>
> Here are the declarations for C++20 from en.cppreference.com:
>
> template< class K >
> iterator find( const K& x );
>     (3)     (since C++20)
> template< class K >
> const_iterator find( const K& x ) const;
>     (4)     (since C++20)
>
> MSVC hasn't code for that even I use C++20. I think MS dropped
> that because an internal conversion within find()
What "internal conversion" within find() do you refer to? What is the
source type? What is the destination type?

> could be also
> done externally while calling find(). Smart decision.
No, just a defect. Not including the template version of find() is
non-compliant with C++20 because it is affects for C++ programs. As a
simple example, it makes a valid C++20 program fail to compile if that
program calls one of unordered_map::find templates with the explicitly
specified template parameter type.

Bonita Montero

unread,
Sep 7, 2023, 10:10:45 PM9/7/23
to
Am 07.09.2023 um 17:08 schrieb Pavel:

> Not true. I was benevolently trying to make you read the standard
> so you could become a better C++ programmer but you refused.

You're making a total differnt discussion and don't notce where's my
point. Within find for a string key a string key is generated from K
if the key is a string-object; that's all. What's wrong with that ?

James Kuyper

unread,
Sep 8, 2023, 12:09:44 AM9/8/23
to
On 9/6/23 19:18, jak wrote:
...
> However, it is obvious that it prints the warnings, I extracted the test
> intentionally from the function/method, because otherwise the compiler
> does not recognize the UB. If I modified my program in this way:
>
> $ cat ./pp.cpp
> #include <iostream>
>
> int main()
> {
> auto eif = [] (char const *a, char const *b) {
> std::cout << ( ( a == b ) ? "equal" : "not equal" ) << std::endl;
> };
> char const *sp = "hello";
> char sl[] = "hello";
>
> eif( sp, sp );
> eif( "hello", sp );
> eif( "hello", "hello" );
> eif( "hello", sl );
>
> return 0;
> }
>
> $ g++ -Wall -Wpedantic -Werror -std=c++17 ./pp.cpp -o ./pp
>
> $ ./pp
> equal
> equal
> equal
> not equal
>
> $ _
>
> The warnings disappear but the UB remain.

What UB are you referring to?

Chris M. Thomasson

unread,
Sep 14, 2023, 7:28:07 PM9/14/23
to
^^^^^^^^^^^

Wow, way back in 2009. How time flies!

Chris M. Thomasson

unread,
Sep 18, 2023, 7:20:29 PM9/18/23
to
On 9/4/2023 7:24 AM, Scott Lurndal wrote:
> "Chris M. Thomasson" <chris.m.t...@gmail.com> writes:
>> On 9/1/2023 9:19 PM, Bonita Montero wrote:
>>> Am 01.09.2023 um 23:50 schrieb Chris M. Thomasson:
>>>
>>>> Try something like:
>>>
>>> I know that I could have done that easier, but I wanted to know
>>> where's the bug and why my example does find the key value anyway.
>>>
>>>> ___________________________
>>>> #include <iostream>
>>>> #include <unordered_map>
>>>> #include <string>
>>>> #include <cstdlib>
>>>>
>>>>
>>>> int main()
>>>> {
>>>>      static std::unordered_map<std::string, long> opts = {
>>>>        {"--idle", 0 },
>>>>        {"--below", 1 },
>>>>        {"--above", 2 },
>>>>        {"--high", 3 },
>>>>        {"--realtime", 4 }
>>>>      };
>>>>
>>>>      auto mapped_opt = opts.find("--realtime");
>>>
>>> The problem here is that each find creates a string-object,
>>> which is rather slow.
>>
>> Right. Afaict, there is a way to create the strings on the stack and use
>> a little adapter logic to make it work with std::unordered_map. So, no
>> dynamic allocation.
>
> Or just use getopt_long() with UTF-8 strings.

:^)
0 new messages