Unordered Map at() Exception

What's the correct way to handle not finding a key in an unordered_map? (which I am new to using)

I didn't want to use 'find' as it iterates over the full map (if I understand it), where as 'at' uses the key's hash to directly access the correct bucket so it's faster.


#include <unordered_map>

std::unordered_map<byte, byte> myMap={
  {{1},{2}}
};


void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  Serial.println(myMap.at(4));
}

void loop() {
}

This code crashes... What's the return value if the key isn't found?

Looking online it says it throws an exception. I'll be honest, I learned c++ purely on Arduino and apparently they don't use exceptions, so I don't really understand them or how to deal with them outside of 'they get thrown when an error occurs'.

If the requested key does not match the key of any element in the container, the function throws an out_of_range exception

Challenge is that you can’t handle exceptions with the way standard compilation works, try catch Is just too costly on small microcontrollers.

So Using the iterator (find) is probably the way to go.

EDIT: you modified your post to add the exception description…

Read try-block - cppreference.com if you want to understand how this would be done in “normal c++”

1 Like

Are there any clever ways to ensure that at least 'some' valid bucket is returned? Like maybe if I added a 'dummy' key:value pair last and edited the lib so that gets returned as a fail-safe instead of throwing the exception?

Basically I'm trying to make invalid keys essentially be ignored so it's at least crash-proof.

My prediction of your response on the above is... 'last isn't guarenteed, hence 'unordered' so that won't work' and 'it's up to the programmer to ensure that valid keys are being hashed when using 'at()' '

So the way my code works is that I have midi sysex messages coming in over USB, that contain an 'address' and a 'data' byte for a 'Parameter' (which both get extracted from the sysex as uint32_t and byte variables)

I have the unordered map of parameter pointers (750 of them, keyed by address) and when I do a sync to read and update the data of all parameters, my device spits out a sysex message for each one containing the data for the requested address.

So I have something along the lines of: (syntax from memory)

    parameterMap.at(address)->index = data;

Most of the time my device spits out sysex messages for the specific addresses I've requested, but it also spits out (in certain cases) much longer messages with bulk data for an address block which isn't found in my map. I think that's what is causing my program to crash.

I swapped to map because iterating over potentially 750 array entries, 750 times (worst case) to find the right place to dump that data is vastly slower than the hash to bucket of the map.

I did some quick benchmarking and find() and at() seem very similar in performance and both are way faster in comparison to an array and for loop.

Why is this? I thought find simply iterated over everything in the map and returns if found or returns end() if not. How is that not the same as an array and for loop?

Yes code for find and at are very similar in cost, documentation for both says

Average case: constant. Worst case: linear in container size.

See c++ - Which one is faster "find" or "at" in unordered_map? - Stack Overflow

What wizardry does 'find' use to make it so much faster than a for loop/array?

Also, if the performance and result are very similar (one returning the value, one returning an iterator to the pair) why do they both exist?

Why not just use find and make people use the '->first' and '->second' syntax?

The main difference is the exception throwing

See answer 3 in the previous link, you can see the source code

1 Like

Okay great, appreciate the help and insights I'll do some reading on the links

A map is typically implemented as a tree were searching can be performed by a wizard at up to O(log n), but mere mortals suffer worse case at O(n).

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.