Kaynağa Gözat

autoinit_funcs.h: significantly reworked

Widen support for compilers with function "attribute" support.
Improved W32 support.
Implemented more flexibility with more control macros for users.
Separated "namespace" by prefixing used names with 'AIF_'.
autoinit_funcs.h: significantly reworked

Widen support for compilers with function "attribute" support.
Improved W32 support.
Implemented more flexibility with more control macros for users.
Separated "namespace" by prefixing names with 'AIF_'.
Evgeny Grin (Karlson2k) 1 yıl önce
ebeveyn
işleme
0c4f15bb0c
1 değiştirilmiş dosya ile 414 ekleme ve 179 silme
  1. 414 179
      src/mhd2/autoinit_funcs.h

+ 414 - 179
src/mhd2/autoinit_funcs.h

@@ -1,6 +1,6 @@
 /*
- *  AutoinitFuncs: Automatic Initialization and Deinitialization Functions
- *  Copyright(C) 2014-2023 Karlson2k (Evgeny Grin)
+ *  AutoinitFuncs: Automatic Initialisation and Deinitialisation Functions
+ *  Copyright(C) 2014-2024 Karlson2k (Evgeny Grin)
  *
  *  This header is free software; you can redistribute it and / or
  *  modify it under the terms of the GNU Lesser General Public
@@ -18,14 +18,15 @@
  */
 
 /*
-   General usage is simple: include this header, declare or define two
-   functions with zero parameters (void) and any return type: one for
-   initialisation and one for deinitialisation, add
-   _SET_INIT_AND_DEINIT_FUNCS(FuncInitName, FuncDeInitName) to the code
+   General usage is simple: include this header, define two functions
+   with zero parameters (void) and any return type: one for initialisation and
+   one for deinitialisation, add
+   AIF_SET_INIT_AND_DEINIT_FUNCS(FuncInitName, FuncDeInitName) to the code,
    and functions will be automatically called during application startup
    and shutdown.
    This is useful for libraries as libraries don't have direct access
-   to main() functions.
+   to the main() function.
+
    Example:
    -------------------------------------------------
    #include <stdlib.h>
@@ -37,181 +38,275 @@
    void libInit(void)
    {
      someVar = 3;
-     somePtr = malloc(100);
+     // Calling other library functions could be unsafe the as the order
+     // of initialisation of libraries is not strictly defined
+     //somePtr = malloc(100);
    }
 
    void libDeinit(void)
    {
-     free(somePtr);
+     // Calling other library functions could be unsafe
+     //free(somePtr);
    }
 
-   _SET_INIT_AND_DEINIT_FUNCS(libInit,libDeinit);
+   AIF_SET_INIT_AND_DEINIT_FUNCS(libInit,libDeinit);
    -------------------------------------------------
 
-   If initialiser or deinitialiser function is not needed, just define
-   it as empty function.
+   If initialiser or deinitialiser function is not needed, just use
+   an empty function as a placeholder.
 
    This header should work with GCC, clang, MSVC (2010 or later) and
    SunPro / Sun Studio / Oracle Solaris Studio / Oracle Developer Studio
-   compiler.
+   compiler and other compatible compilers.
    Supported C and C++ languages; application, static and dynamic (DLL)
    libraries; non-optimized (Debug) and optimised (Release) compilation
    and linking.
 
-   For more information see header code and comments in code.
+   Besides the main macro mentioned above, the header defines other helper
+   macros prefixed with AIF_. These macros can be used directly, but they
+   are not designed to be defined externally.
+
+   The header behaviour could be adjusted by defining various AUTOINIT_FUNCS_*
+   macros before including the header.
+
+   For more information see the header code and comments in the code.
  */
-#ifndef AUTOINIT_FUNCS_INCLUDED
-#define AUTOINIT_FUNCS_INCLUDED 1
+#ifndef AIF_HEADER_INCLUDED
+#define AIF_HEADER_INCLUDED 1
 
 /**
-* Current version of the header in packed BCD form.
-* 0x01093001 = 1.9.30-1.
+* The header version number in packed BCD form.
+* (For example, version 1.9.30-1 would be 0x01093001)
 */
-#define AUTOINIT_FUNCS_VERSION 0x01001000
+#define AIF_VERSION 0x02000000
+
+/* Define AUTOINIT_FUNCS_NO_WARNINGS to disable all custom warnings
+   in this header */
+#ifdef AUTOINIT_FUNCS_NO_WARNINGS
+#  ifndef AUTOINIT_FUNCS_NO_WARNINGS_W32_ARCH
+#    define AUTOINIT_FUNCS_NO_WARNINGS_W32_ARCH 1
+#  endif
+#  ifndef AUTOINIT_FUNCS_NO_WARNINGS_SUNPRO_C
+#    define AUTOINIT_FUNCS_NO_WARNINGS_SUNPRO_C 1
+#  endif
+#endif
 
-#if defined(__GNUC__) || defined(__clang__)
-/* if possible - check for supported attribute */
+/* If possible - check for supported attributes */
 #ifdef __has_attribute
-#if ! __has_attribute (constructor) || ! __has_attribute (destructor)
-#define _GNUC_ATTR_CONSTR_NOT_SUPPORTED 1
-#endif /* !__has_attribute(constructor) || !__has_attribute(destructor) */
+#  if __has_attribute (constructor) && __has_attribute (destructor)
+#    define AIF_GNUC_ATTR_CONSTR_SUPPORTED 1
+#  else  /* ! __has_attribute (constructor) || ! __has_attribute (destructor) */
+#    define AIF_GNUC_ATTR_CONSTR_NOT_SUPPORTED 1
+#  endif /* ! __has_attribute (constructor) || ! __has_attribute (destructor) */
 #endif /* __has_attribute */
