From 3f2176007a3c1ed5ceba8db06afa9269fce19a27 Mon Sep 17 00:00:00 2001 From: Michael Date: Wed, 5 Jan 2022 13:04:51 +0000 Subject: [PATCH] Improve support for Apple Silicon and macOS --- groups/bdl/bdlpcre/bdlpcre_regex.cpp | 2 + groups/bdl/bdlpcre/bdlpcre_regex.h | 1 - groups/bdl/bdlpcre/bdlpcre_regex.t.cpp | 2 + groups/bdl/bdls/bdls_testutil.t.cpp | 4 +- groups/bsl/bslh/bslh_siphashalgorithm.cpp | 2 +- groups/bsl/bslh/bslh_spookyhashalgorithm.h | 2 +- groups/bsl/bslim/bslim_testutil.t.cpp | 4 +- groups/bsl/bsls/bsls_alignmentfromtype.t.cpp | 2 + groups/bsl/bsls/bsls_alignmentimp.t.cpp | 2 + groups/bsl/bsls/bsls_bsltestutil.t.cpp | 4 +- groups/bsl/bsls/bsls_log.t.cpp | 4 +- groups/bsl/bsls/bsls_platform.h | 18 +++- groups/bsl/bsls/bsls_platformutil.h | 7 ++ groups/bsl/bsls/bsls_spinlock.h | 4 +- groups/bsl/bsls/bsls_timeutil.cpp | 107 +++++-------------- groups/bsl/bsls/bsls_timeutil.h | 12 +-- groups/bsl/bslstl/bslstl_error.t.cpp | 8 +- 17 files changed, 79 insertions(+), 106 deletions(-) diff --git a/groups/bdl/bdlpcre/bdlpcre_regex.cpp b/groups/bdl/bdlpcre/bdlpcre_regex.cpp index 272ca35e0e..7766da7e04 100644 --- a/groups/bdl/bdlpcre/bdlpcre_regex.cpp +++ b/groups/bdl/bdlpcre/bdlpcre_regex.cpp @@ -68,6 +68,8 @@ namespace { static const bool k_IS_JIT_SUPPORTED = #if defined(BSLS_PLATFORM_CPU_SPARC) && defined (BSLS_PLATFORM_CPU_64_BIT) false; +#elif defined(BSLS_PLATFORM_CPU_ARM) && defined (BSLS_PLATFORM_CPU_64_BIT) +false; #else true; #endif diff --git a/groups/bdl/bdlpcre/bdlpcre_regex.h b/groups/bdl/bdlpcre/bdlpcre_regex.h index 6100ae84d0..03b1d076b6 100644 --- a/groups/bdl/bdlpcre/bdlpcre_regex.h +++ b/groups/bdl/bdlpcre/bdlpcre_regex.h @@ -232,7 +232,6 @@ BSLS_IDENT("$Id$ $CSID$") // JIT is supported on the following platforms: //.. // ARM 32-bit (v5, v7, and Thumb2) -// ARM 64-bit // Intel x86 32-bit and 64-bit // MIPS 32-bit and 64-bit // Power PC 32-bit and 64-bit diff --git a/groups/bdl/bdlpcre/bdlpcre_regex.t.cpp b/groups/bdl/bdlpcre/bdlpcre_regex.t.cpp index e1ad6d53b6..09ceeeaf91 100644 --- a/groups/bdl/bdlpcre/bdlpcre_regex.t.cpp +++ b/groups/bdl/bdlpcre/bdlpcre_regex.t.cpp @@ -1656,6 +1656,8 @@ int main(int argc, char *argv[]) #if defined(BSLS_PLATFORM_CPU_SPARC_V9) ASSERT(false == Obj::isJitAvailable()); +#elif defined(BSLS_PLATFORM_CPU_ARM) && defined(BSLS_PLATFORM_CPU_64_BIT) + ASSERT(false == Obj::isJitAvailable()); #else ASSERT(true == Obj::isJitAvailable()); #endif diff --git a/groups/bdl/bdls/bdls_testutil.t.cpp b/groups/bdl/bdls/bdls_testutil.t.cpp index 61c8e394db..3fb78766d8 100644 --- a/groups/bdl/bdls/bdls_testutil.t.cpp +++ b/groups/bdl/bdls/bdls_testutil.t.cpp @@ -320,7 +320,7 @@ static int verbose, veryVerbose, veryVeryVerbose; namespace { -#if defined(BSLS_PLATFORM_OS_WINDOWS) +#if defined(BSLS_PLATFORM_OS_WINDOWS) || defined(BSLS_PLATFORM_OS_DARWIN) typedef struct stat StatType; #else typedef struct stat64 StatType; @@ -329,7 +329,7 @@ typedef struct stat64 StatType; inline int fstatFunc(int fd, StatType *buf) { -#if defined(BSLS_PLATFORM_OS_WINDOWS) +#if defined(BSLS_PLATFORM_OS_WINDOWS) || defined(BSLS_PLATFORM_OS_DARWIN) return fstat(fd, buf); #else return fstat64(fd, buf); diff --git a/groups/bsl/bslh/bslh_siphashalgorithm.cpp b/groups/bsl/bslh/bslh_siphashalgorithm.cpp index 76b0cc8918..0e14cc6d85 100644 --- a/groups/bsl/bslh/bslh_siphashalgorithm.cpp +++ b/groups/bsl/bslh/bslh_siphashalgorithm.cpp @@ -121,7 +121,7 @@ static u64 u8to64_le(const u8* p) { BSLS_ASSERT(p); -#if defined(BSLS_PLATFORM_CPU_X86) || defined(BSLS_PLATFORM_CPU_X86_64) +#if defined(BSLS_PLATFORM_IS_LITTLE_ENDIAN) return *reinterpret_cast(p); // Ignore alignment. #else u64 ret; diff --git a/groups/bsl/bslh/bslh_spookyhashalgorithm.h b/groups/bsl/bslh/bslh_spookyhashalgorithm.h index c2f768ce16..c7b03715d0 100644 --- a/groups/bsl/bslh/bslh_spookyhashalgorithm.h +++ b/groups/bsl/bslh/bslh_spookyhashalgorithm.h @@ -380,7 +380,7 @@ SpookyHashAlgorithm::SpookyHashAlgorithm() inline SpookyHashAlgorithm::SpookyHashAlgorithm(const char *seed) : d_state( -#if !defined(BSLS_PLATFORM_CPU_X86_64) && !defined(BSLS_PLATFORM_CPU_X86) +#if !defined(BSLS_PLATFORM_IS_LITTLE_ENDIAN) static_cast(seed[0]) << 56 | static_cast(seed[1]) << 48 | static_cast(seed[2]) << 40 | diff --git a/groups/bsl/bslim/bslim_testutil.t.cpp b/groups/bsl/bslim/bslim_testutil.t.cpp index 20f5264870..d70bae529a 100644 --- a/groups/bsl/bslim/bslim_testutil.t.cpp +++ b/groups/bsl/bslim/bslim_testutil.t.cpp @@ -314,7 +314,7 @@ static bool verbose, veryVerbose, veryVeryVerbose; // GLOBAL HELPER TYPES AND FUNCTIONS //----------------------------------------------------------------------------- -#if defined(BSLS_PLATFORM_OS_WINDOWS) +#if defined(BSLS_PLATFORM_OS_WINDOWS) || defined(BSLS_PLATFORM_OS_DARWIN) typedef struct stat StatType; #else typedef struct stat64 StatType; @@ -323,7 +323,7 @@ typedef struct stat64 StatType; inline int fstatFunc(int fd, StatType *buf) { -#if defined(BSLS_PLATFORM_OS_WINDOWS) +#if defined(BSLS_PLATFORM_OS_WINDOWS) || defined(BSLS_PLATFORM_OS_DARWIN) return fstat(fd, buf); #else return fstat64(fd, buf); diff --git a/groups/bsl/bsls/bsls_alignmentfromtype.t.cpp b/groups/bsl/bsls/bsls_alignmentfromtype.t.cpp index 62b6567a12..b732e9ba89 100644 --- a/groups/bsl/bsls/bsls_alignmentfromtype.t.cpp +++ b/groups/bsl/bsls/bsls_alignmentfromtype.t.cpp @@ -619,6 +619,8 @@ int main(int argc, char *argv[]) EXP_U1_ALIGNMENT = 8; #if defined(BSLS_PLATFORM_CPU_POWERPC) && defined(BSLS_PLATFORM_OS_LINUX) EXP_LONG_DOUBLE_ALIGNMENT = 8; +#elif defined(BSLS_PLATFORM_CPU_ARM) + EXP_LONG_DOUBLE_ALIGNMENT = 8; #else EXP_LONG_DOUBLE_ALIGNMENT = 16; #endif diff --git a/groups/bsl/bsls/bsls_alignmentimp.t.cpp b/groups/bsl/bsls/bsls_alignmentimp.t.cpp index 49a45c3854..aef3a34c7f 100644 --- a/groups/bsl/bsls/bsls_alignmentimp.t.cpp +++ b/groups/bsl/bsls/bsls_alignmentimp.t.cpp @@ -415,6 +415,8 @@ int main(int argc, char *argv[]) EXP_U1_ALIGNMENT = 8; #if defined(BSLS_PLATFORM_CPU_POWERPC) && defined(BSLS_PLATFORM_OS_LINUX) EXP_LONG_DOUBLE_ALIGNMENT = 8; +#elif defined(BSLS_PLATFORM_CPU_ARM) + EXP_LONG_DOUBLE_ALIGNMENT = 8; #else EXP_LONG_DOUBLE_ALIGNMENT = 16; #endif diff --git a/groups/bsl/bsls/bsls_bsltestutil.t.cpp b/groups/bsl/bsls/bsls_bsltestutil.t.cpp index 278fde0bf9..c77028782b 100644 --- a/groups/bsl/bsls/bsls_bsltestutil.t.cpp +++ b/groups/bsl/bsls/bsls_bsltestutil.t.cpp @@ -842,7 +842,7 @@ double dummyDoubleFunction() // GLOBAL HELPER CLASSES FOR TESTING // ---------------------------------------------------------------------------- -#if defined(BSLS_PLATFORM_OS_WINDOWS) +#if defined(BSLS_PLATFORM_OS_WINDOWS) || defined(BSLS_PLATFORM_OS_DARWIN) typedef struct stat StatType; #else typedef struct stat64 StatType; @@ -850,7 +850,7 @@ typedef struct stat64 StatType; inline int fstatFunc(int fd, StatType *buf) { -#if defined(BSLS_PLATFORM_OS_WINDOWS) +#if defined(BSLS_PLATFORM_OS_WINDOWS) || defined(BSLS_PLATFORM_OS_DARWIN) return fstat(fd, buf); #else return fstat64(fd, buf); diff --git a/groups/bsl/bsls/bsls_log.t.cpp b/groups/bsl/bsls/bsls_log.t.cpp index d161624a45..45e84cb702 100644 --- a/groups/bsl/bsls/bsls_log.t.cpp +++ b/groups/bsl/bsls/bsls_log.t.cpp @@ -445,7 +445,7 @@ const char *LargeTestData::expectedOutput() const // GLOBAL HELPER CLASSES FOR TESTING // ---------------------------------------------------------------------------- -#if defined(BSLS_PLATFORM_OS_WINDOWS) +#if defined(BSLS_PLATFORM_OS_WINDOWS) || defined(BSLS_PLATFORM_OS_DARWIN) typedef struct stat StatType; #else typedef struct stat64 StatType; @@ -453,7 +453,7 @@ typedef struct stat64 StatType; inline int fstatFunc(int fd, StatType *buf) { -#if defined(BSLS_PLATFORM_OS_WINDOWS) +#if defined(BSLS_PLATFORM_OS_WINDOWS) || defined(BSLS_PLATFORM_OS_DARWIN) return fstat(fd, buf); #else return fstat64(fd, buf); diff --git a/groups/bsl/bsls/bsls_platform.h b/groups/bsl/bsls/bsls_platform.h index 20d5cdf1d6..8c30a60563 100644 --- a/groups/bsl/bsls/bsls_platform.h +++ b/groups/bsl/bsls/bsls_platform.h @@ -483,8 +483,11 @@ struct bsls_Platform_Assert; #else #define BSLS_PLATFORM_CPU_SPARC_32 1 #endif - #elif defined(__arm__) + #elif defined(__arm__) || defined(__arm64__) #define BSLS_PLATFORM_CPU_ARM 1 + #if defined(__arm64__) + #define BSLS_PLATFORM_CPU_64_BIT 1 + #endif #if defined(__ARM_ARCH) #if __ARM_ARCH == 6 #define BSLS_PLATFORM_CPU_ARM_V6 @@ -506,6 +509,11 @@ struct bsls_Platform_Assert; || defined(__ARM_ARCH_7M__) \ || defined(__ARM_ARCH_7R__) #define BSLS_PLATFORM_CPU_ARM_V7 + #elif defined(__ARM64_ARCH_8__) \ + || defined(__ARM_ARCH_8_3__) \ + || defined(__ARM_ARCH_8_4__) \ + || defined(__ARM_ARCH_8_5__) + #define BSLS_PLATFORM_CPU_ARM_V8 #else #error "Unsupported ARM platform." #endif @@ -911,6 +919,8 @@ struct Platform { struct CpuArmv5 : CpuArm {}; struct CpuArmv6 : CpuArm {}; struct CpuArmv7 : CpuArm {}; + struct CpuArmv8 : CpuArm {}; + struct CpuArmv9 : CpuArm {}; // PLATFORM TRAITS @@ -987,6 +997,12 @@ struct Platform { #if defined(BSLS_PLATFORM_CPU_ARM_V7) typedef CpuArmv7 Cpu; #endif + #if defined(BSLS_PLATFORM_CPU_ARM_V8) + typedef CpuArmv8 Cpu; + #endif + #if defined(BSLS_PLATFORM_CPU_ARM_V9) + typedef CpuArmv9 Cpu; + #endif }; diff --git a/groups/bsl/bsls/bsls_platformutil.h b/groups/bsl/bsls/bsls_platformutil.h index f1c58ecf52..1d6ef4c1fd 100644 --- a/groups/bsl/bsls/bsls_platformutil.h +++ b/groups/bsl/bsls/bsls_platformutil.h @@ -211,6 +211,13 @@ struct PlatformUtil { // defined in 'bsls_platform' instead. #endif +#if defined(BSLS_PLATFORM_CPU_ARM) + #define BSLS_PLATFORMUTIL_IS_LITTLE_ENDIAN \ + BSLS_PLATFORM_IS_LITTLE_ENDIAN + // DEPRECATED: Use preprocessor macro 'BSLS_PLATFORM_IS_LITTLE_ENDIAN' + // defined in 'bsls_platform' instead. +#endif + #if !defined(BSLS_PLATFORMUTIL_IS_LITTLE_ENDIAN) #define BSLS_PLATFORMUTIL_IS_BIG_ENDIAN BSLS_PLATFORM_IS_BIG_ENDIAN // DEPRECATED: Use preprocessor macro 'BSLS_PLATFORM_IS_BIG_ENDIAN' diff --git a/groups/bsl/bsls/bsls_spinlock.h b/groups/bsl/bsls/bsls_spinlock.h index 74330f1378..916f7a14ac 100644 --- a/groups/bsl/bsls/bsls_spinlock.h +++ b/groups/bsl/bsls/bsls_spinlock.h @@ -292,7 +292,7 @@ extern "C" { #include #endif -#if !(defined(BSLS_PLATFORM_OS_SOLARIS)) && !(defined(BSLS_PLATFORM_OS_AIX)) +#if defined(BSLS_PLATFORM_CPU_X86) || defined(BSLS_PLATFORM_CPU_X86_64) #include #include #endif @@ -451,7 +451,7 @@ void SpinLock::doBackoff(int *count) { inline void SpinLock::pause() { -#if !(defined(BSLS_PLATFORM_OS_SOLARIS)) && !(defined(BSLS_PLATFORM_OS_AIX)) +#if defined(BSLS_PLATFORM_CPU_X86) || defined(BSLS_PLATFORM_CPU_X86_64) _mm_pause(); #endif } diff --git a/groups/bsl/bsls/bsls_timeutil.cpp b/groups/bsl/bsls/bsls_timeutil.cpp index 160670765f..351d822e31 100644 --- a/groups/bsl/bsls/bsls_timeutil.cpp +++ b/groups/bsl/bsls/bsls_timeutil.cpp @@ -448,114 +448,57 @@ struct MachTimerUtil { private: // CLASS DATA - static bsls::AtomicOperations::AtomicTypes::Int - s_initRequired; - - static bsls::Types::Int64 s_initialTime; + static bsls::Types::Uint64 s_initialTime; // initial time for the Mach // hardware timer - static mach_timebase_info_data_t s_timeBase; - // time base used to scale the - // absolute raw timer values - public: // CLASS METHODS + static bsls::Types::Uint64 getNanosecondsUptime(); + // Return converted to nanoseconds the current uptime as reported by + // clock_gettime_nsec_np. + static void initialize(); // Initialize the static data used by 'MachTimerUtil' (currently the - // 's_initialTime' and the 's_timeBase' values). + // 's_initialTime' value). - static bsls::Types::Int64 getTimerRaw(); + static bsls::Types::Uint64 getTimerRaw(); // Return a machine-dependent value representing the current time. - // 'timeValue' must be converted by the 'convertRawTime' method to - // conventional units (nanoseconds). This method is intended to - // facilitate accurate timing of small segments of code, and care must - // be used in interpreting the results. The behavior is undefined - // unless 'initialize' has been called. Note that this method is - // thread-safe only if 'initialize' has been called before. - - static bsls::Types::Int64 convertRawTime(bsls::Types::Int64 rawTime); - // Convert the specified 'rawTime' to a value in nanoseconds, - // referenced to an arbitrary but fixed origin, and return the result - // of the conversion. The behavior is undefined unless 'initialize' - // has been called. Note that this method is thread-safe only if - // 'initialize' has been called before. }; -bsls::Types::Int64 MachTimerUtil::s_initialTime = {-1}; -mach_timebase_info_data_t MachTimerUtil::s_timeBase; +bsls::Types::Uint64 MachTimerUtil::s_initialTime = {0}; inline -void MachTimerUtil::initialize() +bsls::Types::Uint64 MachTimerUtil::getNanosecondsUptime() { - static bsls::BslOnce once = BSLS_BSLONCE_INITIALIZER; - - bsls::BslOnceGuard onceGuard; - if (onceGuard.enter(&once)) { - - // There is little official documentation on 'mach_absolute_time' - // and 'mach_timebase_info'. The 'mach_absolute_time' return value - // is declared 'uint64_t' in 'mach/mach_time.h' and it has been - // observed to have the high bit set on hardware with an uptime of - // only 18 days. Therefore, a base value is saved in 'initialize' - // and all returned 'getTimerRaw' values are relative to that value. - // - // According to a technical question found on Apple's website, the - // value returned by 'mach_absolute_time' can be scaled correctly - // without a dependency on the 'CoreServices' framework by calling - // 'mach_timebase_info' and using the returned values. The values - // do not change, so they are cached in 'initialize'. - // - //: o https://developer.apple.com/library/mac/documentation/darwin - //: /conceptual/kernelprogramming/services/services.html - //: o https://developer.apple.com/library/mac/qa/qa1398/_index.html - - s_initialTime = (bsls::Types::Int64) mach_absolute_time(); - - (void) mach_timebase_info(&s_timeBase); + // The original implementation used 'mach_absolute_time()', which is + // considered deprecated (since it requires scaling on Apple Silicon). + // + //: o https://developer.apple.com/documentation/kernel/1462446-mach_absolute_time - BSLS_ASSERT(0 < s_timeBase.numer); - BSLS_ASSERT(0 < s_timeBase.denom); - } + return static_cast( + clock_gettime_nsec_np(CLOCK_UPTIME_RAW)); } inline -bsls::Types::Int64 MachTimerUtil::convertRawTime(bsls::Types::Int64 rawTime) +void MachTimerUtil::initialize() { - initialize(); - -#ifdef __SIZEOF_INT128__ - - // Use the built-in '__int128' type to avoid any potential overflow. - - __int128 result = (__int128) rawTime * - (__int128) s_timeBase.numer / - (__int128) s_timeBase.denom; - return static_cast(result); - -#else // !__SIZEOF_INT128__ - - // In practice, it is not expected that multiplying 'rawTime' by - // 's_timeBase.numer' will overflow an Int64. The 'numer' and - // 'denom' values have been observed to both be 1 on a late model - // laptop and Mac mini. Just to be safe, the overflow is checked in safe - // builds. - - BSLS_ASSERT_SAFE(LLONG_MAX / s_timeBase.numer >= rawTime && - LLONG_MIN / s_timeBase.numer <= rawTime); + static bsls::BslOnce once = BSLS_BSLONCE_INITIALIZER; - return rawTime * s_timeBase.numer / s_timeBase.denom; + bsls::BslOnceGuard onceGuard; + if (onceGuard.enter(&once)) { -#endif // !__SIZEOF_INT128__ + s_initialTime = getNanosecondsUptime(); + } } inline -bsls::Types::Int64 MachTimerUtil::getTimerRaw() +bsls::Types::Uint64 MachTimerUtil::getTimerRaw() { initialize(); - return static_cast( - mach_absolute_time() - s_initialTime); + return static_cast( + getNanosecondsUptime() - s_initialTime); } #endif @@ -606,7 +549,7 @@ TimeUtil::convertRawTime(TimeUtil::OpaqueNativeTime rawTime) #elif defined BSLS_PLATFORM_OS_DARWIN - return MachTimerUtil::convertRawTime(rawTime.d_opaque); + return rawTime.d_opaque; #elif defined BSLS_PLATFORM_OS_UNIX diff --git a/groups/bsl/bsls/bsls_timeutil.h b/groups/bsl/bsls/bsls_timeutil.h index 13813fa63c..6a16bde0dd 100644 --- a/groups/bsl/bsls/bsls_timeutil.h +++ b/groups/bsl/bsls/bsls_timeutil.h @@ -222,17 +222,17 @@ struct TimeUtil { // TYPES #if defined BSLS_PLATFORM_OS_SOLARIS - typedef struct { Types::Int64 d_opaque; } OpaqueNativeTime; + typedef struct { Types::Int64 d_opaque; } OpaqueNativeTime; #elif defined BSLS_PLATFORM_OS_AIX - typedef timebasestruct_t OpaqueNativeTime; + typedef timebasestruct_t OpaqueNativeTime; #elif defined(BSLS_PLATFORM_OS_LINUX) || defined(BSLS_PLATFORM_OS_CYGWIN) - typedef timespec OpaqueNativeTime; + typedef timespec OpaqueNativeTime; #elif defined BSLS_PLATFORM_OS_DARWIN - typedef struct { Types::Int64 d_opaque; } OpaqueNativeTime; + typedef struct { Types::Uint64 d_opaque; } OpaqueNativeTime; #elif defined BSLS_PLATFORM_OS_UNIX - typedef timeval OpaqueNativeTime; + typedef timeval OpaqueNativeTime; #elif defined BSLS_PLATFORM_OS_WINDOWS - typedef struct { Types::Int64 d_opaque; } OpaqueNativeTime; + typedef struct { Types::Int64 d_opaque; } OpaqueNativeTime; #endif // CLASS METHODS diff --git a/groups/bsl/bslstl/bslstl_error.t.cpp b/groups/bsl/bslstl/bslstl_error.t.cpp index c533631f8a..a37537426f 100644 --- a/groups/bsl/bslstl/bslstl_error.t.cpp +++ b/groups/bsl/bslstl/bslstl_error.t.cpp @@ -1350,8 +1350,8 @@ int main(int argc, char *argv[]) printf("\t%d %s %d %zu\n", j, cj.category().name(), cj.value(), hasher(cj)); } -#ifdef BSLS_PLATFORM_OS_WINDOWS - // Windows implementation appears to not include the category. +#if defined(BSLS_PLATFORM_OS_WINDOWS) || defined(BSLS_PLATFORM_OS_DARWIN) + // macOS/Windows implementations appear to not include the category. bool equal = ci.value() == cj.value(); #else bool equal = i == j; @@ -1393,7 +1393,7 @@ int main(int argc, char *argv[]) printf("\t%d %s %d %zu\n", j, cj.category().name(), cj.value(), hasher(cj)); } -#ifdef BSLS_PLATFORM_OS_WINDOWS +#if defined(BSLS_PLATFORM_OS_WINDOWS) || defined(BSLS_PLATFORM_OS_DARWIN) // Windows implementation appears to not include the category. bool equal = ci.value() == cj.value(); #else @@ -1415,7 +1415,7 @@ int main(int argc, char *argv[]) printf("\t%d %s %d %zu\n", j, cj.category().name(), cj.value(), hasher(cj)); } -#ifdef BSLS_PLATFORM_OS_WINDOWS +#if defined(BSLS_PLATFORM_OS_WINDOWS) || defined(BSLS_PLATFORM_OS_DARWIN) // Windows implementation appears to not include the category. bool equal = ci.value() == cj.value(); #else