c++ - Make std's data-structure use my existing non-static hash function "hashCode()" by default -
i have moderate-size codebase (>200 .cpp
) use function hashcode()
return hash number:-
class b01{ //a class //..... complex thing .... public: size_t hashcode(){ /* hash algorithm #h01 */} }; class b02{ //just unrelated class //..... complex thing .... public: size_t hashcode(){/* #h02 */} //this same name above };
i have used in various locations, e.g. in custom data-structure. works well.
now, want make hash algorithm recognized std::
data structure:-
here should :- (modified cppreference, call code #d
).
//#d namespace std { template<> struct hash<b01> { std::size_t operator()(const b01& b) const { /* hash algorithm #h01 */ } }; }
if insert block #d
(with appropriate implementation) in every class (b01
,b02
,...), can call :-
std::unordered_set<b01> b01s; std::unordered_set<b02> b02s;
without passing second template argument,
, hash algorithm (#h01
) called. (by default)
question
to make recognize of b01::hashcode, b02::hashcode, ...
,
have insert block #d
200+ bxx.h
?
can add single block #d
(in top header?)
and, there, re-route std::anydatastructure
call hashcode()
whenever possible?
//pseudo code namespace std{ template<> struct hash<x> { std::size_t operator()(const x& x) const { // std::enable_if?? if(x has hashcode()){ //e.g. t=b01 or b02 make template highest priority //how? return hashcode(); }else{ //e.g. t=std::string don't match template; } } }; }
it sounds sfinae question me.
side note: the similar question in didn't ask how achieve this.
edit (why don't refactor it? ; 3 feb 2017)
- i don't know if brute force refactoring right path. guess there might better way.
hashcode()
home. emotionally attach it.- i want keep code short , clean possible.
std::
blocks dirty. - it may curiosity. if stubborn not refactor code, how far c++ can go?
solution one
if can make classes b01
, b02
, ... class templates dummy parameters go along specialization of std::hash
template template takes dummy template parameter:
#include <iostream> #include <unordered_set> struct dummy {}; template <class = dummy> class b01{ public: size_t hashcode() const { return 0; } }; template <class = dummy> class b02{ public: size_t hashcode() const { return 0; } }; namespace std{ template<template <class> class tt> struct hash<tt<dummy>> { std::size_t operator()(const tt<dummy>& x) const { return x.hashcode(); } }; } int main() { std::unordered_set<b01<>> us; (void)us; }
solution 2 (contain error/don't use it)
but believe desire looks more this:
#include <iostream> #include <unordered_set> class b01{ public: size_t hashcode() const { return 0; } }; class b02{ public: size_t hashcode() const { return 0; } }; template <class t, class> using enable_hash = t; namespace std{ template<class t> struct hash<enable_hash<t, decltype(std::declval<t>().hashcode())>> { std::size_t operator()(const t& x) const { return x.hashcode(); } }; } int main() { std::unordered_set<b01> us; (void)us; }
(inspired this answer)
however long can work on gcc isn't allowed c++ standard (but i'm not sure if literally disallowed...). see this thread in context.
edit:
as pointed out @barry gcc behaviour not mandated c++ standard , such there absolutely no guaranties work in next gcc version... can perceived bug allows partial specialization of template in fact not specialize template.
solution 3 (preffered)
another way specialize std::unordered_set
instead of std::hash
:
#include <iostream> #include <type_traits> #include <unordered_set> class specializeunorderedset { }; class b01: public specializeunorderedset { public: size_t hashcode() const { return 0; } }; class b02: public specializeunorderedset { public: size_t hashcode() const { return 0; } }; template <class t> struct my_hash { std::size_t operator()(const t& x) const { return x.hashcode(); } }; template <class...> using voider = void; template <class t, class = void> struct hashcodetrait: std::false_type { }; template <class t> struct hashcodetrait<t, voider<decltype(std::declval<t>().hashcode())>>: std::true_type { }; namespace std{ template <class t> struct unordered_set<t, typename std::enable_if<hashcodetrait<t>::value && std::is_base_of<specializeunorderedset, t>::value, std::hash<t>>::type, std::equal_to<t>, std::allocator<t>>: unordered_set<t, my_hash<t>, std::equal_to<t>, std::allocator<t>> { }; } int main() { std::unordered_set<b01> us; (void)us; }
according discussion presented here should valid. work in gcc, clang, icc, vs
to able use code without interfering in code of classes believe can utilize adl rules make sfinae check if given class not involve std namespace. can find background here. credits cheers , hth. - alf. approach change follows:
#include <utility> #include <unordered_set> #include <string> #include <type_traits> #include <functional> template< class type > void ref( type&& ) {} template< class type > constexpr auto involve_std() -> bool { using std::is_same; using std::declval; return not is_same< void, decltype( ref( declval<type &>() ) )>::value; } class b01 { public: size_t hashcode() const { return 0; } }; class b02 { public: size_t hashcode() const { return 0; } }; template <class t> struct my_hash { std::size_t operator()(const t& x) const { return x.hashcode(); } }; template <class...> struct voider { using type = void; }; template <class t, class = void> struct hashcodetrait: std::false_type { }; template <class t> struct hashcodetrait<t, typename voider<decltype(std::declval<t>().hashcode())>::type>: std::true_type { }; namespace std{ template <class t> struct unordered_set<t, typename std::enable_if<hashcodetrait<t>::value && !involve_std<t>(), std::hash<t>>::type, std::equal_to<t>, std::allocator<t>>: unordered_set<t, my_hash<t>, std::equal_to<t>, std::allocator<t>> { }; } int main() { std::unordered_set<b01> usb01; std::unordered_set<std::string> uss; static_assert(std::is_base_of<std::unordered_set<b01, my_hash<b01>>, std::unordered_set<b01>>::value, "!"); static_assert(!std::is_base_of<std::unordered_set<std::string, my_hash<std::string>>, std::unordered_set<std::string>>::value, "!"); (void)usb01; (void)uss; }
Comments
Post a Comment