/* try.cpp - for trying out some language features.... * scons will create the binary bin/try * */ // 8/07 - how to control NOBUG?? // execute with NOBUG_LOG='ttt:TRACE' bin/try // 1/08 - working out a static initialisation problem for Visitor (Tag creation) // 1/08 - check 64bit longs // 4/08 - comparison operators on shared_ptr // 4/08 - conversions on the value_type used for boost::any // 5/08 - how to guard a downcasting access, so it is compiled in only if the involved types are convertible // 7/08 - combining partial specialisation and subclasses // 10/8 - abusing the STL containers to hold noncopyable values // 6/09 - investigating how to build a mixin template providing an operator bool() // 12/9 - tracking down a strange "warning: type qualifiers ignored on function return type" // 1/10 - can we determine at compile time the presence of a certain function (for duck-typing)? // 4/10 - pretty printing STL containers with python enabled GDB? // 1/11 - exploring numeric limits // 1/11 - integer floor and wrap operation(s) // 1/11 - how to fetch the path of the own executable -- at least under Linux? // 10/11 - simple demo using a pointer and a struct // 11/11 - using the boost random number generator(s) // 12/11 - how to detect if string conversion is possible? // 1/12 - is partial application of member functions possible? // 5/14 - c++11 transition: detect empty function object // 7/14 - c++11 transition: std hash function vs. boost hash /** @file try.cpp ** Investigation: how to supply a hash function for custom types. ** This example defines two custom types, each of which provides a way ** to calculate hash values. But in one case, we use the new \c std::hash ** as a framework, in the other case we use the extension mechanism for ** \c boost::hash. The latter has the benefit of being simpler and less verbose ** to write, since a simple ADL function is sufficient as extension point ** ** Now it would be desirable to bridge automatically between the two systems of ** defining hash functions. Unfortunately, the standard library since GCC 4.7 ** contains a default implementation of std::hash to trigger a static assertion. ** Probably this was meant to educate people, but has the adverse side effect to ** prohibit any metaprogramming through SFINAE ** ** This mistake was corrected somewhere in the 4.8.x series, but in the meantime ** we'll use the dirty trick proposed by "enobayram" to hijack and fix the problematic ** definition from the standard library. ** http://stackoverflow.com/questions/12753997/check-if-type-is-hashable ** ** Moreover, in this exploration, we build an automatic bridge to invoke ** existing boost hash functions whenever a \c std::hash definition is required. ** */ #include namespace lib { namespace meta { namespace { struct NoUsableHashDefinition { size_t more_than_one[2]; }; typedef size_t HasUsableHashDefinition; NoUsableHashDefinition hash_value(...); } template class provides_BoostHashFunction { TY const& unusedDummy = *(TY*)nullptr; public: enum{ value = (sizeof(HasUsableHashDefinition) == sizeof(hash_value(unusedDummy))) }; }; }} #include namespace std { template struct __hash_base; template struct _HashImplementationSelector : public __hash_base { static_assert (sizeof(TY) < 0, "No hash implementation found. Either specialise std::hash or provide a boost-style hash_value via ADL."); typedef int NotHashable; }; template struct _HashImplementationSelector >::type > : public __hash_base { size_t operator() (TY const& elm) const noexcept { return hash_value(elm); } }; /** * Primary class template for std::hash. * We provide no default implementation, but a marker type * to allow detection of custom implementation through metaprogramming */ template struct hash : public _HashImplementationSelector { }; } #define hash hash_HIDDEN #define _Hash_impl _Hash_impl_HIDDEN #include #undef hash #undef _Hash_impl namespace std { struct _Hash_impl : public std::_Hash_impl_HIDDEN { template static auto hash (ARGS&&... args) -> decltype(hash_HIDDEN (std::forward(args)...)) { return hash_HIDDEN (std::forward(args)...); } }; #define STD_HASH_IMPL(_TY_) \ template<> struct hash<_TY_> : public hash_HIDDEN<_TY_> { }; STD_HASH_IMPL (bool) STD_HASH_IMPL (char) STD_HASH_IMPL (signed char) STD_HASH_IMPL (unsigned char) STD_HASH_IMPL (wchar_t) STD_HASH_IMPL (char16_t) STD_HASH_IMPL (char32_t) STD_HASH_IMPL (short) STD_HASH_IMPL (int) STD_HASH_IMPL (long) STD_HASH_IMPL (long long) STD_HASH_IMPL (unsigned short) STD_HASH_IMPL (unsigned int) STD_HASH_IMPL (unsigned long) STD_HASH_IMPL (unsigned long long) STD_HASH_IMPL (float) STD_HASH_IMPL (double) STD_HASH_IMPL (long double) #undef STD_HASH_IMPL } #include #include #include #include using std::vector; using std::string; using std::cout; using std::endl; class S { string s_; friend std::hash; public: S(string ss ="") : s_(ss) { } }; namespace std { template<> struct hash { size_t operator() (S const& val) const noexcept { hash string_hasher; return string_hasher(val.s_); } }; } class V { vector v_; public: V(string ss ="") { v_.push_back(ss); } friend size_t hash_value (V const& v) { return boost::hash_value(v.v_); } }; int main (int, char**) { string p("Путин"), pp(p); S s(p), ss(pp); V v(p), vv(pp); std::hash std_stringHasher; boost::hash boo_stringHasher; std::hash std_customHasher; boost::hash boo_customHasher; std::hash boo2std_crossHar; cout << "raw hash(std) = " << std_stringHasher(p) <<"|"<< std_stringHasher(pp) << "\n (boost) = " << boo_stringHasher(p) <<"|"<< boo_stringHasher(pp) << "\n custom hash (std) " << std_customHasher(s) <<"|"<< std_customHasher(ss) << "\n custom hash (boost) " << boo_customHasher(v) <<"|"<< boo_customHasher(vv) << "\n has_boost_hash " << lib::meta::provides_BoostHashFunction::value << "\n has_boost_hash " << lib::meta::provides_BoostHashFunction::value << "\n use boost from std: " << boo2std_crossHar(v) <<"|"<< boo2std_crossHar(vv) ; // Note: does not compile, // since there is not automatic bridge from std to boost hash // // boost::hash()(s); cout << "\n.gulp.\n"; return 0; }