Std::unordered_map with enum

Hello everybody.
I’m trying to compile on ROOT 6 a code that uses std::unordered_map, which, I admit, I’ve never used before.
Now, the lines that give me trouble are these:

enum class EProc {kPAIR = 0, kCOMP, kPHOT, kPFIS, kDRAY, kANNI, kBREM, kHADR, kMUNU, kDCAY, kLOSS, kMULS, kCKOV, kLABS, kRAYL};
const static std::unordered_map<EProc, const char*> mProcessIDToName;

When I try to compile the class, I get every kind of error, starting from

In file included from /usr/include/c++/5/unordered_map:47:
In file included from /usr/include/c++/5/bits/hashtable.h:35:
/usr/include/c++/5/bits/hashtable_policy.h:85:11: error: implicit instantiation of undefined template 'std::hash<EProc>'
        noexcept(declval<const _Hash&>()(declval<const _Key&>()))>
                 ^
/usr/include/c++/5/type_traits:138:14: note: in instantiation of template class 'std::__detail::__is_noexcept_hash<EProc, std::hash<EProc> >' requested here
    : public conditional<_B1::value, _B2, _B1>::type
             ^
/usr/include/c++/5/type_traits:148:39: note: in instantiation of template class
      'std::__and_<std::__is_fast_hash<std::hash<EProc> >, std::__detail::__is_noexcept_hash<EProc, std::hash<EProc> > >' requested here
    : public integral_constant<bool, !_Pp::value>
                                      ^
/usr/include/c++/5/bits/unordered_map.h:46:34: note: in instantiation of template class 'std::__not_<std::__and_<std::__is_fast_hash<std::hash<EProc> >, std::__detail::__is_noexcept_hash<EProc,
      std::hash<EProc> > > >' requested here
           typename _Tr = __umap_traits<__cache_default<_Key, _Hash>::value>>
                                        ^
/usr/include/c++/5/bits/unordered_map.h:100:15: note: in instantiation of default argument for '__umap_hashtable<EProc, const char *, std::hash<EProc>, std::equal_to<EProc>,
      std::allocator<std::pair<const EProc, const char *> > >' required here
      typedef __umap_hashtable<_Key, _Tp, _Hash, _Pred, _Alloc>  _Hashtable;
              ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/home/./MaterialManager.h:174:29: note: in instantiation of template class 'std::unordered_map<EProc, const char *, std::hash<EProc>, std::equal_to<EProc>, std::allocator<std::pair<const
      EProc, const char *> > >' requested here
  auto it = mProcessIDToName.find(process);
                            ^
/usr/include/c++/5/bits/functional_hash.h:58:12: note: template is declared here
    struct hash;
           ^
In file included from input_line_11:6:
In file included from ././MaterialManager.cxx:11:
In file included from /home/./MaterialManager.h:16:
In file included from /usr/include/c++/5/unordered_map:48:
/usr/include/c++/5/bits/unordered_map.h:522:7: error: multiple overloads of 'erase' instantiate to the same signature 'std::unordered_map<EProc, const char *, std::hash<EProc>, std::equal_to<EProc>,
      std::allocator<std::pair<const EProc, const char *> > >::iterator (std::unordered_map<EProc, const char *, std::hash<EProc>,
      std::equal_to<EProc>, std::allocator<std::pair<const EProc, const char *> > >::iterator)' (aka 'int (int)')
      erase(iterator __position)
      ^
/home/./MaterialManager.h:174:29: note: in instantiation of template class 'std::unordered_map<EProc, const char *, std::hash<EProc>, std::equal_to<EProc>, std::allocator<std::pair<const
      EProc, const char *> > >' requested here
  auto it = mProcessIDToName.find(process);
                            ^
/usr/include/c++/5/bits/unordered_map.h:517:7: note: previous declaration is here
      erase(const_iterator __position)
      ^

I understood that the problem is related with using enum with unordered_map, but I really have no idea how to fix this.
Any help/tip would be much appreciated.
Thanks in advance! :slightly_smiling_face:

Hi,

teh std::unordered_map should work with the enumeration. It looks to me that there is a issue when calling the find() member function. What is the type of process ?
Also be sure you are calling only const functions of the unordered _map since you have declared as const

Best regards

Lorenzo

I get basically the same errors when I just write those two lines in a root terminal:

In module 'std' imported from input_line_1:1:
/usr/include/c++/5/bits/hashtable_policy.h:85:11: error: implicit instantiation of undefined template 'std::hash<EProc>'
        noexcept(declval<const _Hash&>()(declval<const _Key&>()))>
                 ^
/usr/include/c++/5/type_traits:138:14: note: in instantiation of template class 'std::__detail::__is_noexcept_hash<EProc, std::hash<EProc> >' requested here
    : public conditional<_B1::value, _B2, _B1>::type
             ^
/usr/include/c++/5/type_traits:148:39: note: in instantiation of template class
      'std::__and_<std::__is_fast_hash<std::hash<EProc> >, std::__detail::__is_noexcept_hash<EProc, std::hash<EProc> > >' requested here
    : public integral_constant<bool, !_Pp::value>
                                      ^
