Improved compatibility with older versions of MSVC.
authorDavid Korth <gerbilsoft@gerbilsoft.com>
Sat, 5 Sep 2015 16:27:43 +0000 (12:27 -0400)
committerDavid Korth <gerbilsoft@gerbilsoft.com>
Sat, 5 Sep 2015 16:27:43 +0000 (12:27 -0400)
Note that stdint.h was first added in MSVC 2010, so for older versions,
you'll need to add stdint.h to the MSVC include path first.

win32-msvc.cmake: Only add -Zc:wchar_t for MSVC 2002 and later.

c++11-compat.msvc.h:
- 'sealed' was added in MSVC 2005.
- Support for variadic macros was also added in MSVC 2005.
- (TODO: Verify MSVC 2002 and 2003.)

libcompat:
- Check for localtime_s(). This function seems to have been added
  in MSVC 2005, but since I'm not sure, we're better off doing a
  CMake check instead of version checks. This also works for MinGW,
  which provides inlined versions.
  - Sidenote: On MSVC 2005, localtime_s() is a macro that's defined
    to either _localtime32_s() or _localtime64_s(). The inline part
    of CHECK_FUNCTION_EXISTS_OR_INLINE() will handle that.

W32U_alloca.h:
- WtoA_filename(): Declare 'int cbMbs' at the top of the macro.
  I didn't catch this with MSVC 2010 because the ANSI code is
  disabled there, since 2010's minimum target is Windows 2000.

W32U_libc.h:
- Use #pragma message() instead of #warning. MSVC doesn't support
  this preprocessor directive at all, but it was never tripped
  until I tried compiling with MSVC 6.0, which doesn't support
  _fseeki64() and _ftelli64().

cpuflags_x86.h: Added an inline asm version for older MSVC that
doesn't support CPU intrinsics, e.g. __cpuid().
- TODO: Do MSVC 2002 or 2003 support __cpuid()?

cmake/platform/win32-msvc.cmake
src/c++11-compat.h
src/c++11-compat.msvc.h
src/libcompat/CMakeLists.txt
src/libcompat/W32U/W32U_alloca.h
src/libcompat/W32U/W32U_libc.h
src/libcompat/config.libcompat.h.in
src/libcompat/cpuflags_x86.h
src/libcompat/reentrant.h

index 047bc9b..5c4d276 100644 (file)
@@ -2,8 +2,10 @@
 # For Microsoft Visual C++ compilers.
 
 # Basic platform flags for MSVC:
-# - wchar_t should be a distinct type.
-SET(GENS_C_FLAGS_WIN32 "${GENS_C_FLAGS_WIN32} -Zc:wchar_t")
+# - wchar_t should be a distinct type. (MSVC 2002+)
+IF(MSVC_VERSION GREATER 1200)
+       SET(GENS_C_FLAGS_WIN32 "${GENS_C_FLAGS_WIN32} -Zc:wchar_t")
+ENDIF()
 
 # Disable ANSI Windows support if:
 # - We're building for 64-bit.
