Created
March 7, 2017 21:12
-
-
Save xiangfan-ms/359e4ef424625bdadec1fc2d2708792b to your computer and use it in GitHub Desktop.
Operator
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <iostream> | |
#include <type_traits> | |
template<typename T, int N> | |
struct S1 { | |
// A somewhat 'expensive' ctor | |
template<typename U, typename = std::enable_if_t<std::is_arithmetic<std::decay_t<U>>::value>> | |
S1(U); | |
float operator()() const; | |
}; | |
template<typename T, int N> | |
struct S2 { | |
// A somewhat 'expensive' ctor | |
template<typename U, typename = std::enable_if_t<std::is_arithmetic<std::decay_t<U>>::value>> | |
S2(U) {} | |
float operator()() const { return 0; } | |
#ifdef OPT | |
// Define a hidden friend which can only be found via ADL (argument dependent lookup) | |
friend std::ostream& operator<<(std::ostream& os, const S2& s) { os << s() << ""; return os; } | |
#endif | |
}; | |
// In the code below, each function definition does two things, | |
// 1. The function declaration adds a new overload of operator<< to the corresponding namespace scope | |
// 2. 'os << s()' in the function definition requires the compiler to do overload resolutions on all available 'operator<<' overloads | |
// The compiler has to try the conversion from float (type of 's()') to S (type of the second argument of the candiate) for each candidate, | |
// so the time complexity is O(c1 * c2) where c1 is the number of function definitions and c2 is number of the candidates for each function | |
// definition. | |
// c1 is always 512 in the code below and c2 varies. | |
#if TEST == 0 | |
#ifdef OPT | |
// The candidates are only the ones defined in ostream | |
#define ADDOVERLOAD9(n) std::ostream& operator<<(std::ostream& os, const S1<void, n>& s) { os.operator<<(s()).operator<<(""); return os; } | |
#else | |
// The candidates include everything in the global namespace, std namespace and the ones defined in ostream | |
#define ADDOVERLOAD9(n) std::ostream& operator<<(std::ostream& os, const S1<void, n>& s) { os << s() << ""; return os; } | |
#endif | |
#define ADDOVERLOAD8(n) ADDOVERLOAD9((n) * 2) ADDOVERLOAD9((n) * 2 + 1) | |
#define ADDOVERLOAD7(n) ADDOVERLOAD8((n) * 2) ADDOVERLOAD8((n) * 2 + 1) | |
#define ADDOVERLOAD6(n) ADDOVERLOAD7((n) * 2) ADDOVERLOAD7((n) * 2 + 1) | |
#define ADDOVERLOAD5(n) ADDOVERLOAD6((n) * 2) ADDOVERLOAD6((n) * 2 + 1) | |
#define ADDOVERLOAD4(n) ADDOVERLOAD5((n) * 2) ADDOVERLOAD5((n) * 2 + 1) | |
#define ADDOVERLOAD3(n) ADDOVERLOAD4((n) * 2) ADDOVERLOAD4((n) * 2 + 1) | |
#define ADDOVERLOAD2(n) ADDOVERLOAD3((n) * 2) ADDOVERLOAD3((n) * 2 + 1) | |
#define ADDOVERLOAD1(n) ADDOVERLOAD2((n) * 2) ADDOVERLOAD2((n) * 2 + 1) | |
#define ADDOVERLOAD0(n) ADDOVERLOAD1((n) * 2) ADDOVERLOAD1((n) * 2 + 1) | |
// This adds 512 'operator<<' overloads to the global namespace | |
ADDOVERLOAD0(0); | |
#elif TEST == 1 | |
#ifdef OPT | |
// The overload can be found via ADL (argument dependent lookup) | |
// The candidates include everything in the ns namespace, std namespace and the ones defined in ostream | |
#define ADDOVERLOAD9(ns, n) namespace ns { std::ostream& operator<<(std::ostream& os, const S1<type, n>& s) { os << s() << ""; return os; } } | |
#else | |
// The candidates include everything in the global namespace, std namespace and the ones defined in ostream | |
#define ADDOVERLOAD9(ns, n) std::ostream& operator<<(std::ostream& os, const S1<ns::type, n>& s) { os << s() << ""; return os; } | |
#endif | |
#define ADDOVERLOAD8(ns, n) ADDOVERLOAD9(ns, (n) * 2) ADDOVERLOAD9(ns, (n) * 2 + 1) | |
#define ADDOVERLOAD7(ns, n) ADDOVERLOAD8(ns, (n) * 2) ADDOVERLOAD8(ns, (n) * 2 + 1) | |
#define ADDOVERLOAD6(ns, n) ADDOVERLOAD7(ns, (n) * 2) ADDOVERLOAD7(ns, (n) * 2 + 1) | |
#define ADDOVERLOAD5(ns, n) ADDOVERLOAD6(ns, (n) * 2) ADDOVERLOAD6(ns, (n) * 2 + 1) | |
#define ADDOVERLOAD4(ns, n) ADDOVERLOAD5(ns, (n) * 2) ADDOVERLOAD5(ns, (n) * 2 + 1) | |
#define ADDOVERLOAD(n) namespace N##n { struct type {}; } ADDOVERLOAD4(N##n, 0); | |
// This adds 32 'operator<<' overloads each to 16 namespaces (N0 ~ N15) | |
ADDOVERLOAD(0) ADDOVERLOAD(1) | |
ADDOVERLOAD(2) ADDOVERLOAD(3) | |
ADDOVERLOAD(4) ADDOVERLOAD(5) | |
ADDOVERLOAD(6) ADDOVERLOAD(7) | |
ADDOVERLOAD(8) ADDOVERLOAD(9) | |
ADDOVERLOAD(10) ADDOVERLOAD(11) | |
ADDOVERLOAD(12) ADDOVERLOAD(13) | |
ADDOVERLOAD(14) ADDOVERLOAD(15) | |
#elif TEST == 2 | |
#ifdef OPT | |
// This forces the compiler to parse the body of the friend function so that we can compare the compile time with the non-optimized version | |
#define ADDOVERLOAD9(n) template struct S2<void, n>; | |
#else | |
// The candidates include everything in the global namespace, std namespace and the ones defined in ostream | |
#define ADDOVERLOAD9(n) std::ostream& operator<<(std::ostream& os, const S2<void, n>& s) { os << s() << ""; return os; } | |
#endif | |
#define ADDOVERLOAD8(n) ADDOVERLOAD9((n) * 2) ADDOVERLOAD9((n) * 2 + 1) | |
#define ADDOVERLOAD7(n) ADDOVERLOAD8((n) * 2) ADDOVERLOAD8((n) * 2 + 1) | |
#define ADDOVERLOAD6(n) ADDOVERLOAD7((n) * 2) ADDOVERLOAD7((n) * 2 + 1) | |
#define ADDOVERLOAD5(n) ADDOVERLOAD6((n) * 2) ADDOVERLOAD6((n) * 2 + 1) | |
#define ADDOVERLOAD4(n) ADDOVERLOAD5((n) * 2) ADDOVERLOAD5((n) * 2 + 1) | |
#define ADDOVERLOAD3(n) ADDOVERLOAD4((n) * 2) ADDOVERLOAD4((n) * 2 + 1) | |
#define ADDOVERLOAD2(n) ADDOVERLOAD3((n) * 2) ADDOVERLOAD3((n) * 2 + 1) | |
#define ADDOVERLOAD1(n) ADDOVERLOAD2((n) * 2) ADDOVERLOAD2((n) * 2 + 1) | |
#define ADDOVERLOAD0(n) ADDOVERLOAD1((n) * 2) ADDOVERLOAD1((n) * 2 + 1) | |
// This adds 512 'operator<<' overloads to the global namespace | |
ADDOVERLOAD0(0); | |
#endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment