├── CompilerVersions.cppm ├── CompilerVersions.h ├── Demo.cpp ├── FunctionTraits.cppm ├── FunctionTraits.h ├── LICENSE ├── README.md └── TypeTraits.h /CompilerVersions.cppm: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////// 2 | // LICENSE NOTICE 3 | // -------------- 4 | // Copyright (c) Hexadigm Systems 5 | // 6 | // Permission to use this software is granted under the following license: 7 | // https://www.hexadigm.com/GenericLib/License.html 8 | // 9 | // This copyright notice must be included in this and all copies of the 10 | // software as described in the above license. 11 | // 12 | // DESCRIPTION 13 | // ----------- 14 | // Module version of "CompilerVersions.h". Simply defers to 15 | // "CompilerVersions.h" to export all public declarations in that header. 16 | // For complete details on module support in "FunctionTraits", see 17 | // https://github.com/HexadigmSystems/FunctionTraits#moduleusage 18 | ///////////////////////////////////////////////////////////////////////////// 19 | 20 | module; 21 | 22 | ///////////////////////////////////////////////////////////// 23 | // Let "CompilerVersions.h" just below know we're building 24 | // the "CompilerVersions" module. Following is only #defined 25 | // when we are ... 26 | ///////////////////////////////////////////////////////////// 27 | #define STDEXT_BUILDING_MODULE_COMPILERVERSIONS 28 | #include "CompilerVersions.h" 29 | #undef STDEXT_BUILDING_MODULE_COMPILERVERSIONS 30 | 31 | export module CompilerVersions; 32 | 33 | ////////////////////////////////////////////////////////////////////// 34 | // Interface for this module. We simply rely on "using" declarations 35 | // in the code below to export all public declarations from 36 | // "CompilerVersions.h" above. See the following for details on using 37 | // this module: 38 | // 39 | // https://github.com/HexadigmSystems/FunctionTraits#moduleusage 40 | // 41 | // IMPORTANT: 42 | // --------- 43 | // Note that GCC is currently buggy at this writing (modules still 44 | // under development), and fails to compile the code below (so until 45 | // corrected, this module can't be used in GCC). See the following 46 | // GCC bug reports (both identifying the same issue): 47 | // 48 | // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=109679 49 | // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=113129 50 | ////////////////////////////////////////////////////////////////////// 51 | export namespace StdExt 52 | { 53 | using StdExt::tchar; 54 | using StdExt::tstring_view; 55 | using StdExt::tcout; 56 | using StdExt::GetCompilerName; 57 | } -------------------------------------------------------------------------------- /CompilerVersions.h: -------------------------------------------------------------------------------- 1 | #ifndef COMPILER_VERSIONS 2 | #define COMPILER_VERSIONS 3 | 4 | ///////////////////////////////////////////////////////////////////////////// 5 | // LICENSE NOTICE 6 | // -------------- 7 | // Copyright (c) Hexadigm Systems 8 | // 9 | // Permission to use this software is granted under the following license: 10 | // https://www.hexadigm.com/GenericLib/License.html 11 | // 12 | // This copyright notice must be included in this and all copies of the 13 | // software as described in the above license. 14 | // 15 | // DESCRIPTION 16 | // ----------- 17 | // Contains miscellaneous #defined constants to identify the target compiler 18 | // in effect (see "Compiler Identification Macros" below), the version of 19 | // C++ in effect (see "C++ Version Macros" below), and a few other 20 | // miscellaneous compiler-related declarations. Should typically be 21 | // #included first in all other files so they're immediately available if 22 | // you need to test these #defined constants for anything in those files (so 23 | // while #including it first isn't mandatory, it's usually a good idea so 24 | // they're immediately ready for use). 25 | ///////////////////////////////////////////////////////////////////////////// 26 | 27 | ///////////////////////////////////////////////////////////////////////////// 28 | // Compiler Identification Macros 29 | // ------------------------------ 30 | // The following #ifs are used to define one and only one of the following 31 | // #defined constants in order to indicate which compiler is currently in 32 | // use. Only one of the following will therefore ever be #defined (they are 33 | // all mutually exclusive): 34 | // 35 | // GCC_COMPILER 36 | // MICROSOFT_COMPILER 37 | // CLANG_COMPILER 38 | // INTEL_COMPILER 39 | // 40 | // Note that we rely on the following predefined (compiler-specific) constants 41 | // to determine the above (respectively): 42 | // 43 | // __GNUC__ 44 | // _MSC_VER 45 | // __clang__ 46 | // __INTEL_LLVM_COMPILER 47 | // 48 | // The reason for having to #define our own constants rather than just 49 | // rely on the native predefined compiler constants just above (though our own 50 | // constants are determined by them), is because the predefined constants are 51 | // sometimes #defined by other compilers as well. This makes it impossible to 52 | // tell which compiler is actually running based on checks of these predefined 53 | // constants alone. For instance, both the Clang compiler and Intel oneAPI 54 | // DPC++/C compiler #define __clang__ (since the Intel compiler is based on 55 | // Clang), so you can't rely on __clang__ to determine if the real Clang 56 | // compiler is actually running and not the Intel compiler, assuming you need to 57 | // know this (depending on what you're checking __clang__ for). Similarly, both 58 | // Clang and Intel also #define _MSC_VER when running in Microsoft VC++ 59 | // compatibility mode (i.e., when compiling code for Windows), so if _MSC_VER is 60 | // #defined then you don't know if it's actually the native (Microsoft) VC++ 61 | // compiler running or possibly Clang or Intel running in Microsoft VC++ 62 | // compatibility mode (such as when you install Clang and/or Intel on Windows 63 | // and then select one of them to compile your app, overriding the native 64 | // Microsoft VC++ compiler - they can also be run from other OSs to explicitly 65 | // compile Windows apps). 66 | // 67 | // The following #ifs therefore #define our own constants (the first group 68 | // above), for use when you need to know the real compiler that's actually 69 | // in use. The predefined constants above (the native constants for each 70 | // compiler), can still be applied however whenever the actual, real 71 | // compiler doesn't matter (for whatever your purpose is). For instance, you 72 | // would check _MSC_VER instead of MICROSOFT_COMPILER when the actual (real) 73 | // Microsoft VC++ compiler doesn't matter, i.e., all that matters is that a 74 | // compiler that's compatible with VC++ is running, namely Clang or Intel 75 | // running in Microsoft VC++ compatibility mode (so for all intents and 76 | // purposes if _MSC_VER is #defined then you can proceed as if the real VC++ 77 | // compiler is running - if another like Clang or Intel is actually running 78 | // then because they've #defined _MSC_VER it means they're running in 79 | // Microsoft compatibility VC++ mode so their behavior should be the same 80 | // as VC++ - caution advised however as you should make sure the behavior 81 | // is in fact the same depending on what you're doing, since it's not always 82 | // 100% compatible). If you need to know if the real Microsoft VC++ compiler 83 | // is running however then you would simply check MICROSOFT_COMPILER instead. 84 | // 85 | // IMPORTANT: 86 | // --------- 87 | // The order we check compilers in the following code is relevant so don't 88 | // change it if you're not sure what you're doing. For instance, we check 89 | // the Intel compiler first since it normally #defines __clang__ as well 90 | // (since the Intel oneAPI DPC++/C++ compiler is based on Clang), but the 91 | // Clang compiler never #defines __INTEL_LLVM_COMPILER so Intel needs to be 92 | // checked first (otherwise, if __clang__ is #defined and you check it first 93 | // then you can't tell if it's the real Clang or Intel that #defined it). 94 | ///////////////////////////////////////////////////////////////////////////// 95 | #if defined(__INTEL_LLVM_COMPILER) // See https://www.intel.com/content/www/us/en/docs/dpcpp-cpp-compiler/developer-guide-reference/2023-2/use-predefined-macros-to-specify-intel-compilers.html 96 | #define INTEL_COMPILER __INTEL_LLVM_COMPILER 97 | #elif defined(__clang__) // See https://clang.llvm.org/docs/LanguageExtensions.html#builtin-macros 98 | #define CLANG_COMPILER __clang__ 99 | #elif defined(__GNUC__) // See: https://gcc.gnu.org/onlinedocs/cpp/macros/predefined-macros.html#c.__GNUC__ 100 | // https://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html#Common-Predefined-Macros 101 | // https://stackoverflow.com/a/55926503 102 | #define GCC_COMPILER __GNUC__ 103 | #elif defined(_MSC_VER) // See: https://learn.microsoft.com/en-us/cpp/preprocessor/predefined-macros?view=msvc-170 104 | // https://devblogs.microsoft.com/cppblog/visual-c-compiler-version/ 105 | // Note: __INTEL_COMPILER identifies the classic Intel compiler which 106 | // is now deprecated by Intel (so we longer support it) 107 | #define MICROSOFT_COMPILER _MSC_VER 108 | #else 109 | #error "Unsupported compiler (GCC, Microsoft, Clang and Intel DPC++/C++ are the only ones supported at this writing)" 110 | #endif 111 | 112 | ////////////////////////////////////////////////////////////////// 113 | // C++ Version Macros 114 | // ------------------ 115 | // The code that follows below #defines our C++ version constants 116 | // (macros) which are all of the form CPPXX_OR_LATER, 117 | // CPPXX_OR_EARLIER and CPPXX, where "XX" is the C++ release year 118 | // (C++14, C++17, C++20, etc.). One such constant is #defined for 119 | // each C++ release year starting at C++14 (read on for C++11). 120 | // These can be used to determine which version of C++ is in 121 | // effect (currently running). See examples just below (WYSIWYG). 122 | // Note that we always assume C++11 or greater since we don't 123 | // support versions prior to that. Only CPP11 is therefore 124 | // #defined for that year, not CPP11_OR_LATER nor 125 | // CPP11_OR_EARLIER, since the former is always assumed to be 126 | // true so no need to ever explicitly test it (C++11 or later is 127 | // always assumed), and the latter also never needs to be 128 | // explicitly tested (since no version earlier than C++11 is ever 129 | // assumed), leaving only the need for #defined constant CPP11 to 130 | // specifically test for that version (if required but in reality 131 | // most users will be using much later versions by now). Lastly, 132 | // note that new constants should be added for each new release 133 | // of C++ (CPPXX_OR_LATER, CPPXX_OR_EARLIER and CPPXX). 134 | // 135 | // Example Usage (for C++17) 136 | // ------------------------- 137 | // 138 | // #if CPP17_OR_LATER 139 | // // Code that targets C++17 or later 140 | // #else 141 | // // Must be C++14 or earlier (C++14 precedes C++17) 142 | // #endif 143 | // 144 | // #if CPP17_OR_EARLIER 145 | // // Code that targets C++17 or earlier 146 | // #else 147 | // // Must be C++20 or later (C++20 succeeds C++17) 148 | // #endif 149 | // 150 | // #if CPP17 151 | // // Code that specifically targets C++17 only 152 | // #else 153 | // // Any other version of C++ besides C++17 154 | // #endif 155 | ////////////////////////////////////////////////////////////// 156 | 157 | //////////////////////////////////////////////////////// 158 | // MSFT? (_MSC_VER #defined, but also includes any 159 | // other compiler we support running in Microsoft VC++ 160 | // compatibility mode). Note that _MSVC_LANG is only 161 | // #defined however beginning in Visual Studio 2015 162 | // Update 3 (see predefined macros in MSDN). If it's 163 | // #defined then it's guaranteed to be 201402L or 164 | // greater (i.e., C++14 or greater). Again, see this in 165 | // MSDN (it's the same value as the __cplusplus macro). 166 | //////////////////////////////////////////////////////// 167 | #if defined(_MSC_VER) && defined(_MSVC_LANG) 168 | #define CPP_VERSION _MSVC_LANG // For internal use only 169 | 170 | ///////////////////////////////////////////////////////// 171 | // In MSFT (opposed to other compilers like GCC, Clang, 172 | // etc., where __cplusplus is always assumed to be 173 | // accurately #defined), __cplusplus is only accurately 174 | // #defined starting in VS 2017 15.7 Preview 3, but only 175 | // if the /Zc:__cplusplus switch is set. See: 176 | // 177 | // MSVC now correctly reports __cplusplus 178 | // https://devblogs.microsoft.com/cppblog/msvc-now-correctly-reports-__cplusplus/ 179 | // 180 | // It's always 199711L otherwise (erroneously indicating 181 | // C++98). Since we already tested for the presence of 182 | // _MSVC_LANG just above however, which is #defined 183 | // starting in Visual Studio 2015 Update 3 (as noted 184 | // above), then if we drop into the following #elif it 185 | // means we must be targeting an earlier version of VC++ 186 | // (prior to Visual Studio 2015 Update 3), so 187 | // __cplusplus is guaranteed to be 199711L at this 188 | // point, as just described (again, erroneously 189 | // indicating C++98). That's fine for our purposes 190 | // however, since all the CPPXX_OR_LATER constants 191 | // #defined below will be set to 0 in this case, and all 192 | // CPPXX_OR_EARLIER constants will be set to 1. Since 193 | // "XX" is always >= 14 (CPPXX_OR_LATER and 194 | // CPPXX_OR_EARLIER are only #defined for XX >= 14, no 195 | // such constants exists for XX=11), only C++11 should 196 | // therefore be considered in effect by default (CPP11 197 | // will be set to 1), not any later versions nor earlier 198 | // versions (all CPPXX_OR_LATER constants will be set to 199 | // 0, and all CPPXX_OR_EARLIER constants will be set to 200 | // 1). Note that C++98 is the only other official 201 | // earlier version though and __cplusplus actually 202 | // targets it in this case (because it's always 199711L 203 | // as described), but that value is erroneous since MSFT 204 | // didn't start setting it until VS 2017 15.7 Preview 3 205 | // (as described above), so we just ignore the value and 206 | // always implicitly assume C++11 instead (since all 207 | // modern MSFT compilers now support it - we don't 208 | // support C++98 since it's ancient now, and even C++11 209 | // is likely rarely used anymore). 210 | ///////////////////////////////////////////////////////// 211 | #elif defined(__cplusplus) 212 | #define CPP_VERSION __cplusplus // For internal use only 213 | #else 214 | #define CPP_VERSION 0L // For internal use only (if we arrive here then the #defined 215 | // constants that follow below will now target C++11 - we don't 216 | // support anything earlier, namely C++98) 217 | #endif 218 | 219 | ////////////////////////////////////////////////////////// 220 | // Next version of C++ after C++26? This version is still 221 | // unknown at this writing but the version # we're 222 | // testing here (202700L) will realistically work for our 223 | // purposes (to see if we're targeting the next version 224 | // after C++26). Presumably it will be 202700L or greater, 225 | // and C++26, which hasn't been released yet at this 226 | // writing, will presumably be "2026??" for some "??". 227 | // We'll properly update this however once the version 228 | // that succeeds C++26 is officially released (so 229 | // although the version we're testing here is still 230 | // potentially brittle, it's realistically guaranteed to 231 | // work). 232 | ////////////////////////////////////////////////////////// 233 | #if CPP_VERSION >= 202700L 234 | // #define CPP?? 1 // TBD (set "??" to the next version after C++26) 235 | #define CPP26 0 236 | #define CPP23 0 237 | #define CPP20 0 238 | #define CPP17 0 239 | #define CPP14 0 240 | #define CPP11 0 241 | // #define CPP??_OR_LATER 1 // TBD (set "??" to the next version after C++26) 242 | #define CPP26_OR_LATER 1 243 | #define CPP23_OR_LATER 1 244 | #define CPP20_OR_LATER 1 245 | #define CPP17_OR_LATER 1 246 | #define CPP14_OR_LATER 1 247 | 248 | // C++26 (testing for > C++23 here so if true then we must be dealing with C++26 at this point) 249 | #elif CPP_VERSION > 202302L 250 | //#define CPP?? 0 // TBD (set "??" to the next version after C++26) 251 | #define CPP26 1 252 | #define CPP23 0 253 | #define CPP20 0 254 | #define CPP17 0 255 | #define CPP14 0 256 | #define CPP11 0 257 | // #define CPP??_OR_LATER 0 // TBD (set "??" to the next version after C++26) 258 | #define CPP26_OR_LATER 1 259 | #define CPP23_OR_LATER 1 260 | #define CPP20_OR_LATER 1 261 | #define CPP17_OR_LATER 1 262 | #define CPP14_OR_LATER 1 263 | 264 | // C++23 (testing for > C++20 here so if true then we must be dealing with C++23 at this point) 265 | #elif CPP_VERSION > 202002L 266 | //#define CPP?? 0 // TBD (set "??" to the next version after C++26) 267 | #define CPP26 0 268 | #define CPP23 1 269 | #define CPP20 0 270 | #define CPP17 0 271 | #define CPP14 0 272 | #define CPP11 0 273 | // #define CPP??_OR_LATER 0 // TBD (set "??" to the next version after C++26) 274 | #define CPP26_OR_LATER 0 275 | #define CPP23_OR_LATER 1 276 | #define CPP20_OR_LATER 1 277 | #define CPP17_OR_LATER 1 278 | #define CPP14_OR_LATER 1 279 | 280 | // C++20 (testing for > C++17 here so if true then we must be dealing with C++20 at this point) 281 | #elif CPP_VERSION > 201703L 282 | //#define CPP?? 0 // TBD (set "??" to the next version after C++26) 283 | #define CPP26 0 284 | #define CPP23 0 285 | #define CPP20 1 286 | #define CPP17 0 287 | #define CPP14 0 288 | #define CPP11 0 289 | // #define CPP??_OR_LATER 0 // TBD (set "??" to the next version after C++26) 290 | #define CPP26_OR_LATER 0 291 | #define CPP23_OR_LATER 0 292 | #define CPP20_OR_LATER 1 293 | #define CPP17_OR_LATER 1 294 | #define CPP14_OR_LATER 1 295 | 296 | // C++17 (testing for > C++14 here so if true then we must be dealing with C++17 at this point) 297 | #elif CPP_VERSION > 201402L 298 | //#define CPP?? 0 // TBD (set "??" to the next version after C++26) 299 | #define CPP26 0 300 | #define CPP23 0 301 | #define CPP20 0 302 | #define CPP17 1 303 | #define CPP14 0 304 | #define CPP11 0 305 | // #define CPP??_OR_LATER 0 // TBD (set "??" to the next version after C++26) 306 | #define CPP26_OR_LATER 0 307 | #define CPP23_OR_LATER 0 308 | #define CPP20_OR_LATER 0 309 | #define CPP17_OR_LATER 1 310 | #define CPP14_OR_LATER 1 311 | 312 | // C++14 (testing for this exact version) 313 | #elif CPP_VERSION == 201402L 314 | //#define CPP?? 0 // TBD (set "??" to the next version after C++26) 315 | #define CPP26 0 316 | #define CPP23 0 317 | #define CPP20 0 318 | #define CPP17 0 319 | #define CPP14 1 320 | #define CPP11 0 321 | // #define CPP??_OR_LATER 0 // TBD (set "??" to the next version after C++26) 322 | #define CPP26_OR_LATER 0 323 | #define CPP23_OR_LATER 0 324 | #define CPP20_OR_LATER 0 325 | #define CPP17_OR_LATER 0 326 | #define CPP14_OR_LATER 1 327 | 328 | // C++11 (testing for < C++14 here so we implicitly assume C++11 - only other (official) earlier version is C++98 which we don't support) 329 | #else 330 | //#define CPP?? 0 // TBD (set "??" to the next version after C++26) 331 | #define CPP26 0 332 | #define CPP23 0 333 | #define CPP20 0 334 | #define CPP17 0 335 | #define CPP14 0 336 | #define CPP11 1 // Implicitly assumed at this point (we don't support C++98, the only official earlier version) 337 | // #define CPP??_OR_LATER 0 // TBD (set "??" to the next version after C++26) 338 | #define CPP26_OR_LATER 0 339 | #define CPP23_OR_LATER 0 340 | #define CPP20_OR_LATER 0 341 | #define CPP17_OR_LATER 0 342 | #define CPP14_OR_LATER 0 343 | #endif 344 | 345 | #undef CPP_VERSION // Done with this (for internal use only) 346 | 347 | ////////////////////////////////////////////////////////////////// 348 | // CPPXX_OR_EARLIER (#defined) constants that can be tested 349 | // instead of the CPPXX_OR_LATER #defined constants above. You 350 | // can use either the CPPXX_OR_LATER and/or CPPXX_OR_EARLIER 351 | // constants based on your needs (to determine which version of 352 | // C++ is in effect, C++14, C++17, C++20, etc.). We always assume 353 | // C++11 or greater however so no specific constant exists to 354 | // target earlier versions (C++98 the only official earlier 355 | // version). Note that a new CPPXX_OR_EARLIER constant should be 356 | // added for each new release of C++ 357 | // 358 | // Example usage: 359 | // 360 | // #if CPP17_OR_EARLIER 361 | // // Code that targets C++17 or earlier 362 | // #else 363 | // // Code that targets C++20 or greater (C++20 succeeds C++17) 364 | // #endif 365 | ////////////////////////////////////////////////////////////////// 366 | //#define CPP26_OR_EARLIER (!CPP??_OR_LATER) // TBD (set "??" to the next version after C++26) 367 | #define CPP23_OR_EARLIER (!CPP26_OR_LATER) 368 | #define CPP20_OR_EARLIER (!CPP23_OR_LATER) 369 | #define CPP17_OR_EARLIER (!CPP20_OR_LATER) 370 | #define CPP14_OR_EARLIER (!CPP17_OR_LATER) 371 | 372 | /////////////////////////////////////////////////// 373 | // Sanity check only. Should always be false (for 374 | // internal use only - #defined further below if 375 | // required) 376 | /////////////////////////////////////////////////// 377 | #if defined(DECLARE_PUBLIC_MACROS_ONLY) 378 | #error "DECLARE_PUBLIC_MACROS_ONLY already #defined (for internal use on a per file basis only so never should be)" 379 | #endif 380 | 381 | #if CPP20_OR_LATER 382 | ///////////////////////////////////////////////////////////////////////// 383 | // Pick up standard library feature-test macros from (C++20 or 384 | // later). See the following 2 links (which mostly duplicate each other 385 | // though 1st link has more info but 2nd link explains the "Value" 386 | // column in the "Notes" section at the bottom - it also links back to 387 | // the 1st link): 388 | // https://en.cppreference.com/w/cpp/feature_test#Library_features 389 | // https://en.cppreference.com/w/cpp/utility/feature_test 390 | // 391 | // Note that there are also predefined language feature-test macros 392 | // (opposed to "library" feature-test macros above) so not 393 | // required (macros are always predefined). See: 394 | // https://en.cppreference.com/w/cpp/feature_test#Language_features 395 | // 396 | // See here for "__has_include" usage example (2nd link from GCC docs): 397 | // https://en.cppreference.com/w/cpp/feature_test#Example 398 | // https://gcc.gnu.org/onlinedocs/cpp/_005f_005fhas_005finclude.html 399 | ///////////////////////////////////////////////////////////////////////// 400 | #if defined(__has_include) // C++17 feature so should always be true at this point 401 | // normally (since we tested for C++20 or later just above) 402 | 403 | // available in C++20 or later (tested for above so normally true) 404 | #if __has_include() 405 | #include 406 | #endif 407 | #endif 408 | 409 | //////////////////////////////////////////////////////////////// 410 | // "import std" supported? (C++23 feature). See: 411 | // 412 | // Modules "std" and "std.compat" (official C++23 document) 413 | // https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2022/p2465r3.pdf 414 | // 415 | // "__cpp_lib_modules" mentioned in above link (but read on for details) 416 | // https://en.cppreference.com/w/cpp/feature_test#cpp_lib_modules 417 | // 418 | // MSFT specific but occasionally provides some standard insight 419 | // https://www.youtube.com/watch?v=Dk_C_E8AtRs&t=1331s 420 | // 421 | // Note that at this writing the C++ feature macro 422 | // __cpp_lib_modules (used to indicate that "import std" and 423 | // "import std.compat" are supported as of C++23) may not be 424 | // #defined due to (still) evolving compiler support (still in 425 | // a state of flux at this writing). Moreover, even if it is 426 | // #defined such as in the latest editions of MSVC, we can't 427 | // safely rely on it yet without the user's permission (at this 428 | // writing). It may still be experimental that is and in fact, 429 | // even though __cpp_lib_modules will be #defined on MSVC at 430 | // this writing (in recent versions of Visual Studio 2022), 431 | // MSVC doesn't yet support mixing "import std" and headers 432 | // from the "std" library (at least in the same file). This is 433 | // officially documented by MSFT here: 434 | // 435 | // https://learn.microsoft.com/en-us/cpp/cpp/tutorial-import-stl-named-module?view=msvc-170#standard-library-named-module-considerations 436 | // 437 | // However, according to Stephan Lavajej of MSFT in the 438 | // following video (first link goes straight to the issue): 439 | // 440 | // https://www.youtube.com/watch?v=Dk_C_E8AtRs&t=2181s // This specific issue (at 36:21 in the video) 441 | // https://www.youtube.com/watch?v=Dk_C_E8AtRs&t=1331s // Full description of "import std" and "import std.compat" in general (at 22:11 in the video) 442 | // https://www.youtube.com/watch?v=Dk_C_E8AtRs // Start of video (describes C++23 features in general) 443 | // 444 | // He indicates (in the 1st link) that mixing "import std" 445 | // and "std" headers from the "std" library is not yet 446 | // supported, but confirms it eventually will be (since the 447 | // standard itself supports it). For now then we can't just 448 | // implement an "import std" statement in the following code by 449 | // checking if __cpp_lib_modules is #defined since our use of 450 | // "import std" will still interfere with any "std" headers the 451 | // user's own project may be #including (if the user's project 452 | // doesn't rely on "import std" itself yet). However, if the 453 | // user's project does rely on "import std" then they can 454 | // simply grant permission for us to use it as well (by 455 | // #defining STDEXT_IMPORT_STD_EXPERIMENTAL which we rely on in 456 | // the following call). We'll likely remove 457 | // STDEXT_IMPORT_STD_EXPERIMENTAL in a later release however 458 | // and rely solely on __cpp_lib_modules instead, once "import 459 | // std" is fully supported by all target compilers (so issues 460 | // like mixing "import std" with the "std" headers in MSVC no 461 | // longer a problem). For now however, 462 | // STDEXT_IMPORT_STD_EXPERIMENTAL can #defined by users who 463 | // wish us to rely on "import std" instead of us #including 464 | // individual headers (dependencies) from the "std" library in 465 | // the usual C++ way. If STDEXT_IMPORT_STD_EXPERIMENTAL is 466 | // #defined however then it's assumed both the target compiler 467 | // AND the user's own project supports "import std" (i.e., it's 468 | // assumed the compiler itself is set up to handle it which can 469 | // vary among different compilers - it may not be available 470 | // out-of-the-box for instance depending on the compiler, and 471 | // the user's own project must also support it, so in MSVC 472 | // projects for instance it's assumed the user is also using 473 | // "import std" everywhere to prevent our own call to "import 474 | // std" below from conflicting with any #include statements 475 | // from the "std" library in the user's project - again, mixing 476 | // the two isn't currently supported by MSVC). It's the user's 477 | // responsibility to ensure this or compilation will likely 478 | // fail, usually with cryptic compiler errors. Note that the 479 | // following ignores STDEXT_IMPORT_STD_EXPERIMENTAL however if 480 | // C++23 or later isn't even targeted (via the check for 481 | // CPP23_OR_LATER seen below). STDEXT_IMPORT_STD_EXPERIMENTAL 482 | // normally implies C++23 or later however but we check for 483 | // CPP23_OR_LATER anyway to play it safe (in addition to the 484 | // check for version 19.35 when targeting MSVC as seen, which 485 | // is when this feature became available in MSVC, and the check 486 | // for STDEXT_IMPORTED_STD as well, which prevents redundant 487 | // calls to "import std" if it's already been previously called 488 | // - note that redundant "import" statements are harmless 489 | // though but STDEXT_IMPORTED_STD won't normally be #defined 490 | // at this point anyway - checking it for future use only). 491 | //////////////////////////////////////////////////////////////// 492 | #if CPP23_OR_LATER && \ 493 | defined(STDEXT_IMPORT_STD_EXPERIMENTAL) && \ 494 | (!defined(MICROSOFT_COMPILER) || _MSC_VER >= 1935) && \ 495 | !defined(STDEXT_IMPORTED_STD) 496 | import std; 497 | #define STDEXT_IMPORTED_STD 498 | #endif 499 | 500 | ////////////////////////////////////////////////////////////////// 501 | // USE_CONCEPTS. #defined constant to control whether concepts 502 | // should be used or not wherever required in all remaining code. 503 | // Concepts only became available in C++20 so by default we 504 | // #define USE_CONCEPTS in C++20 or later (assuming the C++ 505 | // feature test macro __cpp_lib_concepts is also #defined which 506 | // we test for here - normally should test true in C++20). 507 | // USE_CONCEPTS is #undefined in C++17 and earlier so we 508 | // therefore rely on "static_assert" instead of concepts. If you 509 | // prefer to always rely on "static_assert" even in C++20 and 510 | // later however then you can just #undef it below. This may even 511 | // be preferable since I often find "static_assert" messages more 512 | // visible and clear than concept messages (in some situations 513 | // anyway). In any case, it's your responsibility to explicitly 514 | // test if USE_CONCEPTS is #defined and apply concepts if so or 515 | // "static_assert" otherwise (or you can just explicitly check 516 | // the feature-test macro __cpp_lib_concepts instead but unlike 517 | // the latter, USE_CONCEPTS can be turned off by #undefining it 518 | // here - you wouldn't want to do that for __cpp_lib_concepts). 519 | // One way to do this (rely on USE_CONCEPTS) that we consistently 520 | // rely on ourselves is as per the following example (though you 521 | // need not follow this model but it works well IMHO). 522 | // 523 | // Let's say you want a concept to ensure some type is "const" 524 | // using "std::is_const". You could therefore declare a concept 525 | // like so (note that I've applied the suffix "_c" to the concept 526 | // name as a convention to indicate it's a concept but it's up to 527 | // you if you want to follow this convention): 528 | // 529 | // #if defined(USE_CONCEPTS) 530 | // template 531 | // concept IsConst_c = std::is_const_v; 532 | // 533 | // #define IS_CONST_C StdExt::IsConst_c // Assuming the "StdExt" namespace for this example 534 | // #else 535 | // #define IS_CONST_C typename 536 | // #define STATIC_ASSERT_IS_CONST(T) static_assert(std::is_const_v, \ 537 | // "\"" #T "\" must be const (i.e., " \ 538 | // "it must satisfy \"std::is_const\""); 539 | // #endif 540 | // 541 | // Then in any code that requires this concept you can simply do 542 | // this: 543 | // 544 | // template 545 | // struct Whatever 546 | // { 547 | // #if !defined(USE_CONCEPTS) 548 | // ////////////////////////////////////////////////// 549 | // // Kicks in if concepts not supported, otherwise 550 | // // corresponding concept kicks in in the template 551 | // // declaration above instead (macro for this 552 | // // above resolves to the "typename" keyword when 553 | // // concepts aren't supported and the following 554 | // // "static_assert" is then used instead) 555 | // ////////////////////////////////////////////////// 556 | // STATIC_ASSERT_IS_CONST(T); 557 | // #endif 558 | // 559 | // // ... 560 | // } 561 | // 562 | // Template arg "T" in the above struct therefore utilizes the 563 | // IS_CONST_C concept if concepts are supported but if not, then 564 | // IS_CONST_C simply resolves to the "typename" keyword instead, 565 | // and the STATIC_ASSERT_IS_CONST macro then kicks in to do a 566 | // "static_assert". You can therefore apply the above technique 567 | // to all your concepts and we do throughout all remaining code 568 | // where applicable. 569 | ////////////////////////////////////////////////////////////////// 570 | #if defined(__cpp_lib_concepts) // See https://en.cppreference.com/w/cpp/feature_test#cpp_lib_concepts 571 | // There's also cpp_concepts: https://en.cppreference.com/w/cpp/feature_test#cpp_concepts 572 | #define USE_CONCEPTS 573 | #endif 574 | 575 | //////////////////////////////////////////////////////////////// 576 | // Official language feature macro for both generic lambdas in 577 | // C++14 and lambda templates in C++20: 578 | // 579 | // https://en.cppreference.com/w/cpp/feature_test#cpp_generic_lambdas 580 | // 581 | // Also see its official feature spec (P0428R2): 582 | // 583 | // https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0428r2.pdf 584 | // 585 | // Note that the same feature macro "__cpp_generic_lambdas" is 586 | // used to test for both generic lambdas in C++14 AND lambda 587 | // templates in C++20, but for generic lambdas the version to 588 | // test for is 201304 and for lambda templates it's 201707 589 | // (what we're checking for below). Lambda templates however 590 | // are still considered a form of generic lambdas by the C++ 591 | // standard (why the same feature macro was obviously chosen 592 | // for both), and the term "lambda template", while widely used, 593 | // isn't actually an official name in the C++ standard. "Lambda 594 | // templates" are effectively just generic lambdas that support 595 | // the usual C++ template syntax though I think the standard 596 | // should have made a distinction between them. 597 | // 598 | // In any case, note that we're currently inside an "#if 599 | // CPP20_OR_LATER" block so the following check is only 600 | // conducted in C++20 or later but that's normally implied by 601 | // "__cpp_generic_lambdas" itself anyway (when greater than or 602 | // equal to 201707). Checking for the following inside an "#if 603 | // CPP20_OR_LATER" block however not only makes it clear this 604 | // is a C++20 feature, but it avoids the (albeit rare) 605 | // possibility that "__cpp_generic_lambdas" is >= 201707 in an 606 | // earlier C++ version (if some compiler already supported it 607 | // before it was officially released in C++20 - for our 608 | // purposes we prefer to rely on its official release in C++20 609 | // only which is guaranteed since the following occurs inside 610 | // an "#if CPP20_OR_LATER" block as noted). Note that relying 611 | // on LAMBDA_TEMPLATES_SUPPORTED instead of checking for "#if 612 | // CPP20_OR_LATER" is also more reliable since some compilers 613 | // don't support lambda templates in their early releases of 614 | // C++20 (so while CPP20_OR_LATER may be #defined, lambda 615 | // templates may still not be available). Microsoft for 616 | // instance doesn't support them in C++20 until MSVC V19.27 617 | // (released with VS V16.7), so testing "#if CPP20_OR_LATER" in 618 | // versions just prior to that will return true even though 619 | // lambda templates aren't supported yet (leading to compiler 620 | // errors or other possible issues). 621 | //////////////////////////////////////////////////////////////// 622 | #if defined(__cpp_generic_lambdas) 623 | #if __cpp_generic_lambdas >= 201707 624 | #define LAMBDA_TEMPLATES_SUPPORTED 625 | #endif 626 | #endif 627 | 628 | ///////////////////////////////////////////////////////////////////////////// 629 | // If both conditions we're testing here are true then this header is now 630 | // being #included by a client in the module version of the header (the 631 | // module named "CompilerVersions" in "CompilerVersions.cppm" or whatever 632 | // the user may have renamed the latter file's extension to). In this case 633 | // we know modules (a C++20 feature) are supported (we already tested 634 | // CPP20_OR_LATER above), but we'll also add a check for the official C++ 635 | // feature constant "__cpp_modules" in a later release since it's not yet 636 | // supported by all compilers (we'll add that check to the following). The 637 | // 2nd condition just below then checks if STDEXT_USE_MODULES was #defined 638 | // by the user themself, indicating they've added the ".cppm" files to their 639 | // project (in this case the "CompilerVersions.cppm" module) and wish to use 640 | // it. In that case we'll go on to declare only #defined (public) macros in 641 | // this header instead of all other C++ declarations that are also normally 642 | // declare (when STDEXT_USE_MODULES isn't #defined), since those C++ 643 | // declarations will now originate from the module itself (which we import 644 | // via "import CompilerVersions" in the code just below, though the user 645 | // might also do this themself). Therefore, when a user #includes this 646 | // header in the module version, only the macros in the header will need to 647 | // be declared since C++ modules don't export #defined macros (we #define an 648 | // internal constant DECLARE_PUBLIC_MACROS_ONLY below to facilitate this). 649 | // If the user hasn't #defined STDEXT_USE_MODULES however then they wish to 650 | // use this header normally (declare everything as usual), even if they've 651 | // also added the module to their project as well (which will still be 652 | // successfully compiled), though that would be very unlikely (why add the 653 | // module if they're not going to use it? - if they've added it then they'll 654 | // normally #define STDEXT_USE_MODULES as well). As for the 2nd condition 655 | // we're testing here, STDEXT_BUILDING_MODULE_COMPILERVERSIONS (an internal 656 | // constant), this is #defined by the module itself (CompilerVersions.cppm) 657 | // before #including this header in its global fragment section. The module 658 | // then simply exports all public declarations from this header in its 659 | // purview section via "using" declarations. 660 | // STDEXT_BUILDING_MODULE_COMPILERVERSIONS is therefore #defined only if the 661 | // module itself is now being built. If #defined then it must be the module 662 | // itself now #including us and if not #defined then it must be a client of 663 | // the module #including us instead (which is what the following tests for 664 | // in its 2nd condition - if both conditions are true then it must be a user 665 | // #including us, not the module itself, so we only declare the #defined 666 | // macros in this header instead of all other declarations, as described 667 | // above). 668 | ///////////////////////////////////////////////////////////////////////////// 669 | #if defined(STDEXT_USE_MODULES) && !defined(STDEXT_BUILDING_MODULE_COMPILERVERSIONS) 670 | ////////////////////////////////////////////////////// 671 | // Importing the "CompilerVersions" module as a 672 | // convenience to module clients. This way clients 673 | // can simply #include this header without having to 674 | // explicitly import the "CompilerVersions" module 675 | // themselves (since this header does it for them). 676 | // It's harmless though if they've already imported 677 | // the "CompilerVersions" module on their own (which 678 | // is more natural anyway - relying on this header 679 | // may even confuse some users who might not realize 680 | // that a #include "CompilerVersions.h" statement is 681 | // actually importing the "CompilerVersions" module 682 | // to pick up all public declarations in the header 683 | // instead of declaring them in the header itself - 684 | // this is how the header behaves when 685 | // STDEXT_USE_MODULES is #defined). #including this 686 | // header however will also pick up all public macros 687 | // in the header since modules don't export macros 688 | // (so if clients simply import the 689 | // "CompilerVersions" module and don't #include this 690 | // header, they'll have access to all exported 691 | // declarations in the module which originate from 692 | // this header, but none of the macros in the header 693 | // - fine if they don't use any of them though). 694 | ////////////////////////////////////////////////////// 695 | import CompilerVersions; 696 | 697 | ////////////////////////////////////////////////////// 698 | // All declarations in this module are now available 699 | // via the import statement just above. Only macros 700 | // will be missing since modules don't export them 701 | // (so the above import statement doesn't include 702 | // them). The following will therefore ensure that 703 | // only (public) macros will be #defined in the 704 | // remaining code that follows. All other 705 | // declarations are preprocessed out because they're 706 | // already available via the import statement just 707 | // above. 708 | ////////////////////////////////////////////////////// 709 | #define DECLARE_PUBLIC_MACROS_ONLY 710 | #endif 711 | #endif // #if CPP20_OR_LATER 712 | 713 | #if !defined(DECLARE_PUBLIC_MACROS_ONLY) 714 | // "import std" not currently in effect? (C++23 or later) 715 | #if !defined(STDEXT_IMPORTED_STD) 716 | // Standard C/C++ headers 717 | #include // For declaring "tcout" later on (as 718 | // an inline variable if C++17 or later 719 | // or a macro otherwise - don't really 720 | // need to #include in the macro case 721 | // but we do anyway as a convenience 722 | // to users) 723 | 724 | ////////////////////////////////////////////////////////////// 725 | // Go on to #include if available, a C++17 726 | // feature so we check for that first (using our own 727 | // CPP17_OR_LATER constant #defined earlier). Note that for 728 | // our purposes and likely those of most users, if C++17 or 729 | // later is detected, then it's assumed that is 730 | // also available even though it might not be (due to 731 | // compliancy issues with a given compiler, in particular 732 | // early versions of C++17, where may not have 733 | // been available in some compilers yet). For most users who 734 | // #include "CompilerVersions.h" however, it's just easier to 735 | // test CPP17_OR_LATER and if true, assume that 736 | // is always available, otherwise they'd have to check if 737 | // "__has_include" is both #defined (it might not be in early 738 | // versions of C++17), AND returns true for 739 | // (also not guaranteed). In very rare cases 740 | // might even be available even if "__has_include" isn't 741 | // #defined in which case there's no standard way to test if 742 | // is available or not. 743 | // 744 | // To deal with the hassle and inconvenience of this 745 | // situation, the following code goes on to check if 746 | // "__has_include" is both #defined AND returns true for 747 | // , aborting compilation with an #error message 748 | // if both don't hold (meaning we're dealing with a 749 | // non-compliant version of C++17 or later). If compilation 750 | // isn't aborted (normally), then CPP17_OR_LATER can be 751 | // checked by all users from here on and if true, it 752 | // guarantees that is available (no need to 753 | // check "__has_include" anymore). Very few users should take 754 | // issue with this approach, i.e., aborting compilation if 755 | // CPP17_OR_LATER is detected but "__has_include" either 756 | // isn't #defined or returns false for . Neither 757 | // should normally happen in versions of C++17 or later that 758 | // conform with the C++ standard (unless they're early 759 | // versions of C++17 or perhaps some special compiler 760 | // switch/setting is in effect). We just abort compilation in 761 | // this case and won't worry about the rare user that might 762 | // take issue with the situation (the design is intended to 763 | // make life easier for the vast majority of users instead). 764 | ////////////////////////////////////////////////////////////// 765 | #if CPP17_OR_LATER 766 | ////////////////////////////////////////////////////////// 767 | // "__has_include" is a C++17 feature so should normally 768 | // be #defined at this point (from C++17 check just 769 | // above). If not then we're not dealing with a compliant 770 | // C++17 compiler so we abort compilation as described 771 | // above (would be very rare though these days). Note 772 | // that in C++20 and later the feature testing constant 773 | // "__cpp_lib_string_view" could also be checked as an 774 | // alternative (Google this for details) but the 775 | // following does the job. 776 | ////////////////////////////////////////////////////////// 777 | #if defined(__has_include) 778 | /////////////////////////////////////////////////////// 779 | // is a C++17 header so should normally 780 | // be available at this point as well (again, from 781 | // C++17 check above). If not then again, we're not 782 | // dealing with a compliant C++17 compiler so we abort 783 | // compilation as described above (would be very rare 784 | // though these days). 785 | /////////////////////////////////////////////////////// 786 | #if __has_include() 787 | #include 788 | #else 789 | #error "C++17 or later was detected but isn't available (a C++17 feature so it normally should be). The target compiler isn't compliant with C++17 and therefore not supported (see code comments for further details)" 790 | #endif 791 | #else 792 | #error "C++17 or later was detected but '__has_include' isn't #defined (a C++17 feature so it normally should be). The target compiler isn't compliant with C++17 and therefore not supported (see code comments for further details)" 793 | #endif 794 | #endif 795 | #endif // #if !defined(STDEXT_IMPORTED_STD) 796 | #endif // #if !defined(DECLARE_PUBLIC_MACROS_ONLY) 797 | 798 | /////////////////////////////////////////////////////// 799 | // MSFT? (or any other compiler we support running in 800 | // Microsoft VC++ compatibility mode - "Clang" and 801 | // "Intel" are the only ones we currently support that 802 | // are affected) 803 | /////////////////////////////////////////////////////// 804 | #if defined(_MSC_VER) 805 | //////////////////////////////////////////////////////////////// 806 | // Mainly to pick up TCHAR (typedef) and _T macro (for our 807 | // specific needs). These are well-known to Microsoft 808 | // developers. Google for the details if you're not already 809 | // familiar, starting with Microsoft's own article: 810 | // 811 | // Generic-Text Mappings in tchar.h 812 | // https://learn.microsoft.com/en-us/cpp/text/generic-text-mappings-in-tchar-h?view=msvc-170). 813 | // 814 | // However, we'll later create an alias named "tchar" and set 815 | // it to TCHAR rather than rely on TCHAR directly (since TCHAR 816 | // is in the global namespace while "tchar" will be in our own 817 | // "StdExt" namespace which is what we want). On Microsoft 818 | // platforms TCHAR and tchar will therefore always refer to the 819 | // same type, either "wchar_t" or "char", though normally the 820 | // former in modern Windows (which is based on Unicode so both 821 | // UNICODE and _UNICODE will be #defined normally which results 822 | // in "wchar_t" - again, see the above article for starters). 823 | // 824 | // Note that it's expected that will be found in the 825 | // #include search path which is normally the case when 826 | // compiling for Windows. For non-Microsoft compilers however, 827 | // unless they're running in Microsoft VC++ compatibility mode 828 | // in which case they'll come through here as well (Clang and 829 | // Intel compilers are the only ones we currently support that 830 | // might), then we'll set "tchar" to "char" further below (for 831 | // our purposes "tchar" will always be "char" on non-Microsoft 832 | // platforms), and also #define _T as well. See these 833 | // declarations further below. We can then rely on "tchar" for 834 | // all character types and the _T macro as well when required, 835 | // no different from how it's done on Microsoft platforms (even 836 | // though TCHAR is normally used on Microsoft platforms instead 837 | // of tchar but when targeting Microsoft they now alias the 838 | // same type). 839 | //////////////////////////////////////////////////////////////// 840 | #include 841 | 842 | ////////////////////////////////////////////////////////////////// 843 | // Macros to extract the major and minor version numbers from the 844 | // predefined Microsoft constant _MSC_VER (pass the latter 845 | // constant to macros MSC_GET_VER_MAJOR or MSC_GET_VER_MINOR as 846 | // required), or the build number from the predefined Microsoft 847 | // constant _MSC_FULL_VER (pass the latter constant to macro 848 | // MSC_GET_VER_BUILD). For instance, if you're currently running 849 | // Visual C++ 2019 and _MSC_VER is 1920 and _MSC_FULL_VER is 850 | // 192027508, then the following will display "19.20.27508" (but 851 | // you normally shouldn't pass _MSC_VER and _MSC_FULL_VER 852 | // directly as seen in this example, since the constants 853 | // described further below are easier - read on): 854 | // 855 | // // Displays "19.20.27508" 856 | // tcout << MSC_GET_VER_MAJOR(_MSC_VER) << "." << 857 | // MSC_GET_VER_MINOR(_MSC_VER) << "." << 858 | // MSC_GET_VER_BUILD(_MSC_FULL_VER); 859 | // 860 | // Note that you need not call these macros directly however (as 861 | // seen in the above example) when you wish to pass _MSC_VER 862 | // itself (to extract the major or minor version number), or 863 | // _MSC_FULL_VER (to extract the build number), opposed to 864 | // passing some other constants instead (besides _MSC_VER or 865 | // _MSC_FULL_VER). When passing _MSC_VER or _MSC_FULL_VER that 866 | // is, you can simply rely on the constants MSC_VER_MAJOR, 867 | // MSC_VER_MINOR or MSC_VER_BUILD (respectively) instead, which 868 | // are #defined further below. They in turn call the following 869 | // "MSC_GET_VER_?" macros for you, passing _MSC_VER or 870 | // _MSC_FULL_VER as required. The above example can therefore be 871 | // more cleanly written like so (yielding the same result as the 872 | // above but less verbose): 873 | // 874 | // // Displays "19.20.27508" 875 | // tcout << MSC_VER_MAJOR << "." << 876 | // MSC_VER_MINOR << "." << 877 | // MSC_VER_BUILD; 878 | // 879 | // You therefore only need to call the following "MSC_GET_VER_?" 880 | // macros directly when you need to explicitly pass arguments 881 | // other than the predefined Microsoft constants _MSC_VER and 882 | // _MSC_FULL_VER. For instance, if you wish to extract the major, 883 | // minor and build numbers for, say, the first release version of 884 | // Visual Studio 2022 (version 19.30.30705), then you can pass 885 | // the following Visual Studio 2022 constants (#defined in this 886 | // header later on) to these "MSC_GET_VER_?" macros like so: 887 | // 888 | // // Displays "19.30.30705" 889 | // tcout << MSC_GET_VER_MAJOR(MSC_VER_2022) << _T(".") << 890 | // MSC_GET_VER_MINOR(MSC_VER_2022) << _T(".") << 891 | // MSC_GET_VER_BUILD(MSC_FULL_VER_2022); 892 | // 893 | // Again, you normally only need to call these "MSC_GET_VER_?" 894 | // macros directly when passing args other than the predefined 895 | // Microsoft constants _MSC_VER and _MSC_FULL_VER (since in the 896 | // latter case you can just rely on the constants MSC_VER_MAJOR, 897 | // MSC_VER_MINOR and MSC_VER_BUILD instead, as described above). 898 | ////////////////////////////////////////////////////////////////// 899 | #define MSC_GET_VER_MAJOR(MSC_VER) ((MSC_VER) / 100) // E.g, if MSC_VER is 1920 then returns 19 900 | #define MSC_GET_VER_MINOR(MSC_VER) ((MSC_VER) % 100) // E.g, if MSC_VER is 1920 then returns 20 901 | #define MSC_GET_VER_BUILD(MSC_FULL_VER) ((MSC_FULL_VER) % 100000) // E.g, if MSC_FULL_VER is 192027508 then returns 27508 902 | 903 | /////////////////////////////////////////////////////////////////// 904 | // #defined constants storing the compiler's major, minor, build 905 | // and revision numbers (for Microsoft C++). We simply strip these 906 | // out of the predefined Microsoft constants _MSC_FULL_VER, _MSC_VER, 907 | // and _MSC_BUILD. See these here at this writing: 908 | // 909 | // Predefined macros 910 | // https://learn.microsoft.com/en-us/cpp/preprocessor/predefined-macros?view=msvc-170&redirectedfrom=MSDN 911 | // 912 | // Given the following example then, where the version number is 913 | // (arbitrarily) 19.20.27508.01, the predefined Microsoft constants 914 | // would be: 915 | // 916 | // _MSC_FULL_VER = 192027508 (major, minor and build number) 917 | // _MSC_VER = 1920 (major and minor number) 918 | // _MSC_BUILD = 1 (revision number) 919 | // 920 | // The major, minor, build and revision numbers are then extracted 921 | // from the above to populate the following constants. 922 | // 923 | // Example (using the above values) 924 | // -------------------------------- 925 | // // Displays "19.20.27508.1" 926 | // tcout << MSC_VER_MAJOR << _T(".") << 927 | // MSC_VER_MINOR << _T(".") << 928 | // MSC_VER_BUILD << _T(".") << 929 | // MSC_VER_REVISION; 930 | /////////////////////////////////////////////////////////////////// 931 | #define MSC_VER_MAJOR MSC_GET_VER_MAJOR(_MSC_VER) // E.g, if _MSC_VER is 1920 then returns 19 932 | #define MSC_VER_MINOR MSC_GET_VER_MINOR(_MSC_VER) // E.g, if _MSC_VER is 1920 then returns 20 933 | #define MSC_VER_BUILD MSC_GET_VER_BUILD(_MSC_FULL_VER) // E.g, if _MSC_FULL_VER is 192027508 then returns 27508 934 | #define MSC_VER_REVISION _MSC_BUILD // Just return _MSC_BUILD as-is (1 in the above example) since it stores 935 | // the actual revision number (so doesn't have to be calculated by us) 936 | 937 | ///////////////////////////////////////////////////////////// 938 | // #defined constants corresponding to the predefined MSFT 939 | // constants _MSC_VER and _MSC_FULL_VER. One such constant 940 | // exists for each modern version of Visual Studio. MSFT 941 | // periodically moves this information around however and 942 | // currently publishes the values of _MSC_VER here: 943 | // 944 | // Microsoft Visual C++ compiler versioning 945 | // https://learn.microsoft.com/en-us/cpp/overview/compiler-versions?view=msvc-170 946 | // 947 | // Unfortunately they don't publish each _MSC_FULL_VER at 948 | // this writing (after extensive searching - go figure), but 949 | // there is the following which is presumably (hopefully) a 950 | // reliable source so the MSC_FULL_VER_? constants below were 951 | // picked up from here (not sure where they got it from but 952 | // hopefully accurate - their citations aren't sufficient): 953 | // 954 | // Microsoft Visual C++ 955 | // https://en.wikipedia.org/wiki/Microsoft_Visual_C%2B%2B#Internal_version_numbering 956 | // 957 | // Also see the following for details on how to use the macros 958 | // _MSC_VER and _MSC_FULL_VER in general (they can be compared 959 | // against the #defined constants below): 960 | // 961 | // Visual C++ Compiler Version (By Gabriel Dos Reis of MSFT) 962 | // https://devblogs.microsoft.com/cppblog/visual-c-compiler-version/ 963 | ///////////////////////////////////////////////////////////////// 964 | 965 | // Visual Studio 2005 966 | #define MSC_VER_2005 1400 967 | #define MSC_FULL_VER_2005 140050727 968 | 969 | // Visual Studio 2008 970 | #define MSC_VER_2008 1500 971 | #define MSC_FULL_VER_2008 150021022 972 | #define MSC_FULL_VER_2008_SP1 150030729 973 | 974 | // Visual Studio 2010 975 | #define MSC_VER_2010 1600 976 | #define MSC_FULL_VER_2010 160030319 977 | #define MSC_FULL_VER_2010_SP1 160040219 978 | 979 | // Visual Studio 2012 980 | #define MSC_VER_2012 1700 981 | #define MSC_FULL_VER_2012 170050727 982 | 983 | // Visual Studio 2013 984 | #define MSC_VER_2013 1800 985 | #define MSC_FULL_VER_2013 180021005 986 | 987 | // Visual Studio 2015 988 | #define MSC_VER_2015 1900 989 | #define MSC_FULL_VER_2015 190023026 990 | #define MSC_FULL_VER_2015_UPDATE_1 190023506 991 | #define MSC_FULL_VER_2015_UPDATE_2 190023918 992 | #define MSC_FULL_VER_2015_UPDATE_3 190024210 993 | 994 | // Visual Studio 2017 995 | #define MSC_VER_2017 1910 996 | #define MSC_FULL_VER_2017 191025017 997 | #define MSC_VER_2017_V15_3 1911 998 | #define MSC_VER_2017_V15_5 1912 999 | #define MSC_VER_2017_V15_6 1913 1000 | #define MSC_VER_2017_V15_7 1914 1001 | #define MSC_VER_2017_V15_8 1915 1002 | #define MSC_VER_2017_V15_9 1916 1003 | 1004 | // Visual Studio 2019 1005 | #define MSC_VER_2019 1920 1006 | #define MSC_FULL_VER_2019 192027508 1007 | #define MSC_VER_2019_V16_1 1921 1008 | #define MSC_VER_2019_V16_2 1922 1009 | #define MSC_VER_2019_V16_3 1923 1010 | #define MSC_VER_2019_V16_4 1924 1011 | #define MSC_VER_2019_V16_5 1925 1012 | #define MSC_VER_2019_V16_6 1926 1013 | #define MSC_VER_2019_V16_7 1927 1014 | #define MSC_VER_2019_V16_8_AND_9 1928 // Search for 16.8 and 16.9 here (merged by MSFT into a single version): https://learn.microsoft.com/en-us/cpp/overview/compiler-versions?view=msvc-170 1015 | #define MSC_VER_2019_V16_10_AND_11 1929 // Search for 16.10 and 16.11 here (merged by MSFT into a single version): https://learn.microsoft.com/en-us/cpp/overview/compiler-versions?view=msvc-170 1016 | 1017 | // Visual Studio 2022 1018 | #define MSC_VER_2022 1930 1019 | #define MSC_FULL_VER_2022 193030705 1020 | #define MSC_VER_2022_V17_1 1931 1021 | #define MSC_VER_2022_V17_2 1932 1022 | #define MSC_VER_2022_V17_3 1933 1023 | #define MSC_VER_2022_V17_4 1934 1024 | #define MSC_VER_2022_V17_5 1935 1025 | #define MSC_VER_2022_V17_6 1936 1026 | #define MSC_VER_2022_V17_7 1937 1027 | #define MSC_VER_2022_V17_8 1938 1028 | #define MSC_VER_2022_V17_9 1939 1029 | #define MSC_VER_2022_V17_10 1940 1030 | #define MSC_VER_2022_V17_11 1941 1031 | #define MSC_VER_2022_V17_12 1942 1032 | #define MSC_VER_2022_V17_13 1943 1033 | #define MSC_VER_2022_V17_14 1944 1034 | #else 1035 | /////////////////////////////////////////////////////// 1036 | // Native (very ancient and ugly) MSFT specific macro 1037 | // we'll adopt for other compilers as well (so we 1038 | // #define it here for GCC/Clang/Intel and any others 1039 | // we may support in the future). Used on MSFT 1040 | // platforms to append an "L" to all string literals 1041 | // (and less frequently character constants) so that, 1042 | // say, "Your string" becomes L"Your string" instead 1043 | // (when compiling for UTF-16 on Windows, as is 1044 | // normally the case there - all string literals are 1045 | // therefore wchar_t-based on MSFT platforms). We 1046 | // #define it here to do nothing however meaning 1047 | // everything will remain "char" based on non-MSFT 1048 | // platforms (see tchar further below). Only when 1049 | // compiling for MSFT will we rely on their own (real) 1050 | // _T macro from (#included above when 1051 | // targeting MSFT), which appends an "L" as described 1052 | // for Unicode builds. tchar-based string literals 1053 | // will therefore normally be "wchar_t" in most MSFT 1054 | // apps (since almost all MSFT apps in the real world 1055 | // are compiled that way now - again, "wchar_t" is 1056 | // used to store UTF-16 on all modern MSFT platforms) 1057 | /////////////////////////////////////////////////////// 1058 | #define _T(x) x 1059 | #endif // #if defined(_MSC_VER) 1060 | 1061 | #if !defined(DECLARE_PUBLIC_MACROS_ONLY) 1062 | namespace StdExt 1063 | { 1064 | /////////////////////////////////////////////////////// 1065 | // MSFT? (or any other compiler we support running in 1066 | // Microsoft VC++ compatibility mode - "Clang" and 1067 | // "Intel" are the only ones we currently support that 1068 | // are affected) 1069 | /////////////////////////////////////////////////////// 1070 | #if defined(_MSC_VER) 1071 | ////////////////////////////////////////// 1072 | // TCHAR declared in #included 1073 | // further above (Microsoft only) 1074 | ////////////////////////////////////////// 1075 | using tchar = TCHAR; 1076 | 1077 | ////////////////////////////////////////////////////// 1078 | // _UNICODE or UNICODE #defined in caller's build 1079 | // settings? Normally both are #defined in modern 1080 | // versions of Windows meaning all strings will be 1081 | // based on UTF-16 (we only check for either of these 1082 | // constants though which is fine), but if neither is 1083 | // #defined then user is compiling for ANSI normally 1084 | // (ancient now so very rare) 1085 | ////////////////////////////////////////////////////// 1086 | #if defined(_UNICODE) || defined(UNICODE) 1087 | #if CPP17_OR_LATER // Inline variables not available until C++17 ... 1088 | inline decltype(std::wcout)& tcout = std::wcout; 1089 | #else 1090 | #define tcout std::wcout 1091 | #endif 1092 | #else 1093 | #if CPP17_OR_LATER // Inline variables not available until C++17 ... 1094 | inline decltype(std::cout)& tcout = std::cout; 1095 | #else 1096 | #define tcout std::cout 1097 | #endif 1098 | #endif 1099 | #else 1100 | ///////////////////////////////////////////////////////// 1101 | // Ancient technique from MSFT we'll adopt for non-MSFT 1102 | // compilers (see earlier comments before 1103 | // which we #include on Microsoft platforms). All 1104 | // strings and other character-based types we need will 1105 | // be based on "tchar" from here on. We always set this 1106 | // to char here for non-Microsoft compilers (and TCHAR 1107 | // on Microsoft platforms - see #if above), so only the 1108 | // MSFT compiler (or other compilers running in VC++ 1109 | // compatibility mode) will use "wchar_t" instead 1110 | // normally (for Unicode builds), or "char" for 1111 | // non-Unicode builds (very rare these days). 1112 | // 1113 | // IMPORTANT: Note that you should not change this type 1114 | // to anything else on non-MSFT platforms. It must 1115 | // remain "char" in this release. If it were changed to 1116 | // something else like "wchar_t" we'd have to deal with 1117 | // "char <==> wchar_t" conversions at times which this 1118 | // release is not currently designed to do. The use of 1119 | // tchar for non-MSFT platforms simply allows us to more 1120 | // easily deal with character types in a consistent way 1121 | // (by always using "tchar" for any character types we 1122 | // process whether we're targeting MSFT or not). On 1123 | // MSFT platforms it will normally be "wchar_t" as noted 1124 | // above, but "char" is also supported on MSFT if someone 1125 | // compiles their program that way (though it's highly 1126 | // unlikely on Windows anymore - Windows apps are 1127 | // normally compiled for UTF-16 where tchar is "wchar_t" 1128 | // but "char" is also supported if compiled for ANSI). 1129 | // On non-MSFT platforms however only "char" is 1130 | // currently supported ... 1131 | ///////////////////////////////////////////////////////// 1132 | using tchar = char; // IMPORTANT - Don't change this. See comments above. 1133 | 1134 | /////////////////////////////////////////////////////// 1135 | // Always char-based in this release. See tchar alias 1136 | // above. Callers still need to explicitly #include 1137 | // in their own files however if using 1138 | // "tcout" (to pick up "std::cout" since we don't 1139 | // #include in this file) 1140 | /////////////////////////////////////////////////////// 1141 | #if CPP17_OR_LATER // Inline variables not available until C++17 ... 1142 | inline decltype(std::cout)& tcout = std::cout; 1143 | #else 1144 | #define tcout std::cout 1145 | #endif 1146 | #endif // #if defined(_MSC_VER) 1147 | 1148 | // "basic_string_view" not available until C++17 1149 | #if CPP17_OR_LATER 1150 | /////////////////////////////////////////////////////// 1151 | // All uses of "std::basic_string_view" we need will 1152 | // rely on this from here on (less verbose than using 1153 | // "basic_string_view" directly everywhere). It 1154 | // therefore always resolves to "std::string_view" on 1155 | // non-MSFT platforms (since tchar must *always* be 1156 | // "char" in this release - see its declaration 1157 | // further above), and normally (usually) 1158 | // "std::wstring" on MSFT platforms (since tchar 1159 | // normally resolves to "wchar_t" on MSFT platforms - 1160 | // again, see this further above). 1161 | // 1162 | // Note that in theory we should add a similar "using" 1163 | // statement for other types like "std::basic_string" 1164 | // as well (naming it "tstring"), among other classes, 1165 | // but for now the following is the only type we ever 1166 | // use (but we can add more using statements later on 1167 | // an as-needed basis - trying to minimize our use of 1168 | // such using statements here regardless but 1169 | // "tstring_view" in particular will likely be 1170 | // frequently used by many so we declare it). 1171 | // 1172 | // Lastly, we could convert all functions, etc. that 1173 | // deal with tchars into templates instead, with a 1174 | // template arg specifying the character type (rather 1175 | // than rely on "tstring_view"), and it's arguably the 1176 | // "correct" way to do things (many would say), but in 1177 | // my experience relying on "tstring_view" instead has 1178 | // some benefits of its own I won't get into here (but 1179 | // the situation can be revisited if ever required). 1180 | // For now it normally works well. 1181 | /////////////////////////////////////////////////////// 1182 | using tstring_view = std::basic_string_view; 1183 | #endif // #if CPP17_OR_LATER 1184 | 1185 | /////////////////////////////////////////////////////// 1186 | // GetCompilerName(). WYSIWYG. Note that these names 1187 | // are hardcoded to provide the basic compile-time 1188 | // name of each compiler we support and aren't 1189 | // intended to replace the official name of each 1190 | // compiler by its vendor should you require this (no 1191 | // such function to retrieve that is currently 1192 | // available since none of them provide any predefined 1193 | // macros with the name at this writing) 1194 | /////////////////////////////////////////////////////// 1195 | inline constexpr const tchar * GetCompilerName() noexcept 1196 | { 1197 | #if defined(GCC_COMPILER) 1198 | return _T("GCC or compatible"); 1199 | #elif defined(MICROSOFT_COMPILER) 1200 | return _T("Microsoft Visual C++"); 1201 | #elif defined(CLANG_COMPILER) 1202 | return _T("Clang"); 1203 | #elif defined(INTEL_COMPILER) 1204 | return _T("Intel oneAPI DPC++/C++"); 1205 | #else 1206 | ///////////////////////////////////////////////////// 1207 | // Note: Shouldn't be possible to come through here 1208 | // at this writing since the same #error message 1209 | // would have already been displayed earlier (when 1210 | // the compiler constants just above were #defined) 1211 | ///////////////////////////////////////////////////// 1212 | #error "Unsupported compiler" 1213 | #endif 1214 | } 1215 | } // namespace StdExt 1216 | #else 1217 | #undef DECLARE_PUBLIC_MACROS_ONLY 1218 | #endif // #if !defined(DECLARE_PUBLIC_MACROS_ONLY) 1219 | #endif // #ifndef COMPILER_VERSIONS (#include guard) 1220 | -------------------------------------------------------------------------------- /Demo.cpp: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////// 2 | // LICENSE NOTICE 3 | // -------------- 4 | // Copyright (c) Hexadigm Systems 5 | // 6 | // Permission to use this software is granted under the following license: 7 | // https://www.hexadigm.com/GenericLib/License.html 8 | // 9 | // This copyright notice must be included in this and all copies of the 10 | // software as described in the above license. 11 | // 12 | // DESCRIPTION 13 | // ----------- 14 | // For complete details on "FunctionTraits" (fully documented), see 15 | // https://github.com/HexadigmSystems/FunctionTraits 16 | // 17 | // This file contains a small demo program highlighting the capabilities of 18 | // the "FunctionTraits" library declared in "FunctionTraits.h" (see above 19 | // link), a collection of templates used to retrieve and/or modify any 20 | // component of a C++ function (return type, argument types, cv-qualifiers, 21 | // noexcept specification, etc. - all components comprising a C++ function 22 | // are available, and write traits to modify these components are also fully 23 | // supported though not demo'd in this program - see link above for 24 | // examples). Just run the program to display all available traits for the 3 25 | // sample functions seen in namespace "TestFuncs" later in this file (traits 26 | // displayed to the stdout). You can update these 3 functions with any 27 | // signature whose traits you want to display and re-run the app (or 28 | // experiment with any other template in the library). Note however that all 29 | // 3 sample functions are simply passed to the (function) template 30 | // "DisplayAllFunctionTraits()" in the library itself, which displays all 31 | // traits for any function type you pass (via its template arg "F" - see 32 | // https://github.com/HexadigmSystems/FunctionTraits/#displayallfunctiontraits 33 | // for details). You can therefore just call "DisplayAllFunctionTraits()" 34 | // directly if you wish, passing any function type you want for its template 35 | // arg "F" (free functions which includes static member functions, pointers 36 | // and references to free functions, references to pointers to free 37 | // functions, non-static member function pointers, functors including 38 | // lambdas, "abominable" functions and "std::function" specializations). 39 | // Note however that inspecting the code for "DisplayAllFunctionTraits()" 40 | // itself in "FunctionTraits.h" shows some of the major features of the 41 | // library at work, so it's a very good learning exercise for your own code 42 | // (demonstrating what can be accomplished). Moreover, 43 | // "DisplayAllFunctionTraits()" is designed as a client of the library 44 | // itself, no different than your own code will be, since it simply relies 45 | // on the other templates in the library to carry out its own work. It 46 | // therefore just calls the other templates similar to what you'll do in 47 | // your own code, so you can see an actual client of the library in action 48 | // (even though this particular client happens to belong to the library 49 | // itself). It's also reasonably short for what it does, and you only need 50 | // to trace into the implementation of "DisplayAllFunctionTraits()" itself, 51 | // not any of the library's other templates that it calls (which would be an 52 | // arduous and unnecessary task). Note that other examples of how to use the 53 | // library are also available at the library's link above. See the "Usage" 54 | // section there for full details. You can copy these (usage) examples into 55 | // function "main()" below if you wish, or experiment with any other 56 | // template in the library, relying on "DisplayAllFunctionTraits()" whenever 57 | // you want to display a function's complete traits. This is particularly 58 | // useful when testing the library's write trait templates so you can see 59 | // the results of changing any component of a C++ function (return type, 60 | // parameter types, cv-qualifiers, noexcept specification, etc.). Also see 61 | // "TypeName_v" in the library for converting any C++ type to its 62 | // user-friendly string equivalent, which is also useful when you just want 63 | // to display a C++ type - see 64 | // https://github.com/HexadigmSystems/FunctionTraits/#typename_v. 65 | // 66 | // Lastly, note that within Compiler Explorer (the hosting site for this 67 | // demo), the program defaults to C++20. If you wish to change this then add 68 | // -DCMAKE_CXX_STANDARD=?? to the CMake command line options in Compiler 69 | // Explorer, setting "??" in the latter option to the target C++ version 70 | // (17, 20, 23 or beyond - versions prior to C++17 are not supported). In 71 | // Compiler Explorer, the CMake command line options are normally located 72 | // near the top-left corner of the source code window on the left, just 73 | // above the project name "FunctionTraitsDemo", so add just the 74 | // -DCMAKE_CXX_STANDARD=??" option there. For further details on the CMake 75 | // command line options for this demo (though most users don't need to get 76 | // involved with these), see the top of the "CMakeLists.txt" file in the 77 | // Compiler Explorer source code window. 78 | ///////////////////////////////////////////////////////////////////////////// 79 | 80 | // #included first so we can check CPP17_OR_LATER just below 81 | #include "CompilerVersions.h" 82 | 83 | // We only support C++17 or later (stop compiling otherwise) 84 | #if CPP17_OR_LATER 85 | 86 | //////////////////////////////////////////////////////////// 87 | // "import std" not currently in effect? (C++23 or later). 88 | // If it is in effect then no need to #include the headers 89 | // below (they're already available) 90 | //////////////////////////////////////////////////////////// 91 | #if !defined(STDEXT_IMPORTED_STD) 92 | // Standard C/C++ headers 93 | #include 94 | #include 95 | #include 96 | #include 97 | #endif 98 | 99 | //////////////////////////////////////////////////////// 100 | // Our headers (only file in this repository you need 101 | // to explicitly #include) 102 | //////////////////////////////////////////////////////// 103 | #include "FunctionTraits.h" 104 | 105 | ///////////////////////////////////////// 106 | // Everything in "FunctionTraits.h" just 107 | // above declared in this namespace 108 | ///////////////////////////////////////// 109 | using namespace StdExt; 110 | 111 | ///////////////////////////////////////////////////////////////////////////// 112 | // "DisplayFunctionTraits()" 113 | ///////////////////////////////////////////////////////////////////////////// 114 | template 115 | void DisplayFunctionTraits(tstring_view caption) 116 | { 117 | // Display the caption for this function to the stdout 118 | tcout << _T("************************************\n"); 119 | tcout << _T("* ") << caption << _T("\n"); 120 | tcout << _T("************************************\n"); 121 | 122 | ///////////////////////////////////////////////// 123 | // Stream the function's traits to "tcout". The 124 | // following function from the "FunctionTraits" 125 | // library itself does all the work. See here 126 | // for details: 127 | // 128 | // https://github.com/HexadigmSystems/FunctionTraits#DisplayAllFunctionTraits 129 | ///////////////////////////////////////////////// 130 | DisplayAllFunctionTraits(tcout); 131 | } 132 | 133 | ////////////////////////////////////////////////////////////////////////////// 134 | // namespace TestFuncs. Contains 3 functions whose traits this demo displays. 135 | // Functions need not be defined in any namespace however (for this demo to 136 | // work). Doing so just to keep things organized (and placed in this 137 | // namespace for easy access - you can quickly change all functions in this 138 | // one location). Note that you can experiment with this program by simply 139 | // changing the signatures of these functions to whatever you wish and 140 | // re-running the app (but don't change the function names themselves unless 141 | // you also change them where they're actually used later on, though there's 142 | // normally no need to - the function signature is all that matters, not the 143 | // name). 144 | ////////////////////////////////////////////////////////////////////////////// 145 | namespace TestFuncs 146 | { 147 | ////////////////////////////////////////////////////// 148 | // Free function. Note: Need not be defined for this 149 | // demo to work and it's not (only the function's 150 | // type matters) 151 | ////////////////////////////////////////////////////// 152 | int FreeFunc(const std::wstring &, const char *, short, int, float, long, double, ...) noexcept; 153 | 154 | class SomeClass 155 | { 156 | public: 157 | ////////////////////////////////////////////////////// 158 | // Non-static member function (static functions also 159 | // supported but processed like a free function so 160 | // the one above is used). Note: Need not be defined 161 | // for this demo to work and it's not (only the 162 | // function's type matters) 163 | ////////////////////////////////////////////////////// 164 | int STDEXT_CC_STDCALL DoSomething(double &, const std::string_view &) const volatile && noexcept; 165 | 166 | /////////////////////////////////////////////////// 167 | // Adding this to make the class a functor (which 168 | // are also supported) 169 | /////////////////////////////////////////////////// 170 | std::basic_string operator()(std::size_t) const; 171 | }; 172 | } 173 | 174 | ////////////////////////////////////////////////////////////////////////////// 175 | // DisplayFreeFunctionTraits() 176 | ////////////////////////////////////////////////////////////////////////////// 177 | void DisplayFreeFunctionTraits() 178 | { 179 | //////////////////////////////////////////////////////////// 180 | // Free function type or static member function type whose 181 | // traits we wish to display (non-static member functions 182 | // are handled by "DisplayMemberFunctionTraits()" below). 183 | // Note that pointers or reference to the function are also 184 | // supported, as well as references to function pointers. 185 | // The following is the actual (raw) function type however, 186 | // neither pointer nor reference, though most developers 187 | // don't work with the actual function type in code too 188 | // often (since function names decay to a pointer to the 189 | // function in most scenarios so pointers to functions are 190 | // much more common in most code - "FunctionTraits" will 191 | // handle any legal type that identifies the function 192 | // however, whether the function's actual (raw) C++ type, 193 | // or pointers or references to functions, or references 194 | // to pointers to functions) 195 | //////////////////////////////////////////////////////// 196 | using F = decltype(TestFuncs::FreeFunc); 197 | // using F = decltype(&TestFuncs::FreeFunc); // Will also work (pointer to function and usually 198 | // more common in practice) 199 | // using F = decltype(TestFuncs::FreeFunc) &; // Will also work (lvalue reference to function, 200 | // though rvalue works too - both are rare in 201 | // practice though, noting that this particular 202 | // "decltype" syntax is even more rare - usually 203 | // you'll work with a reference to the function 204 | // in code instead, such as "int (&)()" for 205 | // example (reference to a function taking no 206 | // args and returning an "int"), preferably 207 | // using an alias to simplify the latter 208 | // (cryptic) syntax 209 | 210 | DisplayFunctionTraits(_T("Free function traits demo")); 211 | } 212 | 213 | ////////////////////////////////////////////////////////////////////////////// 214 | // DisplayMemberFunctionTraits() 215 | ////////////////////////////////////////////////////////////////////////////// 216 | void DisplayMemberFunctionTraits() 217 | { 218 | /////////////////////////////////////////////////////////// 219 | // Non-static member function type whose traits we wish to 220 | // display. Always a member function pointer (references 221 | // to member function pointers also supported), unlike the 222 | // free function version in "DisplayFreeFunctionTraits()" 223 | // above, which supports the function's actual (raw) C++ 224 | // type as well. We don't support the raw C++ type for 225 | // non-static member functions however, just member 226 | // function pointers only (or references to such pointers). 227 | // To this end, when passing a raw function type, it's 228 | // always assumed to be a free function (which for our 229 | // purposes also includes static member functions). 230 | /////////////////////////////////////////////////////////// 231 | using F = decltype(&TestFuncs::SomeClass::DoSomething); 232 | 233 | DisplayFunctionTraits(_T("Member function traits demo")); 234 | } 235 | 236 | ////////////////////////////////////////////////////////////////////////////// 237 | // DisplayFunctorTraits() 238 | ////////////////////////////////////////////////////////////////////////////// 239 | void DisplayFunctorTraits() 240 | { 241 | /////////////////////////////////////////////////////// 242 | // Functor type whose traits we wish to display. For 243 | // functors, "F" must always be the class type itself, 244 | // where the class has a single member function called 245 | // "operator()" (by definition of a functor). Note 246 | // that overloads of "operator()" are ambiguous 247 | // however so aren't supported (a compiler error will 248 | // result). If the function has overloads of this 249 | // member (rare), then you should take the address of 250 | // the overload you're interested in and use that 251 | // instead (i.e., just a normal non-static member 252 | // function pointer to that overload). In fact, even 253 | // for functors with just one "operator()" member 254 | // (like the following example), "operator()" is still 255 | // just a member function no different than any other, 256 | // so internally "FunctionTraits" defers to the member 257 | // function specialization by simply targeting 258 | // "&F::operator()" 259 | /////////////////////////////////////////////////////// 260 | using F = TestFuncs::SomeClass; 261 | 262 | DisplayFunctionTraits(_T("Functor traits demo")); 263 | } 264 | 265 | ///////////////////////////////////////////////////////////////////////////// 266 | // main() 267 | ///////////////////////////////////////////////////////////////////////////// 268 | int main() 269 | { 270 | tcout << _T("FunctionTraits demo (detected compiler: ") << GetCompilerName() << _T("). See top of \"Demo.cpp\" for details.\n"); 271 | tcout << _T("For complete details on \"FunctionTraits\" see https://github.com/HexadigmSystems/FunctionTraits\n\n"); 272 | 273 | DisplayFreeFunctionTraits(); 274 | tcout << _T("\n\n"); 275 | DisplayMemberFunctionTraits(); 276 | tcout << _T("\n\n"); 277 | DisplayFunctorTraits(); 278 | 279 | return 0; 280 | } 281 | #else 282 | #error "This program is only supported in C++17 or later (an earlier version was detected). Please set the appropriate compiler option to target C++17 or later and try again (minimum of "-std=c++17" for GCC, Clang and Intel, or "/std:c++17" for Microsoft)" 283 | #endif // #if CPP17_OR_LATER -------------------------------------------------------------------------------- /FunctionTraits.cppm: -------------------------------------------------------------------------------- 1 | ///////////////////////////////////////////////////////////////////////////// 2 | // LICENSE NOTICE 3 | // -------------- 4 | // Copyright (c) Hexadigm Systems 5 | // 6 | // Permission to use this software is granted under the following license: 7 | // https://www.hexadigm.com/GenericLib/License.html 8 | // 9 | // This copyright notice must be included in this and all copies of the 10 | // software as described in the above license. 11 | // 12 | // DESCRIPTION 13 | // ----------- 14 | // Module version of "FunctionTraits.h". Simply defers to "FunctionTraits.h" 15 | // to export all public declarations in that header, in particular struct 16 | // "FunctionTraits" and all declarations associated with it. Other public 17 | // declarations unrelated to "FunctionTraits" itself are also available in 18 | // "FunctionTraits.h" however and are therefore also exported by this module 19 | // (mostly support declarations that "FunctionTraits" itself relies on but 20 | // sometimes useful by end-users). The focus of the module for now however 21 | // is "FunctionTraits" even though some of the exported declarations below 22 | // aren't directly related to it (so not documented at the library's GitHub 23 | // web site but exported anyway in case anyone wants to use them). For 24 | // complete details on module support in "FunctionTraits", see 25 | // https://github.com/HexadigmSystems/FunctionTraits#moduleusage 26 | ///////////////////////////////////////////////////////////////////////////// 27 | 28 | module; 29 | 30 | //////////////////////////////////////////////////////////// 31 | // Let "FunctionTraits.h" just below know we're building 32 | // the "FunctionTraits" module. Following is only #defined 33 | // when we are ... 34 | //////////////////////////////////////////////////////////// 35 | #define STDEXT_BUILDING_MODULE_FUNCTION_TRAITS 36 | #include "FunctionTraits.h" 37 | #undef STDEXT_BUILDING_MODULE_FUNCTION_TRAITS 38 | 39 | export module FunctionTraits; 40 | 41 | //////////////////////////////////////////////////////////////////////// 42 | // Export "CompilerVersions" to make the module version of 43 | // "FunctionTraits" consistent with the non-module version. In the 44 | // non-module version, a call to #include "FunctionTraits.h" 45 | // automatically #includes "CompilerVersions.h" as well, so in the 46 | // module version, a call to "import FunctionTraits" automatically 47 | // imports "CompilerVersions" via the following call (so for most 48 | // intents and purposes it's consistent with the behavior of the 49 | // non-module version). To pick up the macros in "CompilerVersions.h" 50 | // as well however (since macros aren't exported by C++ modules), just 51 | // #include either "FunctionTraits.h" or "CompilerVersions.h" directly 52 | // instead (and if so you don't even have to directly code your own 53 | // "import" statement, as each header does this for you when the 54 | // constant STDEXT_USE_MODULES is #defined as it normally should be 55 | // (when using the module version of "FunctionTraits"). See the 56 | // following for complete details: 57 | // 58 | // https://github.com/HexadigmSystems/FunctionTraits#moduleusage 59 | //////////////////////////////////////////////////////////////////////// 60 | export import CompilerVersions; 61 | 62 | ////////////////////////////////////////////////////////////////////// 63 | // Interface for this module. We simply rely on "using" declarations 64 | // in the code below to export all public declarations from 65 | // "FunctionTraits.h" above (internal declarations from 66 | // "FunctionTraits.h" not intended for public use are all declared in 67 | // namespace "StdExt::Private" and are not exported below - all 68 | // others are). Note that only declarations associated with 69 | // "FunctionTraits" are documented at 70 | // https://github.com/HexadigmSystems/FunctionTraits however. All 71 | // other exported declarations below are still available for public 72 | // use however though they're not the focus of the above link. The 73 | // declarations specifically related to "FunctionTraits" are (though 74 | // the "FunctionTraits" declarations rely on the other declarations 75 | // to carry out their work). The upshot is that while all other 76 | // declarations below are undocumented at the above link, users who 77 | // wish to use them for their own purposes may safely do so (just 78 | // consult them in "FunctionTraits.h" itself for details, not the 79 | // above link). See the following for details on using this module: 80 | // 81 | // https://github.com/HexadigmSystems/FunctionTraits#moduleusage 82 | // 83 | // IMPORTANT: 84 | // --------- 85 | // Note that GCC is currently buggy at this writing (modules still 86 | // under development), and fails to compile the code below (so until 87 | // corrected, this module can't be used in GCC). See the following 88 | // (effectively identical) GCC bug reports (now showing the bug has 89 | // in fact been flagged as corrected but an updated version of GCC 90 | // with the fix hasn't been released yet at this writing - it may be 91 | // before too long though): 92 | // 93 | // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=109679 94 | // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=113129 95 | ////////////////////////////////////////////////////////////////////// 96 | export namespace StdExt 97 | { 98 | using StdExt::AlwaysFalse_v; 99 | using StdExt::AlwaysTrue_v; 100 | using StdExt::TypeName_v; 101 | 102 | #if defined(USE_CONCEPTS) 103 | using StdExt::IsFunction_c; 104 | #endif 105 | 106 | #if defined(USE_CONCEPTS) 107 | using StdExt::IsClass_c; 108 | #endif 109 | 110 | using StdExt::IsClassOrVoid_v; 111 | #if defined(USE_CONCEPTS) 112 | using StdExt::IsClassOrVoid_c; 113 | #endif 114 | 115 | using StdExt::IsConstOrVolatile_v; 116 | using StdExt::IsSpecialization; 117 | using StdExt::IsSpecialization_v; 118 | using StdExt::TemplateArgsTuple; 119 | using StdExt::TemplateArgsTuple_t; 120 | using StdExt::IthTemplateArg_t; 121 | using StdExt::FirstTemplateArg_t; 122 | using StdExt::TemplateArg_t; 123 | using StdExt::IsTemplateBaseOf_v; 124 | using StdExt::RemoveCvRef_t; 125 | using StdExt::RemovePtrRef_t; 126 | using StdExt::size_t_npos; 127 | 128 | using StdExt::LessThanOrEqual_v; 129 | #if defined(USE_CONCEPTS) 130 | using StdExt::LessThanOrEqual_c; 131 | #else 132 | using StdExt::StaticAssertLessThanOrEqual; 133 | #endif 134 | 135 | using StdExt::CountRemaining_v; 136 | using StdExt::CountExceedsRemaining_v; 137 | using StdExt::MinOfCountOrRemaining_v; 138 | using StdExt::MakeIndexSequenceStartAt; 139 | using StdExt::IsForEachFunctor; 140 | using StdExt::IsForEachFunctor_v; 141 | #if defined(USE_CONCEPTS) 142 | using StdExt::ForEachFunctor_c; 143 | #endif 144 | using StdExt::ForEach; 145 | 146 | using StdExt::IsTupleSpecialization_v; 147 | #if defined(USE_CONCEPTS) 148 | using StdExt::Tuple_c; 149 | #else 150 | using StdExt::StaticAssertIsTuple; 151 | #endif 152 | 153 | using StdExt::IndexLessThanTupleSize_v; 154 | #if defined(USE_CONCEPTS) 155 | using StdExt::IndexLessThanTupleSize_c; 156 | #else 157 | using StdExt::StaticAssertIndexLessThanTupleSize; 158 | #endif 159 | 160 | using StdExt::IndexLessThanOrEqualToTupleSize_v; 161 | #if defined(USE_CONCEPTS) 162 | using StdExt::IndexLessThanOrEqualToTupleSize_c; 163 | #else 164 | using StdExt::StaticAssertIndexLessThanOrEqualToTupleSize; 165 | #endif 166 | 167 | using StdExt::MinOfCountOrTupleTypesRemaining_v; 168 | using StdExt::SubTuple_t; 169 | using StdExt::TupleModifyTuple_t; 170 | using StdExt::TupleModify_t; 171 | using StdExt::TupleInsertTuple_t; 172 | using StdExt::TupleInsert_t; 173 | using StdExt::TupleAppendTuple_t; 174 | using StdExt::TupleAppend_t; 175 | using StdExt::TupleDelete_t; 176 | using StdExt::IsForEachTupleTypeFunctor; 177 | using StdExt::IsForEachTupleTypeFunctor_v; 178 | #if defined(USE_CONCEPTS) 179 | using StdExt::ForEachTupleTypeFunctor_c; 180 | #endif 181 | using StdExt::ForEachTupleType; 182 | 183 | using StdExt::IsFreeFunction_v; 184 | #if defined(USE_CONCEPTS) 185 | using StdExt::FreeFunction_c; 186 | #endif 187 | 188 | using StdExt::IsFreeFunctionPointer_v; 189 | using StdExt::IsAbominableFunction_v; 190 | #if defined(USE_CONCEPTS) 191 | using StdExt::AbominableFunction_c; 192 | #endif 193 | 194 | #if defined(USE_CONCEPTS) 195 | using StdExt::MemberFunctionPointer_c; 196 | #endif 197 | 198 | using StdExt::IsNonOverloadedFunctor; 199 | using StdExt::IsNonOverloadedFunctor_v; 200 | #if defined(USE_CONCEPTS) 201 | using StdExt::NonOverloadedFunctor_c; 202 | #endif 203 | 204 | using StdExt::IsNonOverloadedStaticFunctor; 205 | using StdExt::IsNonOverloadedStaticFunctor_v; 206 | #if defined(USE_CONCEPTS) 207 | using StdExt::NonOverloadedStaticFunctor_c; 208 | #endif 209 | 210 | using StdExt::IsStdFunctionSpecialization_v; 211 | #if defined(USE_CONCEPTS) 212 | using StdExt::StdFunction_c; 213 | #endif 214 | 215 | using StdExt::StdFunctionTemplateArg_t; 216 | 217 | using StdExt::IsTraitsFreeFunction_v; 218 | #if defined(USE_CONCEPTS) 219 | using StdExt::TraitsFreeFunction_c; 220 | #endif 221 | 222 | using StdExt::IsTraitsMemberFunction_v; 223 | #if defined(USE_CONCEPTS) 224 | using StdExt::TraitsMemberFunction_c; 225 | #endif 226 | 227 | using StdExt::IsTraitsStdFunction_v; 228 | #if defined(USE_CONCEPTS) 229 | using StdExt::TraitsStdFunction_c; 230 | #endif 231 | 232 | using StdExt::IsTraitsFunctor_v; 233 | #if defined(USE_CONCEPTS) 234 | using StdExt::TraitsFunctor_c; 235 | #endif 236 | 237 | using StdExt::IsTraitsStaticFunctor_v; 238 | #if defined(USE_CONCEPTS) 239 | using StdExt::TraitsStaticFunctor_c; 240 | #endif 241 | 242 | using StdExt::IsTraitsNonStaticOrStaticFunctor_v; 243 | #if defined(USE_CONCEPTS) 244 | using StdExt::TraitsNonStaticOrStaticFunctor_c; 245 | #endif 246 | 247 | using StdExt::IsTraitsFunction_v; 248 | #if defined(USE_CONCEPTS) 249 | using StdExt::TraitsFunction_c; 250 | #endif 251 | 252 | using StdExt::CallingConvention; 253 | using StdExt::CallingConventionCount_v; 254 | using StdExt::CallingConventionToString; 255 | using StdExt::CallingConventionReplacedWithCdecl; 256 | using StdExt::FunctionReference; 257 | #if defined(STDEXT_SUPPORT_DEPRECATED) 258 | using StdExt::RefQualifier; 259 | #endif 260 | using StdExt::FunctionReferenceToString; 261 | using StdExt::FunctionClassification; 262 | using StdExt::FunctionOrigin; 263 | using StdExt::AndVariadic; 264 | 265 | using StdExt::IsValidReturnType; 266 | using StdExt::IsValidReturnType_v; 267 | #if defined(USE_CONCEPTS) 268 | using StdExt::ValidReturnType_c; 269 | #endif 270 | 271 | using StdExt::IsValidFunctionArgTypesTuple; 272 | using StdExt::IsValidFunctionArgTypesTuple_v; 273 | #if defined(USE_CONCEPTS) 274 | using StdExt::ValidFunctionArgTypesTuple_c; 275 | #endif 276 | 277 | using StdExt::IsValidFunctionArgTypes; 278 | using StdExt::IsValidFunctionArgTypes_v; 279 | #if defined(USE_CONCEPTS) 280 | using StdExt::ValidFunctionArgType_c; 281 | #endif 282 | 283 | using StdExt::IsForEachArgFunctor_v; 284 | #if defined(USE_CONCEPTS) 285 | using StdExt::ForEachArgFunctor_c; 286 | #endif 287 | 288 | using StdExt::FunctionTraits; 289 | using StdExt::DefaultCallingConvention_v; 290 | using StdExt::DefaultCallingConventionName_v; 291 | using StdExt::IsFunctionTraitsSpecialization_v; 292 | #if defined(USE_CONCEPTS) 293 | using StdExt::FunctionTraits_c; 294 | #endif 295 | 296 | ///////////////////////////////////////////////////// 297 | // "FunctionTraits" helper templates (read traits) 298 | // taking a "FunctionTraits" template arg. Rarely 299 | // used directly as most will rely on the 300 | // "FunctionTraits" helper templates taking a 301 | // function template arg "F" instead (which simply 302 | // defer to the following) 303 | ///////////////////////////////////////////////////// 304 | using StdExt::FunctionTraitsArgCount_v; 305 | #if defined(USE_CONCEPTS) 306 | using StdExt::FunctionTraitsIndexLessThanArgCount_v; 307 | using StdExt::FunctionTraitsIndexLessThanArgCount_c; 308 | #endif 309 | #if defined(USE_CONCEPTS) 310 | using StdExt::FunctionTraitsIndexLessThanOrEqualToArgCount_v; 311 | using StdExt::FunctionTraitsIndexLessThanOrEqualToArgCount_c; 312 | #endif 313 | #if defined(USE_CONCEPTS) 314 | using StdExt::FunctionTraitsIndexLessThanArgCountOrReturnVoid_v; 315 | #endif 316 | using StdExt::FunctionTraitsArgType_t; 317 | #if defined(USE_CONCEPTS) 318 | using StdExt::FunctionTraitsIndexLessThanArgCountOrReturnEmptyString_v; 319 | #endif 320 | using StdExt::FunctionTraitsArgTypeName_v; 321 | using StdExt::FunctionTraitsArgTypes_t; 322 | using StdExt::FunctionTraitsCallingConvention_v; 323 | using StdExt::FunctionTraitsCallingConventionName_v; 324 | using StdExt::FunctionTraitsForEachArg; 325 | using StdExt::FunctionTraitsFunctionClassification_v; 326 | #if defined(STDEXT_SUPPORT_DEPRECATED) 327 | using StdExt::FunctionTraitsIsMemberFunction_v; 328 | #endif 329 | using StdExt::FunctionTraitsFunctionOrigin_v; 330 | #if defined(STDEXT_SUPPORT_DEPRECATED) 331 | using StdExt::FunctionTraitsIsFunctor_v; 332 | #endif 333 | using StdExt::FunctionTraitsFunctionRawType_t; 334 | using StdExt::FunctionTraitsFunctionRawTypeName_v; 335 | using StdExt::FunctionTraitsFunctionType_t; 336 | using StdExt::FunctionTraitsFunctionTypeName_v; 337 | #if defined(USE_CONCEPTS) 338 | using StdExt::FunctionTraitsIndexValidInBothOrReturnFalseIfOneHasArgOtherDoesnt_v; 339 | #endif 340 | using StdExt::FunctionTraitsIsArgListEmpty_v; 341 | #if defined(STDEXT_SUPPORT_DEPRECATED) 342 | using StdExt::FunctionTraitsIsEmptyArgList_v; 343 | #endif 344 | using StdExt::FunctionTraitsIsArgTypeMatch_v; 345 | using StdExt::FunctionTraitsIsArgTypesMatch_v; 346 | #if defined(USE_CONCEPTS) 347 | using StdExt::FunctionTraitsIndexLessThanArgCountOrCompareWithVoid_v; 348 | #endif 349 | using StdExt::FunctionTraitsIsArgTypeSame_v; 350 | using StdExt::FunctionTraitsIsArgTypesSame_v; 351 | using StdExt::FunctionTraitsIsArgTypesSameTuple_v; 352 | using StdExt::FunctionTraitsIsFunctionConst_v; 353 | #if defined(STDEXT_SUPPORT_DEPRECATED) 354 | using StdExt::FunctionTraitsIsMemberFunctionConst_v; 355 | #endif 356 | using StdExt::FunctionTraitsIsFunctionVolatile_v; 357 | #if defined(STDEXT_SUPPORT_DEPRECATED) 358 | using StdExt::FunctionTraitsIsMemberFunctionVolatile_v; 359 | #endif 360 | using StdExt::FunctionTraitsFunctionReference_v; 361 | #if defined(STDEXT_SUPPORT_DEPRECATED) 362 | using StdExt::FunctionTraitsMemberFunctionRefQualifier_v; 363 | #endif 364 | using StdExt::FunctionTraitsFunctionReferenceName_v; 365 | #if defined(STDEXT_SUPPORT_DEPRECATED) 366 | using StdExt::FunctionTraitsMemberFunctionRefQualifierName_v; 367 | #endif 368 | using StdExt::FunctionTraitsIsNoexcept_v; 369 | using StdExt::FunctionTraitsIsReturnTypeMatch_v; 370 | using StdExt::FunctionTraitsIsReturnTypeSame_v; 371 | using StdExt::FunctionTraitsIsReturnTypeVoid_v; 372 | #if defined(STDEXT_SUPPORT_DEPRECATED) 373 | using StdExt::FunctionTraitsIsVoidReturnType_v; 374 | #endif 375 | using StdExt::FunctionTraitsIsVariadic_v; 376 | using StdExt::FunctionTraitsMemberFunctionClass_t; 377 | using StdExt::FunctionTraitsMemberFunctionClassName_v; 378 | using StdExt::FunctionTraitsReturnType_t; 379 | using StdExt::FunctionTraitsReturnTypeName_v; 380 | 381 | ///////////////////////////////////////////////////// 382 | // "FunctionTraits" helper templates (write traits) 383 | // taking a "FunctionTraits" template arg. Rarely 384 | // used as most will rely on the "FunctionTraits" 385 | // helper templates taking a function template arg 386 | // "F" instead (which simply defer to the following) 387 | ///////////////////////////////////////////////////// 388 | #if defined(FUNCTION_WRITE_TRAITS_SUPPORTED) 389 | using StdExt::FunctionTraitsAddNoexcept_t; 390 | using StdExt::FunctionTraitsAddVariadicArgs_t; 391 | using StdExt::FunctionTraitsFunctionAddConst_t; 392 | #if defined(STDEXT_SUPPORT_DEPRECATED) 393 | using StdExt::FunctionTraitsMemberFunctionAddConst_t; 394 | #endif 395 | using StdExt::FunctionTraitsFunctionRemoveConst_t; 396 | #if defined(STDEXT_SUPPORT_DEPRECATED) 397 | using StdExt::FunctionTraitsMemberFunctionRemoveConst_t; 398 | #endif 399 | using StdExt::FunctionTraitsFunctionAddVolatile_t; 400 | #if defined(STDEXT_SUPPORT_DEPRECATED) 401 | using StdExt::FunctionTraitsMemberFunctionAddVolatile_t; 402 | #endif 403 | using StdExt::FunctionTraitsFunctionRemoveVolatile_t; 404 | #if defined(STDEXT_SUPPORT_DEPRECATED) 405 | using StdExt::FunctionTraitsMemberFunctionRemoveVolatile_t; 406 | #endif 407 | using StdExt::FunctionTraitsFunctionAddCV_t; 408 | #if defined(STDEXT_SUPPORT_DEPRECATED) 409 | using StdExt::FunctionTraitsMemberFunctionAddCV_t; 410 | #endif 411 | using StdExt::FunctionTraitsFunctionRemoveCV_t; 412 | #if defined(STDEXT_SUPPORT_DEPRECATED) 413 | using StdExt::FunctionTraitsMemberFunctionRemoveCV_t; 414 | #endif 415 | using StdExt::FunctionTraitsFunctionAddLValueReference_t; 416 | #if defined(STDEXT_SUPPORT_DEPRECATED) 417 | using StdExt::FunctionTraitsMemberFunctionAddLValueReference_t; 418 | #endif 419 | using StdExt::FunctionTraitsFunctionAddRValueReference_t; 420 | #if defined(STDEXT_SUPPORT_DEPRECATED) 421 | using StdExt::FunctionTraitsMemberFunctionAddRValueReference_t; 422 | #endif 423 | using StdExt::FunctionTraitsFunctionRemoveReference_t; 424 | #if defined(STDEXT_SUPPORT_DEPRECATED) 425 | using StdExt::FunctionTraitsMemberFunctionRemoveReference_t; 426 | #endif 427 | using StdExt::FunctionTraitsReplaceMemberFunctionClass_t; 428 | #if defined(STDEXT_SUPPORT_DEPRECATED) 429 | using StdExt::FunctionTraitsMemberFunctionReplaceClass_t; 430 | #endif 431 | using StdExt::FunctionTraitsRemoveNoexcept_t; 432 | using StdExt::FunctionTraitsRemoveVariadicArgs_t; 433 | using StdExt::FunctionTraitsReplaceArgs_t; 434 | using StdExt::FunctionTraitsReplaceArgsTuple_t; 435 | using StdExt::FunctionTraitsReplaceCallingConvention_t; 436 | using StdExt::FunctionTraitsArgsModify_t; 437 | using StdExt::FunctionTraitsArgsModifyTuple_t; 438 | using StdExt::FunctionTraitsArgsInsert_t; 439 | using StdExt::FunctionTraitsArgsInsertTuple_t; 440 | using StdExt::FunctionTraitsArgsMakeVoid_t; 441 | using StdExt::FunctionTraitsArgsAppend_t; 442 | using StdExt::FunctionTraitsArgsAppendTuple_t; 443 | using StdExt::FunctionTraitsArgsDelete_t; 444 | using StdExt::FunctionTraitsReplaceArg_t; 445 | #if defined(STDEXT_SUPPORT_DEPRECATED) 446 | using StdExt::FunctionTraitsReplaceNthArg_t; 447 | #endif 448 | using StdExt::FunctionTraitsReplaceReturnType_t; 449 | using StdExt::FunctionTraitsMakeMemberFunctionPtr_t; 450 | #endif 451 | 452 | //////////////////////////////////////////////////// 453 | // "FunctionTraits" helper templates (read traits) 454 | // taking a function template arg "F". Along with 455 | // the write traits just below, these are the 456 | // templates most will normally rely on as fully 457 | // documented here: 458 | // 459 | // https://github.com/HexadigmSystems/FunctionTraits/#writetraits 460 | //////////////////////////////////////////////////// 461 | using StdExt::ArgCount_v; 462 | #if defined(USE_CONCEPTS) 463 | using StdExt::IndexLessThanArgCount_v; 464 | using StdExt::IndexLessThanArgCount_c; 465 | #endif 466 | #if defined(USE_CONCEPTS) 467 | using StdExt::IndexLessThanOrEqualToArgCount_v; 468 | using StdExt::IndexLessThanOrEqualToArgCount_c; 469 | #endif 470 | #if defined(USE_CONCEPTS) 471 | using StdExt::IndexLessThanArgCountOrReturnVoid_v; 472 | #endif 473 | using StdExt::ArgType_t; 474 | #if defined(USE_CONCEPTS) 475 | using StdExt::IndexLessThanArgCountOrReturnEmptyString_v; 476 | #endif 477 | using StdExt::ArgTypeName_v; 478 | using StdExt::ArgTypes_t; 479 | using StdExt::CallingConvention_v; 480 | using StdExt::CallingConventionName_v; 481 | using StdExt::ForEachArg; 482 | using StdExt::FunctionClassification_v; 483 | #if defined(STDEXT_SUPPORT_DEPRECATED) 484 | using StdExt::IsMemberFunction_v; 485 | #endif 486 | using StdExt::FunctionOrigin_v; 487 | #if defined(STDEXT_SUPPORT_DEPRECATED) 488 | using StdExt::IsFunctor_v; 489 | #endif 490 | using StdExt::FunctionRawType_t; 491 | using StdExt::FunctionRawTypeName_v; 492 | using StdExt::FunctionType_t; 493 | using StdExt::FunctionTypeName_v; 494 | #if defined(USE_CONCEPTS) 495 | using StdExt::IndexValidInBothOrReturnFalseIfOneHasArgOtherDoesnt_v; 496 | #endif 497 | using StdExt::IsArgListEmpty_v; 498 | #if defined(STDEXT_SUPPORT_DEPRECATED) 499 | using StdExt::IsEmptyArgList_v; 500 | #endif 501 | using StdExt::IsArgTypeMatch_v; 502 | using StdExt::IsArgTypesMatch_v; 503 | #if defined(USE_CONCEPTS) 504 | using StdExt::IndexLessThanArgCountOrCompareWithVoid_v; 505 | #endif 506 | using StdExt::IsArgTypeSame_v; 507 | using StdExt::IsArgTypesSame_v; 508 | using StdExt::IsArgTypesSameTuple_v; 509 | using StdExt::IsFunctionConst_v; 510 | #if defined(STDEXT_SUPPORT_DEPRECATED) 511 | using StdExt::IsMemberFunctionConst_v; 512 | #endif 513 | using StdExt::IsFunctionVolatile_v; 514 | #if defined(STDEXT_SUPPORT_DEPRECATED) 515 | using StdExt::IsMemberFunctionVolatile_v; 516 | #endif 517 | using StdExt::FunctionReference_v; 518 | #if defined(STDEXT_SUPPORT_DEPRECATED) 519 | using StdExt::MemberFunctionRefQualifier_v; 520 | #endif 521 | using StdExt::FunctionReferenceName_v; 522 | #if defined(STDEXT_SUPPORT_DEPRECATED) 523 | using StdExt::MemberFunctionRefQualifierName_v; 524 | #endif 525 | using StdExt::IsNoexcept_v; 526 | using StdExt::IsReturnTypeMatch_v; 527 | using StdExt::IsReturnTypeSame_v; 528 | using StdExt::IsReturnTypeVoid_v; 529 | #if defined(STDEXT_SUPPORT_DEPRECATED) 530 | using StdExt::IsVoidReturnType_v; 531 | #endif 532 | using StdExt::IsVariadic_v; 533 | using StdExt::MemberFunctionClass_t; 534 | using StdExt::MemberFunctionClassName_v; 535 | using StdExt::ReturnType_t; 536 | using StdExt::ReturnTypeName_v; 537 | 538 | //////////////////////////////////////////////////// 539 | // "FunctionTraits" helper templates (write traits) 540 | // taking a function template arg "F". Along with 541 | // the read traits just above, these are the 542 | // templates most will normally rely on as fully 543 | // documented here: 544 | // 545 | // https://github.com/HexadigmSystems/FunctionTraits/#writetraits 546 | //////////////////////////////////////////////////// 547 | #if defined(FUNCTION_WRITE_TRAITS_SUPPORTED) 548 | using StdExt::AddNoexcept_t; 549 | using StdExt::AddVariadicArgs_t; 550 | using StdExt::FunctionAddConst_t; 551 | #if defined(STDEXT_SUPPORT_DEPRECATED) 552 | using StdExt::MemberFunctionAddConst_t; 553 | #endif 554 | using StdExt::FunctionRemoveConst_t; 555 | #if defined(STDEXT_SUPPORT_DEPRECATED) 556 | using StdExt::MemberFunctionRemoveConst_t; 557 | #endif 558 | using StdExt::FunctionAddVolatile_t; 559 | #if defined(STDEXT_SUPPORT_DEPRECATED) 560 | using StdExt::MemberFunctionAddVolatile_t; 561 | #endif 562 | using StdExt::FunctionRemoveVolatile_t; 563 | #if defined(STDEXT_SUPPORT_DEPRECATED) 564 | using StdExt::MemberFunctionRemoveVolatile_t; 565 | #endif 566 | using StdExt::FunctionAddCV_t; 567 | #if defined(STDEXT_SUPPORT_DEPRECATED) 568 | using StdExt::MemberFunctionAddCV_t; 569 | #endif 570 | using StdExt::FunctionRemoveCV_t; 571 | #if defined(STDEXT_SUPPORT_DEPRECATED) 572 | using StdExt::MemberFunctionRemoveCV_t; 573 | #endif 574 | using StdExt::FunctionAddLValueReference_t; 575 | #if defined(STDEXT_SUPPORT_DEPRECATED) 576 | using StdExt::MemberFunctionAddLValueReference_t; 577 | #endif 578 | using StdExt::FunctionAddRValueReference_t; 579 | #if defined(STDEXT_SUPPORT_DEPRECATED) 580 | using StdExt::MemberFunctionAddRValueReference_t; 581 | #endif 582 | using StdExt::FunctionRemoveReference_t; 583 | #if defined(STDEXT_SUPPORT_DEPRECATED) 584 | using StdExt::MemberFunctionRemoveReference_t; 585 | #endif 586 | using StdExt::ReplaceMemberFunctionClass_t; 587 | #if defined(STDEXT_SUPPORT_DEPRECATED) 588 | using StdExt::MemberFunctionReplaceClass_t; 589 | #endif 590 | using StdExt::RemoveNoexcept_t; 591 | using StdExt::RemoveVariadicArgs_t; 592 | using StdExt::ReplaceArgs_t; 593 | using StdExt::ReplaceArgsTuple_t; 594 | using StdExt::ReplaceCallingConvention_t; 595 | using StdExt::ArgsModify_t; 596 | using StdExt::ArgsModifyTuple_t; 597 | using StdExt::ArgsInsert_t; 598 | using StdExt::ArgsInsertTuple_t; 599 | using StdExt::ArgsMakeVoid_t; 600 | using StdExt::ArgsAppend_t; 601 | using StdExt::ArgsAppendTuple_t; 602 | using StdExt::ArgsDelete_t; 603 | using StdExt::ReplaceArg_t; 604 | #if defined(STDEXT_SUPPORT_DEPRECATED) 605 | using StdExt::ReplaceNthArg_t; 606 | #endif 607 | using StdExt::ReplaceReturnType_t; 608 | using StdExt::MakeMemberFunctionPtr_t; 609 | #endif 610 | 611 | using StdExt::DisplayAllFunctionTraits; 612 | 613 | using StdExt::IsHasFunctionTraits_v; 614 | #if defined(USE_CONCEPTS) 615 | using StdExt::IsHasFunctionTraits_c; 616 | #endif 617 | 618 | ////////////////////////////////////////////////////////////// 619 | // Always false (undefined) at this writing (for now). May be 620 | // #defined (or removed) in a future release however (still a 621 | // work-in-progress but deferred until reflection is 622 | // available in C++26 - will hopefully resolve several issues 623 | // required to cleanly implement operator detection). 624 | ////////////////////////////////////////////////////////////// 625 | #if defined(STDEXT_SUPPORTS_OPERATOR_DETECTION_TEMPLATES) 626 | using StdExt::OperatorFunctions; 627 | 628 | #if defined(WRITE_DEPENDENT_FUNCTION_DETECTION_TEMPLATES_SUPPORTED) 629 | using StdExt::ClassHasOperator; 630 | using StdExt::ClassHasOperator_v; 631 | using StdExt::ClassHasStaticOperator; 632 | using StdExt::ClassHasStaticOperator_v; 633 | using StdExt::ClassHasNonOverloadedOperator; 634 | using StdExt::ClassHasNonOverloadedOperator_v; 635 | #endif // #if defined(WRITE_DEPENDENT_FUNCTION_DETECTION_TEMPLATES_SUPPORTED) 636 | 637 | using StdExt::ClassHasNonOverloadedStaticOperator; 638 | using StdExt::ClassHasNonOverloadedStaticOperator_v; 639 | using StdExt::ClassHasNonOverloadedOperatorTraits; 640 | using StdExt::ClassHasNonOverloadedOperatorTraits_v; 641 | using StdExt::ClassHasNonOverloadedStaticOperatorTraits; 642 | using StdExt::ClassHasNonOverloadedStaticOperatorTraits_v; 643 | #endif // #if defined(STDEXT_SUPPORTS_OPERATOR_DETECTION_TEMPLATES) 644 | 645 | #if defined(WRITE_DEPENDENT_FUNCTION_DETECTION_TEMPLATES_SUPPORTED) 646 | using StdExt::ClassHasOperator_FunctionCall; 647 | using StdExt::ClassHasOperator_FunctionCall_v; 648 | using StdExt::ClassHasNonOverloadedOperator_FunctionCall; 649 | using StdExt::ClassHasNonOverloadedOperator_FunctionCall_v; 650 | #endif // #if defined(WRITE_DEPENDENT_FUNCTION_DETECTION_TEMPLATES_SUPPORTED) 651 | 652 | using StdExt::ClassHasNonOverloadedOperatorTraits_FunctionCall; 653 | using StdExt::ClassHasNonOverloadedOperatorTraits_FunctionCall_v; 654 | 655 | // See https://wg21.link/P1169R4 (C++23 or later) 656 | #if __cpp_static_call_operator 657 | #if defined(WRITE_DEPENDENT_FUNCTION_DETECTION_TEMPLATES_SUPPORTED) 658 | using StdExt::ClassHasStaticOperator_FunctionCall; 659 | using StdExt::ClassHasStaticOperator_FunctionCall_v; 660 | #endif // #if defined(WRITE_DEPENDENT_FUNCTION_DETECTION_TEMPLATES_SUPPORTED) 661 | 662 | using StdExt::ClassHasNonOverloadedStaticOperator_FunctionCall; 663 | using StdExt::ClassHasNonOverloadedStaticOperator_FunctionCall_v; 664 | using StdExt::ClassHasNonOverloadedStaticOperatorTraits_FunctionCall; 665 | using StdExt::ClassHasNonOverloadedStaticOperatorTraits_FunctionCall_v; 666 | #endif 667 | } // export namespace StdExt 668 | 669 | export namespace StdExt::Private 670 | { 671 | ////////////////////////////////////////////////////////// 672 | // Both of these are declared in "StdExt::Private" (so 673 | // for internal use by the library only) but need to be 674 | // exported anyway (the 1st below only required though if 675 | // the constant just below is #defined). The library's 676 | // function detection templates are declared in the 677 | // user's own code using the function detection macros 678 | // and some of these templates invoke the following 679 | // templates (declared in namespace "StdExt::Private"). 680 | // Because the function detection templates themselves 681 | // are declared in the user's own code however (so 682 | // declared in the user's own namespace, not the 683 | // library's), the following need to be exported so they 684 | // can be called from those templates (in whatever 685 | // namespace the user declares these templates in). 686 | ////////////////////////////////////////////////////////// 687 | #if defined(WRITE_DEPENDENT_FUNCTION_DETECTION_TEMPLATES_SUPPORTED) 688 | using StdExt::Private::ClassHasFunctionStaticCastTarget_t; 689 | #endif 690 | using StdExt::Private::InvokeHasFunctionTraits_v; 691 | } // export namespace StdExt::Private -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | See https://www.hexadigm.com/GenericLib/License.html -------------------------------------------------------------------------------- /TypeTraits.h: -------------------------------------------------------------------------------- 1 | // This header is deprecated! See details below. 2 | 3 | #ifndef TYPE_TRAITS 4 | #define TYPE_TRAITS 5 | ///////////////////////////////////////////////////////////////////////////// 6 | // LICENSE NOTICE 7 | // -------------- 8 | // Copyright (c) Hexadigm Systems 9 | // 10 | // Permission to use this software is granted under the following license: 11 | // https://www.hexadigm.com/GenericLib/License.html 12 | // 13 | // This copyright notice must be included in this and all copies of the 14 | // software as described in the above license. 15 | // 16 | // DESCRIPTION 17 | // ----------- 18 | // This file is deprecated. Users should #include "FunctionTraits.h" instead. 19 | // The following header just defers to it with a warning message (see this 20 | // below for details). 21 | ///////////////////////////////////////////////////////////////////////////// 22 | #pragma message("Warning: This header (\"TypeTraits.h\") was renamed to \"FunctionTraits.h\". The old name continues to be supported for now (it just defers to the new header), but may be removed in a future release. Users should update their code to #include \"FunctionTraits.h\" instead.") 23 | #include "FunctionTraits.h" 24 | 25 | #endif // #ifndef TYPE_TRAITS (#include guard) --------------------------------------------------------------------------------