-#endif /* __GNUC__ */
+
+#if defined(__GNUC__) && __GNUC__ < 2 \
+  && ! defined(AIF_GNUC_ATTR_CONSTR_NOT_SUPPORTED) \
+  && ! defined(AIF_GNUC_ATTR_CONSTR_SUPPORTED)
+#  define AIF_GNUC_ATTR_CONSTR_NOT_SUPPORTED 1
+#endif
 
 /* "__has_attribute__ ((constructor))" is supported by GCC, clang and
    Sun/Oracle compiler starting from version 12.1. */
-#if ((defined(__GNUC__) || defined(__clang__)) && \
-  ! defined(_GNUC_ATTR_CONSTR_NOT_SUPPORTED)) || \
-  (defined(__SUNPRO_C) && __SUNPRO_C + 0 >= 0x5100)
-
-#define GNUC_SET_INIT_AND_DEINIT(FI,FD) \
-        void __attribute__ ((constructor)) _GNUC_init_helper_ ## FI (void);  \
-        void __attribute__ ((destructor)) _GNUC_deinit_helper_ ## FD (void); \
-        void __attribute__ ((constructor)) _GNUC_init_helper_ ## FI (void)   \
+#if (defined(AIF_GNUC_ATTR_CONSTR_SUPPORTED) \
+  || defined(__GNUC__) || defined(__clang__) \
+  || (defined(__SUNPRO_C) && __SUNPRO_C + 0 >= 0x5100)) && \
+  ! defined(AIF_GNUC_ATTR_CONSTR_NOT_SUPPORTED)
+
+#  define AIF_GNUC_SET_INIT_AND_DEINIT(FI,FD) \
+        void __attribute__ ((constructor)) AIF_GNUC_init_helper_ ## FI (void);  \
+        void __attribute__ ((destructor)) AIF_GNUC_deinit_helper_ ## FD (void); \
+        void __attribute__ ((constructor)) AIF_GNUC_init_helper_ ## FI (void)   \
         { (void) (FI) (); } \
-        void __attribute__ ((destructor)) _GNUC_deinit_helper_ ## FD (void)  \
+        void __attribute__ ((destructor)) AIF_GNUC_deinit_helper_ ## FD (void)  \
         { (void) (FD) (); } \
-        struct _GNUC_dummy_str_ ## FI {int i;}
+        struct AIF_GNUC_dummy_str_ ## FI {int i;}
 
-#define _SET_INIT_AND_DEINIT_FUNCS(FI,FD) GNUC_SET_INIT_AND_DEINIT (FI,FD)
-#define _AUTOINIT_FUNCS_ARE_SUPPORTED 1
-
-#elif defined(_MSC_FULL_VER) && _MSC_VER + 0 >= 1600
+#elif defined(_WIN32) && defined(_MSC_FULL_VER) && _MSC_VER + 0 >= 1600 && \
+  ! defined(__CYGWIN__)
 
 /* Make sure that your project/sources define:
    _LIB if building a static library (_LIB is ignored if _CONSOLE is defined);
    _USRDLL if building DLL-library;
    not defined both _LIB and _USRDLL if building an application */
 
-/* Define AUTOINIT_FUNCS_DECLARE_STATIC_REG if you need macro declaration
-   for registering static initialisation functions even if you building DLL */
-/* Define AUTOINIT_FUNCS_FORCE_STATIC_REG if you want to set main macro
-   _SET_INIT_AND_DEINIT_FUNCS to static version even if building a DLL */
-
 /* Stringify macros */
-#define _INSTRMACRO(a) #a
-#define _STRMACRO(a) _INSTRMACRO (a)
-
-#if ! defined(_USRDLL) || defined(AUTOINIT_FUNCS_DECLARE_STATIC_REG) \
-  || defined(AUTOINIT_FUNCS_FORCE_STATIC_REG)
-
-/* Use "C" linkage for variable to simplify variable decoration */
-#ifdef __cplusplus
-#define W32_INITVARDECL extern "C"
-#else
-#define W32_INITVARDECL extern
-#endif
-
-/* How variable is decorated by compiler */
-#if (defined(_WIN32) || defined(_WIN64)) \
+#  define AIF_INSTRMACRO(a) #a                /* Strigify helper */
+#  define AIF_STRMACRO(a) AIF_INSTRMACRO (a)  /* Expand and strigify */
+
+/* Concatenate macros */
+#  define AIF_INCONCAT(a,b) a ## b           /* Concatenate helper */
+#  define AIF_CONCAT(a,b) AIF_INCONCAT (a,b) /* Expand and concatenate */
+
+/* Use "C" linkage for variables to simplify symbols decoration */
+#  ifdef __cplusplus
+#    define AIF_W32_INITVARDECL extern "C"
+#    define AIF_W32_INITHELPERFUNCDECL static
+#  else
+#    define AIF_W32_INITVARDECL extern
+#    define AIF_W32_INITHELPERFUNCDECL static
+#  endif
+
+/* How variables are decorated by compiler */
+#  if (defined(_WIN32) || defined(_WIN64)) \
   && ! defined(_M_IX86) && ! defined(_X86_)
