1 | #pragma once |
2 | |
3 | #include <c10/util/C++17.h> |
4 | #include <c10/util/ConstexprCrc.h> |
5 | #include <c10/util/IdWrapper.h> |
6 | #include <c10/util/string_view.h> |
7 | #include <cinttypes> |
8 | #include <functional> |
9 | |
10 | namespace c10 { |
11 | namespace util { |
12 | |
13 | // TODO Make it work for more compilers |
14 | |
15 | // Intel compiler works |
16 | #if defined(__INTEL_COMPILER) |
17 | #define C10_TYPENAME_SUPPORTS_CONSTEXPR 0 |
18 | #define C10_TYPENAME_CONSTEXPR |
19 | |
20 | // Clang works |
21 | #elif defined(__clang__) |
22 | |
23 | // except for NVCC |
24 | #if defined(__CUDACC__) |
25 | #define C10_TYPENAME_SUPPORTS_CONSTEXPR 0 |
26 | #define C10_TYPENAME_CONSTEXPR |
27 | #else |
28 | #define C10_TYPENAME_SUPPORTS_CONSTEXPR 1 |
29 | #define C10_TYPENAME_CONSTEXPR constexpr |
30 | #endif |
31 | |
32 | // Windows works |
33 | #elif defined(_MSC_VER) |
34 | |
35 | // except for NVCC |
36 | #if defined(__CUDACC__) |
37 | #define C10_TYPENAME_SUPPORTS_CONSTEXPR 0 |
38 | #define C10_TYPENAME_CONSTEXPR |
39 | #else |
40 | #define C10_TYPENAME_SUPPORTS_CONSTEXPR 1 |
41 | #define C10_TYPENAME_CONSTEXPR constexpr |
42 | #endif |
43 | |
44 | // GCC works |
45 | #elif defined(__GNUC__) |
46 | |
47 | // except when gcc < 9 |
48 | #if (__GNUC__ < 9) || defined(__CUDACC__) |
49 | #define C10_TYPENAME_SUPPORTS_CONSTEXPR 0 |
50 | #define C10_TYPENAME_CONSTEXPR |
51 | #else |
52 | #define C10_TYPENAME_SUPPORTS_CONSTEXPR 1 |
53 | #define C10_TYPENAME_CONSTEXPR constexpr |
54 | #endif |
55 | |
56 | // some other compiler we don't know about |
57 | #else |
58 | #define C10_TYPENAME_SUPPORTS_CONSTEXPR 1 |
59 | #define C10_TYPENAME_CONSTEXPR constexpr |
60 | #endif |
61 | |
62 | struct type_index final : IdWrapper<type_index, uint64_t> { |
63 | constexpr explicit type_index(uint64_t checksum) : IdWrapper(checksum) {} |
64 | |
65 | // Allow usage in std::map / std::set |
66 | // TODO Disallow this and rather use std::unordered_map/set everywhere |
67 | friend constexpr bool operator<(type_index lhs, type_index rhs) noexcept { |
68 | return lhs.underlyingId() < rhs.underlyingId(); |
69 | } |
70 | |
71 | friend std::ostream& operator<<(std::ostream& stream, type_index typeId) { |
72 | return stream << typeId.underlyingId(); |
73 | } |
74 | }; |
75 | |
76 | namespace detail { |
77 | |
78 | #if !defined(__clang__) && !defined(_MSC_VER) && defined(__GNUC__) && \ |
79 | __GNUC__ < 5 |
80 | // Getting __PRETTY_FUNCTION__ at compile time only works with GCC >= 5 |
81 | #error "You're running a too old version of GCC. We need GCC 5 or later." |
82 | #endif |
83 | |
84 | #if defined(__clang__) && __clang_major__ < 4 |
85 | // Getting __PRETTY_FUNCTION__ at compile time only works with Clang >= 4 |
86 | #error "You're running a too old version of Clang. We need Clang 4 or later." |
87 | #endif |
88 | |
89 | inline constexpr string_view ( |
90 | string_view prefix, |
91 | string_view suffix, |
92 | string_view str) { |
93 | #if !defined(__CUDA_ARCH__) // CUDA doesn't like std::logic_error in device code |
94 | return (!str.starts_with(prefix) || !str.ends_with(suffix)) |
95 | ? (throw std::logic_error("Invalid pattern" ), string_view()) |
96 | : str.substr(prefix.size(), str.size() - prefix.size() - suffix.size()); |
97 | #else |
98 | return str.substr(prefix.size(), str.size() - prefix.size() - suffix.size()); |
99 | #endif |
100 | } |
101 | |
102 | template <typename T> |
103 | inline C10_TYPENAME_CONSTEXPR c10::string_view fully_qualified_type_name_impl() { |
104 | #if defined(_MSC_VER) && !defined(__clang__) |
105 | #if defined(__NVCC__) |
106 | return extract( |
107 | "c10::basic_string_view<char> c10::util::detail::fully_qualified_type_name_impl<" , |
108 | ">()" , |
109 | __FUNCSIG__); |
110 | #else |
111 | return extract( |
112 | "class c10::basic_string_view<char> __cdecl c10::util::detail::fully_qualified_type_name_impl<" , |
113 | ">(void)" , |
114 | __FUNCSIG__); |
115 | #endif |
116 | #elif defined(__clang__) |
117 | return extract( |
118 | "c10::string_view c10::util::detail::fully_qualified_type_name_impl() [T = " , |
119 | "]" , |
120 | __PRETTY_FUNCTION__); |
121 | #elif defined(__GNUC__) |
122 | return extract( |
123 | #if C10_TYPENAME_SUPPORTS_CONSTEXPR |
124 | "constexpr c10::string_view c10::util::detail::fully_qualified_type_name_impl() [with T = " , |
125 | #else |
126 | "c10::string_view c10::util::detail::fully_qualified_type_name_impl() [with T = " , |
127 | #endif |
128 | "; c10::string_view = c10::basic_string_view<char>]" , |
129 | __PRETTY_FUNCTION__); |
130 | #endif |
131 | } |
132 | |
133 | #if !defined(__CUDA_ARCH__) |
134 | template <typename T> |
135 | inline constexpr uint64_t type_index_impl() { |
136 | // Idea: __PRETTY_FUNCTION__ (or __FUNCSIG__ on msvc) contains a qualified name |
137 | // of this function, including its template parameter, i.e. including the |
138 | // type we want an id for. We use this name and run crc64 on it to get a type |
139 | // id. |
140 | #if defined(_MSC_VER) && !defined(__clang__) |
141 | return crc64(__FUNCSIG__, sizeof(__FUNCSIG__)).checksum(); |
142 | #elif defined(__clang__) |
143 | return crc64(__PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__)).checksum(); |
144 | #elif defined(__GNUC__) |
145 | return crc64(__PRETTY_FUNCTION__, sizeof(__PRETTY_FUNCTION__)).checksum(); |
146 | #endif |
147 | } |
148 | #endif |
149 | |
150 | } // namespace detail |
151 | |
152 | template <typename T> |
153 | inline constexpr type_index get_type_index() { |
154 | #if !defined(__CUDA_ARCH__) |
155 | // To enforce that this is really computed at compile time, we pass the |
156 | // type index through std::integral_constant. |
157 | return type_index{std::integral_constant< |
158 | uint64_t, |
159 | detail::type_index_impl<std::decay_t<T>>()>::value}; |
160 | #else |
161 | // There's nothing in theory preventing us from running this on device code |
162 | // except for nvcc throwing a compiler error if we enable it. |
163 | return (abort(), type_index(0)); |
164 | #endif |
165 | } |
166 | |
167 | #if !defined(TORCH_PEDANTIC) |
168 | // Use precomputed hashsum for std::string |
169 | // Needed to workaround ambiguity in class name resolution |
170 | // into __PRETTY_FUNCION__ when abovementioned class is defined in inlined |
171 | // namespace. In multi-ABI C++ library, `std::string` is an alias to |
172 | // `std::__cxx11::basic_string<char>` which depending on compiler flags can be |
173 | // resolved to `basic_string<char>` either in `std` namespace or in |
174 | // `std::__cxx11` one (`__cxx11` is an inline namespace) |
175 | template <> |
176 | inline constexpr type_index get_type_index<std::string>() { |
177 | // hashsum for std::basic_string<char> |
178 | return type_index{4193213214807308375ULL}; |
179 | } |
180 | #endif |
181 | |
182 | template <typename T> |
183 | inline C10_TYPENAME_CONSTEXPR string_view |
184 | get_fully_qualified_type_name() noexcept { |
185 | #if C10_TYPENAME_SUPPORTS_CONSTEXPR |
186 | constexpr |
187 | #else |
188 | static |
189 | #endif |
190 | string_view name = detail::fully_qualified_type_name_impl<T>(); |
191 | return name; |
192 | } |
193 | } // namespace util |
194 | } // namespace c10 |
195 | |
196 | C10_DEFINE_HASH_FOR_IDWRAPPER(c10::util::type_index); |
197 | |