/usr/include/c++/5/bits/unordered_map.h:46:34: note: in instantiation of template class 'std::__not_<std::__and_<std::__is_fast_hash<std::hash<EProc> >, std::__detail::__is_noexcept_hash<EProc,
      std::hash<EProc> > > >' requested here
           typename _Tr = __umap_traits<__cache_default<_Key, _Hash>::value>>
                                        ^
/usr/include/c++/5/bits/unordered_map.h:100:15: note: in instantiation of default argument for '__umap_hashtable<EProc, const char *, std::hash<EProc>, std::equal_to<EProc>,
      std::allocator<std::pair<const EProc, const char *> > >' required here
      typedef __umap_hashtable<_Key, _Tp, _Hash, _Pred, _Alloc>  _Hashtable;
              ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
ROOT_prompt_1:1:53: note: in instantiation of template class 'std::unordered_map<EProc, const char *, std::hash<EProc>, std::equal_to<EProc>, std::allocator<std::pair<const
      EProc, const char *> > >' requested here
const static std::unordered_map<EProc, const char*> mProcessIDToName;
                                                    ^
/usr/include/c++/5/bits/functional_hash.h:58:12: note: template is declared here
    struct hash;
           ^
In module 'std' imported from input_line_1:1:
/usr/include/c++/5/bits/unordered_map.h:522:7: error: multiple overloads of 'erase' instantiate to the same signature 'std::unordered_map<EProc, const char *, std::hash<EProc>, std::equal_to<EProc>,
      std::allocator<std::pair<const EProc, const char *> > >::iterator (std::unordered_map<EProc, const char *, std::hash<EProc>,
      std::equal_to<EProc>, std::allocator<std::pair<const EProc, const char *> > >::iterator)' (aka 'int (int)')
      erase(iterator __position)
      ^
ROOT_prompt_1:1:53: note: in instantiation of template class 'std::unordered_map<EProc, const char *, std::hash<EProc>, std::equal_to<EProc>, std::allocator<std::pair<const
      EProc, const char *> > >' requested here
const static std::unordered_map<EProc, const char*> mProcessIDToName;
                                                    ^
/usr/include/c++/5/bits/unordered_map.h:517:7: note: previous declaration is here
      erase(const_iterator __position)
      ^

while when I try to use unordered_map, with for example, Double_t instaed of enum, I get no complain from root:

root [2] const static std::unordered_map<Double_t, const char*> mProcessIDToName2;
root [3] 

that’s why I thought that the problem was specifically related with the use of enum :roll_eyes:

/usr/include/c++/5/bits/hashtable_policy.h:85:11: error: implicit instantiation of undefined template 'std::hash<EProc>' noexcept(declval<const _Hash&>()(declval<const _Key&>()))>

This error means that the compiler does not know how to hash an EProc. Keys in the unordered_map must be hashable types. See e.g. this stackoverflow thread.

By the way, it seems that the fact that compilers didn’t just fall back to the hashing function for the underlying type was considered a C++ standard defect, was fixed, and recent compilers seem to be ok with it: https://godbolt.org/z/aYqgGN

What ROOT version are you using? ROOT 6.20 does not seem to have a problem with this code:

enum class EProc {kPAIR = 0, kCOMP, kPHOT, kPFIS, kDRAY, kANNI, kBREM, kHADR, kMUNU, kDCAY, kLOSS, kMULS, kCKOV, kLABS, kRAYL};
const static std::unordered_map<EProc, const char*> mProcessIDToName;

Cheers,
Enrico

I am using ROOT 6.20, but it’s a binary distribution, I suspect that I have to build it instead :thinking:
Thank you for the help in any case! :slight_smile:

That’s weird, it might have to do with the version of the compiler on your platform.

On lxplus:

[eguiraud@lxplus780 ~]$ source /cvmfs/sft.cern.ch/lcg/views/LCG_97/x86_64-centos7-gcc9-opt/setup.sh
[eguiraud@lxplus780 ~]$ root
   ------------------------------------------------------------------
  | Welcome to ROOT 6.20/02                        https://root.cern |
  | (c) 1995-2020, The ROOT Team; conception: R. Brun, F. Rademakers |
  | Built for linuxx8664gcc on Mar 15 2020, 15:25:34                 |
  | From tags/v6-20-02@v6-20-02                                      |
  | Try '.help', '.demo', '.license', '.credits', '.quit'/'.q'       |
   ------------------------------------------------------------------

root [0] enum class EProc {kPAIR = 0, kCOMP, kPHOT, kPFIS, kDRAY, kANNI, kBREM, kHADR, kMUNU, kDCAY, kLOSS, kMULS, kCKOV, kLABS, kRAYL};
root [1] const static std::unordered_map<EProc, const char*> mProcessIDToName;
root [2] mProcessIDToName.find(EProc::kPAIR)
(std::unordered_map<EProc, const char *, std::hash<EProc>, std::equal_to<EProc>, std::allocator<std::pair<const EProc, const char *> > >::const_iterator) @0x428f250
root [3] // all good 

Hope this helps!
Enrico

Actually I think it helps, yes.
So: the gcc version on my laptop is 9.3 BUT the root binary distribution I am using has been built with the 5.4…

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