-#if ! defined(_M_X64) && ! defined(_M_AMD64) && ! defined(_x86_64_) \
+#    if ! defined(_M_X64) && ! defined(_M_AMD64) && ! defined(_x86_64_) \
   && ! defined(_M_ARM) && ! defined(_M_ARM64)
-#pragma message(__FILE__ "(" _STRMACRO(__LINE__) ") : warning AIFW001 : " \
-  "Untested architecture, linker may fail with unresolved symbol")
-#endif /* ! _M_X64 && ! _M_AMD64 && ! _x86_64_ && ! _M_ARM && ! _M_ARM64 */
-#define W32_VARDECORPREFIX
-#define W32_DECORVARNAME(v) v
-#define W32_VARDECORPREFIXSTR ""
-#elif defined(_WIN32) && (defined(_M_IX86) || defined(_X86_))
-#define W32_VARDECORPREFIX _
-#define W32_DECORVARNAME(v) _ ## v
-#define W32_VARDECORPREFIXSTR "_"
-#else
+#      ifndef AUTOINIT_FUNCS_NO_WARNINGS_W32_ARCH
+#pragma message(__FILE__ "(" AIF_STRMACRO(__LINE__) ") : warning AIFW001 : " \
+  "Untested architecture, linker may fail with unresolved symbols")
+#      endif /* ! AUTOINIT_FUNCS_NO_WARNINGS_W32_ARCH */
+#    endif /* ! _M_X64 && ! _M_AMD64 && ! _x86_64_ && ! _M_ARM && ! _M_ARM64 */
+#    define AIF_W32_VARDECORPREFIX
+#    define AIF_W32_DECORVARNAME(v) v
+#    define AIF_W32_VARDECORPREFIXSTR ""
+#  elif defined(_WIN32) && (defined(_M_IX86) || defined(_X86_))
+#    define AIF_W32_VARDECORPREFIX _
+#    define AIF_W32_DECORVARNAME(v) _ ## v
+#    define AIF_W32_VARDECORPREFIXSTR "_"
+#  else
 #error Do not know how to decorate symbols for this architecture
-#endif
+#  endif
+
 
 /* Internal variable prefix (can be any) */
-#define W32_INITHELPERVARNAME(f) _initHelperDummy_ ## f
-#define W32_INITHELPERVARNAMEDECORSTR(f) \
-        W32_VARDECORPREFIXSTR _STRMACRO (W32_INITHELPERVARNAME (f))
+#  define AIF_W32_INITHELPERVARNAME(f) _aif_init_ptr_ ## f
+#  define AIF_W32_INITHELPERVARNAMEDECORSTR(f) \
+        AIF_W32_VARDECORPREFIXSTR AIF_STRMACRO (AIF_W32_INITHELPERVARNAME (f))
 
-/* Declare section (segment), put variable pointing to init function to chosen segment,
-   force linker to always include variable to avoid omitting by optimiser */
-/* Initialisation function must be declared as
-   void __cdecl FuncName(void) */
-/* "extern" with initialisation value means that variable is declared AND defined. */
-#define W32_VFPTR_IN_SEG(S,F) \
-        __pragma(section (S,long,read)) \
-        __pragma (comment (linker, "/INCLUDE:" W32_INITHELPERVARNAMEDECORSTR (F))) \
-        W32_INITVARDECL __declspec(allocate (S))void \
-        (__cdecl * W32_INITHELPERVARNAME (F))(void) = &F
 
-/* Sections (segments) for pointers to initialisers/deinitialisers */
+/* Sections (segments) for pointers to initialisers */
 
 /* Semi-officially suggested section for early initialisers (called before
    C++ objects initialisers), "void" return type */
-#define W32_SEG_INIT_EARLY      ".CRT$XCT"
+#  define AIF_W32_SEG_STAT_INIT_EARLY1     ".CRT$XCT"
+/* Guessed section name for early initialisers (called before
+   C++ objects initialisers, after first initialisers), "void" return type */
+#  define AIF_W32_SEG_STAT_INIT_EARLY2     ".CRT$XCTa"
 /* Semi-officially suggested section for late initialisers (called after
    C++ objects initialisers), "void" return type */
-#define W32_SEG_INIT_LATE       ".CRT$XCV"
+#  define AIF_W32_SEG_STAT_INIT_LATE       ".CRT$XCV"
 
-/* Unsafe sections (segments) for pointers to initialisers/deinitialisers */
+/* Unsafe sections (segments) for pointers to initialisers */
 
 /* C++ lib initialisers, "void" return type (reserved by the system!) */
-#define W32_SEG_INIT_CXX_LIB    ".CRT$XCL"
+#  define AIF_W32_SEG_STAT_INIT_CXX_LIB    ".CRT$XCL"
 /* C++ user initialisers, "void" return type (reserved by the system!) */
-#define W32_SEG_INIT_CXX_USER   ".CRT$XCU"
+#  define AIF_W32_SEG_STAT_INIT_CXX_USER   ".CRT$XCU"
 