index 9676104..e94e9ac 100644 (file)
@@ -72,8 +72,11 @@ namespace std {
 /* Explicit override/final. */
 #ifdef CXX11_COMPAT_OVERRIDE
 #define override
+/* final may be defined as 'sealed' on older MSVC */
+#ifndef final
 #define final
 #endif
+#endif
 
 /** Other compatibility stuff. **/
 
index e7ed1e2..61ca2cd 100644 (file)
  * MSVC 2010 (10.0) does support override, but not final.
  * However, it has a "sealed" keyword that works almost
  * the same way as final.
+ *
+ * 'sealed' is available starting with MSVC 2005 (8.0).
+ * TODO: Verify MSVC 2002 and 2003.
  */
+#if (_MSC_VER >= 1400)
 #define final sealed
 #endif
+#endif
 
 #if (_MSC_VER < 1600)
 /**
  * MSVC 2015 (14.0) supports snprintf() and vsnprintf().
  * Older versions have them prefixed with underscores.
  */
-#if _MSC_VER < 1900
+#if _MSC_VER < 1400
+/**
+ * MSVC 2005 added support for variadic macros. *
+ * https://msdn.microsoft.com/en-US/library/ms177415(v=vs.80).aspx
+ * TODO: Verify MSVC 2002 and 2003.
+ */
+#define snprintf _snprintf
+#define vsnprintf _vsnprintf
+#elif _MSC_VER < 1900
+/* MSVC 2005 - 2013: variadic macros supported, but no snprintf(). */
 #define snprintf(str, size, format, ...) _snprintf(str, size, format, __VA_ARGS__)
 #define vsnprintf(str, size, format, ap) _vsnprintf(str, size, format, ap)
 #endif /* _MSC_VER < 1900 */
index 21c269a..193e63e 100644 (file)
@@ -45,6 +45,9 @@ MACRO(CHECK_FUNCTION_EXISTS_OR_INLINE FUNCTION HEADER SAMPLE_CODE VARIABLE)
        ENDIF(NOT DEFINED ${VARIABLE})
 ENDMACRO(CHECK_FUNCTION_EXISTS_OR_INLINE VARIABLE)
 CHECK_FUNCTION_EXISTS_OR_INLINE(localtime_r "time.h" "time_t tm; localtime_r(&tm, NULL);" HAVE_LOCALTIME_R)
+# NOTE: May be _localtime32_s() or _localtime64_s() on MSVC 2005+.
+# The "inline" part will detect that.
+CHECK_FUNCTION_EXISTS_OR_INLINE(localtime_s "time.h" "time_t tm; localtime_s(&tm, NULL);" HAVE_LOCALTIME_S)
 CHECK_FUNCTION_EXISTS(getpwuid_r HAVE_GETPWUID_R)
 # NOTE: These should either both be present or both be absent.
 # We'll test for both of them anyway.
index 1be9ffc..59cca64 100644 (file)
@@ -34,6 +34,7 @@
 // C includes.
 #include <string.h>
 #include <ctype.h>
+#include <wchar.h>
 
 #ifdef _MSC_VER
 // MSVC: _alloca() is in malloc.h.
  */
 #define WtoA_filename(str) do { \
        const wchar_t *WtoA_src = str##W; \
+       int cbMbs; \
        /* Check if the path is absolute. */ \
        /* NOTE: Only checking for backslashes here. */ \
        if (!memcmp(str##W, L"\\\\?\\", 4*sizeof(*str##W))) { \
                /* Path is absolute. */ \
                WtoA_src += 4; \
        } \
-       int cbMbs = WideCharToMultiByte(CP_ACP, 0, WtoA_src, -1, NULL, 0, NULL, NULL); \
+       cbMbs = WideCharToMultiByte(CP_ACP, 0, WtoA_src, -1, NULL, 0, NULL, NULL); \
        if (cbMbs > 0) { \
                str##A = alloca(cbMbs * sizeof(*str##A)); \
                WideCharToMultiByte(CP_ACP, 0, WtoA_src, -1, str##A, cbMbs, NULL, NULL); \
index 593fd74..7a0d09b 100644 (file)
@@ -76,13 +76,13 @@ FILE *W32U_fopen(const char *filename, const char *mode);
 #ifdef HAVE_FSEEKI64
 #define fseeko(stream, offset, origin) _fseeki64(stream, offset, origin)
 #else /* !HAVE_FSEEKI64 */
-#warning MSVC is missing _fseeki64(), files over 2 GB may not be handled correctly
+#pragma message("MSVC is missing _fseeki64(), files larger than 2 GB may not be handled correctly.")
 #define fseeko(stream, offset, origin) fseek(stream, (int)offset, origin)
 #endif /* HAVE_FSEEKI64 */
 #ifdef HAVE_FTELLI64
 #define ftello(stream) _ftelli64(stream)
 #else /* !HAVE_FTELLI64 */
-#warning MSVC is missing _ftelli64(), files over 2 GB may not be handled correctly
+#pragma message("MSVC is missing _ftelli64(), files larger than 2 GB may not be handled correctly.")
 #define ftello(stream) (__int64)ftell(stream)
 #endif /* HAVE_FTELLI64 */
 #endif /* _MSC_VER */
index 28d8819..7dc3999 100644 (file)
@@ -28,6 +28,9 @@
 /* Define to 1 if you have the `localtime_r` function. */
 #cmakedefine HAVE_LOCALTIME_R 1
 
+/* Define to 1 if you have the `localtime_s` function. */
+#cmakedefine HAVE_LOCALTIME_S 1
+
 /* Define to 1 if you have the `getpwuid_r` function. */
 #cmakedefine HAVE_GETPWUID_R 1
 
index c2a3128..d9200fd 100644 (file)
@@ -83,7 +83,9 @@
 #define CPUID_EXT_PROC_BRAND_STRING_2          ((uint32_t)(0x80000003))
 #define CPUID_EXT_PROC_BRAND_STRING_3          ((uint32_t)(0x80000004))
 
-#ifdef _MSC_VER
+#if defined(_MSC_VER) && _MSC_VER >= 1400
+// __cpuid() was added in MSVC 2005.
+// (TODO: Check MSVC 2002 and 2003?)
 #include <intrin.h>
 #endif
 
                );                                              \
        } while (0)
 #endif
-#elif defined(_MSC_VER)
-// CPUID macro for MSVC.
+#elif defined(_MSC_VER) && MSC_VER >= 1400
+// CPUID macro for MSVC 2005+
 #define CPUID(level, a, b, c, d) do {                          \
        int cpuInfo[4];                                         \
        __cpuid(cpuInfo, (level));                              \
        (c) = cpuInfo[2];                                       \
        (d) = cpuInfo[3];                                       \
 } while (0)
+#elif defined(_MSC_VER) && MSC_VER < 1400 && defined(_M_IX86)
+// CPUID macro for old MSVC that doesn't support intrinsics.
+// (TODO: Check MSVC 2002 and 2003?)
+#define CPUID(level, a, b, c, d) do {                          \
+       __asm   cpuid                                           \
+       __asm   mov     a, eax                                  \
+       __asm   mov     b, ebx                                  \
+       __asm   mov     c, ecx                                  \
+       __asm   mov     d, edx                                  \
+} while (0)
 #else
 #error Missing 'cpuid' asm implementation for this compiler.
 #endif
index a5db149..aa8d1d8 100644 (file)
@@ -38,10 +38,10 @@ extern "C" {
 #endif
 
 #ifndef HAVE_LOCALTIME_R
-#ifdef _WIN32
+#ifdef HAVE_LOCALTIME_S
 /**
- * MinGW-w64's localtime_r() wrapper.
- * Uses MSVCRT's localtime_s().
+ * localtime_r() wrapper using MSVCRT's localtime_s().
+ * Based on MinGW-w64's localtime_r() wrapper.
  */
 static __forceinline struct tm *__cdecl localtime_r(const time_t *_Time, struct tm *_Tm)
 {