From 264b7e8e0f0de694bb0017cf13e230e6ca5d813d Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Sun, 21 Sep 2014 02:54:54 +0200 Subject: [PATCH] Research: corner cases of "perfect forwarding" --- research/try.cpp | 257 ++++++++++++----------------------------------- 1 file changed, 66 insertions(+), 191 deletions(-) diff --git a/research/try.cpp b/research/try.cpp index e0d59a941..80dec741c 100644 --- a/research/try.cpp +++ b/research/try.cpp @@ -25,230 +25,105 @@ // 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 +// 9/14 - variadic templates and perfect forwarding /** @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. + ** Investigation: pitfalls of "perfect forwarding". + ** Find out about the corner cases where chained argument forwarding + ** does not work as expected ** */ +#include "lib/test/test-helper.hpp" +#include "lib/util.hpp" + #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 #include -#include -using std::vector; using std::string; using std::cout; using std::endl; -class S +class Interface + { + public: + virtual ~Interface() { } + virtual string op() const =0; + }; + +class Impl + : public Interface { string s_; - friend std::hash; + string + op() const override + { + return s_; + } public: - S(string ss ="") + Impl(string ss ="IMP") : s_(ss) { } }; -namespace std { - template<> - struct hash - { - size_t - operator() (S const& val) const noexcept - { - hash string_hasher; - return string_hasher(val.s_); - } - }; +template +void +diagnostics (string id, const void* addr) +{ + cout << id << "\n" + << "invoked with.. " << lib::test::showType() + << "\n Address ... " << addr + << "\n is lRef ... " << std::is_lvalue_reference::value + << "\n is rRef ... " << std::is_rvalue_reference::value + << "\n" + ; } -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_); - } - }; + +void +invoke (Interface const& ref) +{ + using Ty = Interface const&; + diagnostics ("Invoke", &ref); + cout << "instanceof Impl?" << bool(INSTANCEOF(Impl, &ref)) <<"\n"; + cout << ref.op(); +} + +template +void +indirect_1 (FUN fun, A&& a) +{ + diagnostics ("Indirect-1", &a); + fun (std::forward (a)); +} + +template +void +indirect_2 (FUN fun, A&& a) +{ + diagnostics ("Indirect-2", &a); + indirect_1 (fun, std::forward (a)); +} int main (int, char**) { - string p("Путин"), pp(p); - S s(p), ss(pp); - V v(p), vv(pp); + Impl obj; + Interface const& ref = obj; - std::hash std_stringHasher; - boost::hash boo_stringHasher; + cout << "before call. Address... "<<&ref<<"\n"; - std::hash std_customHasher; - boost::hash boo_customHasher; + std::function fun(invoke); - 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); + indirect_2 (fun, ref); + indirect_2 (fun, Impl("honk")); cout << "\n.gulp.\n";