-
-/* Declare section (segment), put variable pointing to init function to chosen segment,
-   force linker to always include variable to avoid omitting by optimiser */
-/* Initialisation function must be declared as
-   int __cdecl FuncName(void) */
-/* Startup process is aborted if initialiser returns non-zero */
-/* "extern" with initialisation value means that variable is declared AND defined. */
-#define W32_IFPTR_IN_SEG(S,F) \
-        __pragma(section (S,long,read)) \
-        __pragma (comment (linker, "/INCLUDE:" W32_INITHELPERVARNAMEDECORSTR (F))) \
-        W32_INITVARDECL __declspec(allocate (S))int \
-        (__cdecl * W32_INITHELPERVARNAME (F))(void) = &F
+/* Declare section (segment), put variable pointing to init function to
+   chosen segment, force linker to always include variable to avoid omitting
+   by optimiser */
+/* These initialisation function must be declared as
+   void __cdecl FuncName(void) */
+/* Note: "extern" with initialisation value means that variable is declared AND
+   defined. */
+#  define AIF_W32_INIT_VFPTR_IN_SEG(S,F)              \
+        __pragma(section(S, long, read))               \
+        __pragma(comment(linker, "/INCLUDE:"            \
+        AIF_W32_INITHELPERVARNAMEDECORSTR (F)))          \
+        AIF_W32_INITVARDECL __declspec(allocate (S)) void \
+        (__cdecl * AIF_W32_INITHELPERVARNAME (F))(void) = &F
 
 /* Unsafe sections (segments) for pointers to initialisers with
    "int" return type */
 
 /* C lib initialisers, "int" return type (reserved by the system!).
    These initialisers are called before others. */
-#define W32_SEG_INIT_C_LIB      ".CRT$XIL"
+#  define AIF_W32_SEG_STAT_INIT_C_LIB      ".CRT$XIL"
 /* C user initialisers, "int" return type (reserved by the system!).
    These initialisers are called before others. */
-#define W32_SEG_INIT_C_USER     ".CRT$XIU"
+#  define AIF_W32_SEG_STAT_INIT_C_USER     ".CRT$XIU"
+
+/* Declare section (segment), put variable pointing to init function to
+   chosen segment, force linker to always include variable to avoid omitting
+   by optimiser */
+/* These initialisation function must be declared as
+   int __cdecl FuncName(void) */
+/* Startup process is aborted if initialiser returns non-zero */
+/* Note: "extern" with initialisation value means that variable is declared AND
+   defined. */
+#  define AIF_W32_INIT_IFPTR_IN_SEG(S,F)                 \
+        __pragma(section(S, long, read))                  \
+        __pragma(comment(linker,                           \
+        "/INCLUDE:" AIF_W32_INITHELPERVARNAMEDECORSTR (F))) \
+        AIF_W32_INITVARDECL __declspec(allocate (S)) int     \
+        (__cdecl * AIF_W32_INITHELPERVARNAME (F))(void) = &F
+
+/* Not recommended / unsafe */
+/* "lib" initialisers are called before "user" initialisers */
+/* "C" initialisers are called before "C++" initialisers */
+#  define AIF_W32_REG_STAT_INIT_C_USER(F)   \
+        AIF_W32_FPTR_IN_SEG (AIF_W32_SEG_STAT_INIT_C_USER,F)
+#  define AIF_W32_REG_STAT_INIT_C_LIB(F)    \
+        AIF_W32_FPTR_IN_SEG (AIF_W32_SEG_STAT_INIT_C_LIB,F)
+#  define AIF_W32_REG_STAT_INIT_CXX_USER(F) \
+        AIF_W32_FPTR_IN_SEG (AIF_W32_SEG_STAT_INIT_CXX_USER,F)
+#  define AIF_W32_REG_STAT_INIT_CXX_LIB(F)  \
+        AIF_W32_FPTR_IN_SEG (AIF_W32_SEG_STAT_INIT_CXX_LIB,F)
 
+/* Declare macros for different initialisers sections */
 
-/* Declare macro for different initialisers sections */
 /* Macro can be used several times to register several initialisers */
 /* Once function is registered as initialiser, it will be called automatically
-   during application startup */
-#define W32_REG_INIT_EARLY(F) W32_VFPTR_IN_SEG (W32_SEG_INIT_EARLY,F)
-#define W32_REG_INIT_LATE(F)  W32_VFPTR_IN_SEG (W32_SEG_INIT_LATE,F)
+   during application startup or library loading */
+#  define AIF_W32_REG_STAT_INIT_EARLY1(F) \
+        AIF_W32_INIT_VFPTR_IN_SEG (AIF_W32_SEG_STAT_INIT_EARLY1,F)
+#  define AIF_W32_REG_STAT_INIT_EARLY2(F) \
+        AIF_W32_INIT_VFPTR_IN_SEG (AIF_W32_SEG_STAT_INIT_EARLY2,F)
+#  define AIF_W32_REG_STAT_INIT_LATE(F)   \
+        AIF_W32_INIT_VFPTR_IN_SEG (AIF_W32_SEG_STAT_INIT_LATE,F)
+
+#  ifndef _DLL
+/* Sections (segments) for pointers to deinitialisers */
+
+/* These section are not used when common rutime (CRT, the C library) is used
+   as DLL. In such case only sections in CRT DLL are used for deinitialisation
+   pointers. */
+
+/* The section name based on semi-documented deinitialisation procedure,
+   functions called before first C deinitialisers, "void" return type */
+#    define AIF_W32_SEG_STAT_DEINIT_FIRST      ".CRT$XPAa"
+/* The section name based on semi-documented deinitialisation procedure,
+   functions called after AIF_W32_SEG_STAT_DEINIT_FIRST deinitialisers and
+   before first C deinitialisers, "void" return type */
+#    define AIF_W32_SEG_STAT_DEINIT_SECOND     ".CRT$XPAb"
+/* The section name based on semi-documented deinitialisation procedure,
+   functions called after AIF_W32_SEG_STAT_DEINIT_SECOND deinitialisers and
+   before first C deinitialisers, "void" return type */
+#    define AIF_W32_SEG_STAT_DEINIT_THIRD      ".CRT$XPAc"
 
