Skip to content

Instantly share code, notes, and snippets.

@xiangfan-ms
Created March 7, 2017 21:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save xiangfan-ms/359e4ef424625bdadec1fc2d2708792b to your computer and use it in GitHub Desktop.
Save xiangfan-ms/359e4ef424625bdadec1fc2d2708792b to your computer and use it in GitHub Desktop.
Operator
#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