+/* Internal variable prefix (can be any) */
+#    define AIF_W32_DEINITHELPERVARNAME(f) _aif_deinit_ptr_ ## f
+#    define AIF_W32_DEINITHELPERVARNAMEDECORSTR(f) \
+        AIF_W32_VARDECORPREFIXSTR AIF_STRMACRO (AIF_W32_DEINITHELPERVARNAME (f))
+
+/* The macro to declare section (segment), put variable pointing to deinit
+   function to chosen segment, force linker to always include variable to
+   avoid omitting by optimiser */
+/* These deinitialisation function must be declared as
+   void __cdecl FuncName(void) */
+/* Note: "extern" with initialisation value means that variable is declared AND
+   defined. */
+#    define AIF_W32_DEINIT_VFPTR_IN_SEG(S,F)          \
+        __pragma(section(S, long, read))               \
+        __pragma(comment(linker, "/INCLUDE:"            \
+        AIF_W32_DEINITHELPERVARNAMEDECORSTR (F)))        \
+        AIF_W32_INITVARDECL __declspec(allocate (S)) void \
+        (__cdecl * AIF_W32_DEINITHELPERVARNAME (F))(void) = &F
+
+/* Declare macros for different deinitialisers sections */
+/* Macro can be used several times to register several deinitialisers */
+/* Once function is registered as initialiser, it will be called automatically
+   during application shutdown or library unloading */
+#    define AIF_W32_REG_STAT_DEINIT_EARLY(F)  \
+        AIF_W32_DEINIT_VFPTR_IN_SEG (AIF_W32_SEG_STAT_DEINIT_FIRST,F)
+#    define AIF_W32_REG_STAT_DEINIT_LATE1(F)  \
+        AIF_W32_DEINIT_VFPTR_IN_SEG (AIF_W32_SEG_STAT_DEINIT_SECOND,F)
+#    define AIF_W32_REG_STAT_DEINIT_LATE2(F)  \
+        AIF_W32_DEINIT_VFPTR_IN_SEG (AIF_W32_SEG_STAT_DEINIT_THIRD,F)
 
-/* Not recommended / unsafe */
-/* "lib" initialisers are called before "user" initialisers */
-/* "C" initialisers are called before "C++" initialisers */
-#define W32_REG_INIT_C_USER(F)   W32_FPTR_IN_SEG (W32_SEG_INIT_C_USER,F)
-#define W32_REG_INIT_C_LIB(F)    W32_FPTR_IN_SEG (W32_SEG_INIT_C_LIB,F)
-#define W32_REG_INIT_CXX_USER(F) W32_FPTR_IN_SEG (W32_SEG_INIT_CXX_USER,F)
-#define W32_REG_INIT_CXX_LIB(F)  W32_FPTR_IN_SEG (W32_SEG_INIT_CXX_LIB,F)
+#  endif /* ! _DLL */
 
 /* Choose main register macro based on language and program type */
 /* Assuming that _LIB or _USRDLL is defined for static or DLL-library */
@@ -225,79 +320,219 @@
 /* By default C++ static or DLL-library code and any C code and will be
    registered as early initialiser, while C++ non-library code will be
    registered as late initialiser */
-#if (! defined(__cplusplus) || \
-  ((defined(_LIB) && ! defined(_CONSOLE)) || defined(_USRDLL)) || \
+#  if (! defined(__cplusplus) || \
+  defined(_LIB) || defined(_USRDLL) || \
   defined(AUTOINIT_FUNCS_FORCE_EARLY_INIT)) && \
   ! defined(AUTOINIT_FUNCS_FORCE_LATE_INIT)
-#define W32_REGISTER_INIT(F) W32_REG_INIT_EARLY (F)
-#else
-#define W32_REGISTER_INIT(F) W32_REG_INIT_LATE (F)
-#endif
+/* Use early initialiser and late deinitialiser */
+#    if defined(_LIB) || defined(_USRDLL)
+/* Static or DLL library */
+#      define AIF_W32_REGISTER_STAT_INIT(F)  AIF_W32_REG_STAT_INIT_EARLY1 (F)
+#      ifdef AIF_W32_REG_STAT_DEINIT_LATE2
+#        define AIF_W32_REGISTER_STAT_DEINIT(F) \
+        AIF_W32_REG_STAT_DEINIT_LATE2 (F)
+#      endif
+#    else
+/* Application code */
+#      define AIF_W32_REGISTER_STAT_INIT(F)  AIF_W32_REG_STAT_INIT_EARLY2 (F)
+#      ifdef AIF_W32_REG_STAT_DEINIT_LATE1
+#        define AIF_W32_REGISTER_STAT_DEINIT(F) \
+        AIF_W32_REG_STAT_DEINIT_LATE1 (F)
+#      endif
+#    endif
+#  else
+/* Use late initialiser and early deinitialiser */
+#    define AIF_W32_REGISTER_STAT_INIT(F)     AIF_W32_REG_STAT_INIT_LATE (F)
+#    ifdef AIF_W32_REG_STAT_DEINIT_EARLY
+#      define AIF_W32_REGISTER_STAT_DEINIT(F) \
+        AIF_W32_REG_STAT_DEINIT_EARLY (F)
+#    endif
+#  endif
+
+
+/* Static deinit registration on W32 could be risky as it works only
+   if CRT is used as static lib (not as DLL) and relies on correct
+   definition of "_DLL" macro by build system. */
+/* If "_DLL" macro is correctly defined, static deinitialiser registration
+   can be enabled by defining AUTOINIT_FUNCS_ALLOW_W32_STAT_DEINIT macro.
+   If it is used, it can save from including an extra header. */
+#  if defined(AIF_W32_REGISTER_STAT_DEINIT) \
+  && defined(AUTOINIT_FUNCS_ALLOW_W32_STAT_DEINIT)
+#    define AIF_W32_SET_STAT_INIT_AND_DEINIT(FI,FD)        \
+        AIF_W32_INITHELPERFUNCDECL void                     \
+        __cdecl AIF_W32_stat_init_helper_ ## FI (void);      \
+        AIF_W32_INITHELPERFUNCDECL void                       \
+        __cdecl AIF_W32_stat_deinit_helper_ ## FD (void);      \
+        AIF_W32_INITHELPERFUNCDECL void                         \
+        __cdecl AIF_W32_stat_init_helper_ ## FI (void)           \
+        { (void) (FI) (); }                                       \
+        AIF_W32_INITHELPERFUNCDECL void                            \
+        __cdecl AIF_W32_stat_deinit_helper_ ## FD (void)            \
+        { (void) (FD) (); }                                          \
+        AIF_W32_REGISTER_STAT_INIT (AIF_W32_stat_init_helper_ ## FI); \
+        AIF_W32_REGISTER_STAT_DEINIT (AIF_W32_stat_deinit_helper_ ## FD)
+#  else
+
+/* Note: 'atexit()' is just a wrapper for '_onexit()' on W32 */
+#    include <stdlib.h> /* required for _onexit() */
+
+#    define AIF_W32_SET_STAT_INIT_AND_DEINIT(FI,FD)   \
+        AIF_W32_INITHELPERFUNCDECL void                \
+        __cdecl AIF_W32_stat_init_helper_ ## FI (void); \
+        AIF_W32_INITHELPERFUNCDECL int                   \
+        __cdecl AIF_W32_stat_deinit_helper_ ## FD (void); \
+        AIF_W32_INITHELPERFUNCDECL void                    \
+        __cdecl AIF_W32_stat_init_helper_ ## FI (void)      \
+        { (void) (FI) ();                                    \
+          _onexit (&AIF_W32_stat_deinit_helper_ ## FD); }     \
+        AIF_W32_INITHELPERFUNCDECL int                         \
+        __cdecl AIF_W32_stat_deinit_helper_ ## FD (void)        \
+        { (void) (FD) (); return ! 0; }                          \
+        AIF_W32_REGISTER_STAT_INIT (AIF_W32_stat_init_helper_ ## FI)
+#  endif
+
+#endif /* _WIN32 && _MSC_VER + 0 >= 1600 && ! _GNUC_ATTR_CONSTR_SUPPORTED */
+
+
+#if defined(_WIN32) && ! defined(__CYGWIN__) && \
+  (defined(_USRDLL) || defined(DLL_EXPORT))
+
+#  if defined(__MINGW32__) || defined(__MINGW64__)
+/* The minimal portable set of the headers to pull in the definitions of "BOOL",
+   "WINAPI", "HINSTANCE", "DWORD" and "LPVOID" */
+/* Thi minimal set does not work with MS headers as they depend on some vital
+   macros defined only in the "windows.h" header only */
+#    include <windef.h>
+#    include <winnt.h>
+#  else
+#    ifndef WIN32_LEAN_AND_MEAN
+#      define WIN32_LEAN_AND_MEAN 1
+#    endif
+#    include <windows.h>
+#  endif
+
+#  ifdef DLL_PROCESS_ATTACH
+#    define AIF_W32_DLL_PROCESS_ATTACH  DLL_PROCESS_ATTACH
+#  else
+#    define AIF_W32_DLL_PROCESS_ATTACH  1
+#  endif
+#  ifdef DLL_PROCESS_DETACH
+#    define AIF_W32_DLL_PROCESS_DETACH  DLL_PROCESS_DETACH
+#  else
+#    define AIF_W32_DLL_PROCESS_DETACH  0
+#  endif
+
+/* When process is terminating, DLL should not perform a cleanup, leaving
+   allocated resources to be cleaned by the system. This is because some
+   threads may be explicitly terminated without proper cleanup, and some
+   system resources, including process heap, could be in inconsistent state.
+   Define AUTOINIT_FUNCS_USE_UNSAFE_DLL_DEINIT to enable calling of deinit
+   function such situations.
+   Note: if AUTOINIT_FUNCS_USE_UNSAFE_DLL_DEINIT is defined and the DLL is
+   delay-loaded, then both the initialiser and the deinitialiser could be
+   called late (or last), breaking requirement for calling deinitialisers in
+   reverse order of initialisers. */
+#  ifndef AUTOINIT_FUNCS_USE_UNSAFE_DLL_DEINIT
+#    define AIF_W32_IS_DLL_DEINIT_SAFE(pReserved)       (NULL == (pReserved))
+#  else
+#    define AIF_W32_IS_DLL_DEINIT_SAFE(pReserved)       TRUE
+#  endif
+
+/* If DllMain is already present in user's code,
+   define AUTOINIT_FUNCS_CALL_USR_DLLMAIN and
+   rename user's DllMain to usr_DllMain.
+   The usr_DllMain() must be declared (or full defined) before using the macro
+   for setting initialiser and deinitialiser. Alternatively, if usr_DllMain()
+   is another file (or after the macro), define macro
+   AUTOINIT_FUNCS_DECLARE_USR_DLLMAIN to enable automatic declaration of this
+   function.
+   Define AUTOINIT_FUNCS_USR_DLLMAIN_NAME to user function name if usr_DllMain
+   is not suitable. */
+#  ifndef AUTOINIT_FUNCS_CALL_USR_DLLMAIN
+#    define AIF_W32_CALL_USER_DLLMAIN(h,r,p)    TRUE
+#  else  /* AUTOINIT_FUNCS_CALL_USR_DLLMAIN */
+#    ifndef AUTOINIT_FUNCS_USR_DLLMAIN_NAME
+#      define AIF_W32_USR_DLLMAIN_NAME usr_DllMain
+#    else
+#      define AIF_W32_USR_DLLMAIN_NAME AUTOINIT_FUNCS_USR_DLLMAIN_NAME
+#    endif
+#    define AIF_W32_CALL_USER_DLLMAIN(h,r,p)    \
+        AIF_W32_USR_DLLMAIN_NAME ((h),(r),(p))
+#    ifdef AUTOINIT_FUNCS_DECLARE_USR_DLLMAIN
+#      define AIF_DECL_USR_DLLMAIN \
+        BOOL WINAPI AIF_W32_USR_DLLMAIN_NAME (HINSTANCE hinst, DWORD reason, \
+                                              LPVOID pReserved);
+#    endif
+#  endif /* AUTOINIT_FUNCS_CALL_USR_DLLMAIN */
+
+#  ifndef AIF_DECL_USR_DLLMAIN
+#    define AIF_DECL_USR_DLLMAIN /* empty */
+#  endif
+
+#  define AIF_W32_SET_DLL_INIT_AND_DEINIT(FI,FD) \
+        BOOL WINAPI DllMain (HINSTANCE hinst, DWORD reason, LPVOID pReserved); \
+        AIF_DECL_USR_DLLMAIN                                                   \
+        BOOL WINAPI DllMain (HINSTANCE hinst, DWORD reason, LPVOID pReserved)  \
+        { BOOL aif_ret; (void) hinst;                                          \
+          if (AIF_W32_DLL_PROCESS_ATTACH == reason) {                          \
+            (void) (FI) ();                                                    \
+            aif_ret = AIF_W32_CALL_USER_DLLMAIN (hinst, reason, pReserved);    \
+            if (! aif_ret && NULL != pReserved) { (void) (FD) (); } }          \
+          else if (AIF_W32_DLL_PROCESS_DETACH == reason) {                     \
+            aif_ret = AIF_W32_CALL_USER_DLLMAIN (hinst, reason, pReserved);    \
+            if (AIF_W32_IS_DLL_DEINIT_SAFE (pReserved)) { (void) (FD) (); } }  \
+          else aif_ret = AIF_W32_CALL_USER_DLLMAIN (hinst, reason, pReserved); \
+          return aif_ret;                                                      \
+        } struct AIF_W32_dummy_strc_ ## FI {int i;}
+#endif /* _WIN32 && ! __CYGWIN__ && (_USRDLL || DLL_EXPORT) */
+
 
-#endif /* ! _USRDLL || ! AUTOINIT_FUNCS_DECLARE_STATIC_REG
-          || AUTOINIT_FUNCS_FORCE_STATIC_REG */
+/* Define AUTOINIT_FUNCS_FORCE_STATIC_REG if you want to set main macro
+   AIF_SET_INIT_AND_DEINIT_FUNCS to static version even if building a DLL.
+   Static registration works for DLL too, but less precise and flexible. */
 
+#if defined(AIF_W32_SET_DLL_INIT_AND_DEINIT)      \
+  && ! (defined(AUTOINIT_FUNCS_PREFER_STATIC_REG) \
+  && (defined(AIF_GNUC_SET_INIT_AND_DEINIT)       \
+  || defined(AIF_W32_SET_STAT_INIT_AND_DEINIT)))
 
-#if ! defined(_USRDLL) || defined(AUTOINIT_FUNCS_FORCE_STATIC_REG)
+#  define AIF_SET_INIT_AND_DEINIT_FUNCS(FI,FD) \
+        AIF_W32_SET_DLL_INIT_AND_DEINIT (FI,FD)
+/* Indicate that automatic initialisers/deinitialisers are supported */
+#  define AIF_AUTOINIT_FUNCS_ARE_SUPPORTED 1
 
-#include <stdlib.h> /* required for atexit() */
+#elif defined(AIF_GNUC_SET_INIT_AND_DEINIT)
 
-#define W32_SET_INIT_AND_DEINIT(FI,FD) \
-        void __cdecl _W32_init_helper_ ## FI (void);    \
-        void __cdecl _W32_deinit_helper_ ## FD (void); \
-        void __cdecl _W32_init_helper_ ## FI (void)     \
-        { (void) (FI) (); atexit (_W32_deinit_helper_ ## FD); } \
-        void __cdecl _W32_deinit_helper_ ## FD (void)  \
-        { (void) (FD) (); } \
-        W32_REGISTER_INIT (_W32_init_helper_ ## FI)
-#else  /* _USRDLL */
-
-#ifndef WIN32_LEAN_AND_MEAN
-#define WIN32_LEAN_AND_MEAN 1
-#endif /* WIN32_LEAN_AND_MEAN */
-
-#include <Windows.h> /* Required for DllMain */
-
-/* If DllMain is already present in code, define AUTOINIT_FUNCS_CALL_USR_DLLMAIN
-   and rename DllMain to usr_DllMain */
-#ifndef AUTOINIT_FUNCS_CALL_USR_DLLMAIN
-#define W32_SET_INIT_AND_DEINIT(FI,FD) \
-        BOOL WINAPI DllMain (HINSTANCE hinst,DWORD reason,LPVOID unused); \
-        BOOL WINAPI DllMain (HINSTANCE hinst,DWORD reason,LPVOID unused)  \
-        { (void) hinst; (void) unused; \
-          if (DLL_PROCESS_ATTACH==reason) {(void) (FI) ();} \
-          else if (DLL_PROCESS_DETACH==reason) {(void) (FD) ();} \
-          return TRUE; \
-        } struct _W32_dummy_strc_ ## FI {int i;}
-#else  /* AUTOINIT_FUNCS_CALL_USR_DLLMAIN */
-#define W32_SET_INIT_AND_DEINIT(FI,FD) \
-        BOOL WINAPI usr_DllMain (HINSTANCE hinst,DWORD reason,LPVOID unused); \
-        BOOL WINAPI DllMain (HINSTANCE hinst,DWORD reason,LPVOID unused); \
-        BOOL WINAPI DllMain (HINSTANCE hinst,DWORD reason,LPVOID unused)  \
-        { if (DLL_PROCESS_ATTACH==reason) {(void) (FI) ();} \
-          else if (DLL_PROCESS_DETACH==reason) {(void) (FD) ();} \
-          return usr_DllMain (hinst,reason,unused); \
-        } struct _W32_dummy_strc_ ## FI {int i;}
-#endif /* AUTOINIT_FUNCS_CALL_USR_DLLMAIN */
-#endif /* _USRDLL */
-
-#define _SET_INIT_AND_DEINIT_FUNCS(FI,FD) W32_SET_INIT_AND_DEINIT (FI,FD)
+#  define AIF_SET_INIT_AND_DEINIT_FUNCS(FI,FD) \
+        AIF_GNUC_SET_INIT_AND_DEINIT (FI,FD)
 /* Indicate that automatic initialisers/deinitialisers are supported */
-#define _AUTOINIT_FUNCS_ARE_SUPPORTED 1
+#  define AIF_AUTOINIT_FUNCS_ARE_SUPPORTED 1
 
-#else  /* !__GNUC__ && !_MSC_FULL_VER */
+#elif defined(AIF_W32_SET_STAT_INIT_AND_DEINIT)
 
-/* Define EMIT_ERROR_IF_AUTOINIT_FUNCS_ARE_NOT_SUPPORTED before inclusion of header to
-   abort compilation if automatic initialisers/deinitialisers are not supported */
-#ifdef EMIT_ERROR_IF_AUTOINIT_FUNCS_ARE_NOT_SUPPORTED
-#error \
-  Compiler/platform does not support automatic calls of user-defined initializer and deinitializer
-#endif /* EMIT_ERROR_IF_AUTOINIT_FUNCS_ARE_NOT_SUPPORTED */
+#  define AIF_SET_INIT_AND_DEINIT_FUNCS(FI,FD) \
+        AIF_W32_SET_STAT_INIT_AND_DEINIT (FI,FD)
+/* Indicate that automatic initialisers/deinitialisers are supported */
+#  define AIF_AUTOINIT_FUNCS_ARE_SUPPORTED 1
+
+#else
 
-/* Do nothing */
-#define _SET_INIT_AND_DEINIT_FUNCS(FI,FD)
+/* Define AUTOINIT_FUNCS_EMIT_ERROR_IF_NOT_SUPPORTED before inclusion of
+   this header to abort compilation if automatic initialisers/deinitialisers
+   are not supported */
+#  ifdef AUTOINIT_FUNCS_EMIT_ERROR_IF_NOT_SUPPORTED
+#error User-defined initialiser and deinitialiser functions are not supported
+#  endif /* AUTOINIT_FUNCS_EMIT_ERROR_IF_NOT_SUPPORTED */
+
+#  if defined(__SUNPRO_C) && ! defined(AUTOINIT_FUNCS_NO_WARNINGS_SUNPRO_C)
+#warning The compiler supports "#pragma init(func1)" and "#pragma fini(func2)"
+#warning Use "pragma" to set initialiser and deinitialiser functions
+#  endif
+
+/* "Not supported" implementation */
+#  define AIF_SET_INIT_AND_DEINIT_FUNCS(FI,FD) /* No-op */
 /* Indicate that automatic initialisers/deinitialisers are not supported */
-#define _AUTOINIT_FUNCS_ARE_NOT_SUPPORTED 1
+#  define AIF_AUTOINIT_FUNCS_ARE_NOT_SUPPORTED 1
 
-#endif /* !__GNUC__ && !_MSC_FULL_VER */
-#endif /* !AUTOINIT_FUNCS_INCLUDED */
+#endif
+#endif /* !AIF_HEADER_INCLUDED */