Showing posts with label GCC. Show all posts
Showing posts with label GCC. Show all posts

Friday, June 12, 2020

STLSoft 1.9.133 released

Available at GitHub.

Added support for the ARM architecture when doing Android NDK GCC compilation (kindly provided by christopherplawrence)

Remember: STLSoft-1.10 is the latest and preferred.


Tuesday, October 15, 2019

COM VARIANT nameless union discrimination

Aficionados of COM may remember - because it's been a while, hey? - that the VARIANT structure is a composite of nested union and struct aggregates, as in:

typedef struct tagVARIANT { union { struct { VARTYPE vt; WORD wReserved1; WORD wReserved2; WORD wReserved3; union { LONGLONG llVal; LONG lVal; BYTE bVal; SHORT iVal; FLOAT fltVal;
. . . // and lots more members USHORT *puiVal; ULONG *pulVal; ULONGLONG *pullVal; INT *pintVal; UINT *puintVal; struct { PVOID pvRecord; IRecordInfo *pRecInfo; } __VARIANT_NAME_4; } __VARIANT_NAME_3; } __VARIANT_NAME_2; DECIMAL decVal; } __VARIANT_NAME_1; } VARIANT;

(The full definition is given in https://docs.microsoft.com/en-us/windows/win32/api/oaidl/ns-oaidl-variant.)

The relevant aspect(s) for this post is the apparent use of member names for the anonymous struct/union aggregates - __VARIANT_NAME_1, __VARIANT_NAME_2, etc. They are present because anonymous aggregates are not standard C++ and, prior to C11, were not part of standard C, even though they are commonly supported on many compilers: on supporting compilers, the apparent names are #defined to nothing; on non-supporting compilers they are #defined to, respectively, n1, n2, n3, brecVal, as in:


#if ... some condition ...
# define __VARIANT_NAME_1 n1
# define __VARIANT_NAME_2 n2
# define __VARIANT_NAME_3 n3
# define __VARIANT_NAME_4 brecVal
#else
# define __VARIANT_NAME_1
# define __VARIANT_NAME_2
# define __VARIANT_NAME_3
# define __VARIANT_NAME_4
#endif

This is all fine and well, except when you're writing COM code to work in C++ as well as in C, as is done for various components within the COMSTL project, as in the comstl_C_DECIMAL_compare() function (in comstl/util/DECIMAL_functions.h):

int
comstl_C_DECIMAL_compare(
    DECIMAL const* lhs
,   DECIMAL const* rhs
) /* noexcept */
{
  . . .

    COMSTL_ACCESS_VARIANT_vt_BYPTR(&vdecL) = VT_DECIMAL;
    COMSTL_ACCESS_VARIANT_decVal_BYPTR(&vdecL) = *lhs;

  . . .
}


In order to make it compile for both C and C++, the function is implemented in terms of COMSTL macros such as COMSTL_ACCESS_VARIANT_vt_BYREF(), which is defined as:

#if defined(COMSTL_VARIANT_UNION_ARMS_HAVE_NAMES_)

# define COMSTL_ACCESS_VARIANT_vt_BYPTR(pvar) \
           (pvar)->__VARIANT_NAME_1.__VARIANT_NAME_2.vt
#else

# define COMSTL_ACCESS_VARIANT_vt_BYPTR(pvar) (pvar)->vt
#endif

If the (internal / undocumented) COMSTL_VARIANT_UNION_ARMS_HAVE_NAMES_ macro is defined, then COMSTL_ACCESS_VARIANT_vt_BYPTR() obtains the vt member via the full retinue of named members; if not, then it obtains vt directly.

So, the question is, how is the COMSTL_VARIANT_UNION_ARMS_HAVE_NAMES_ macro discriminated?

It is defined in comstl/comstl.h, and up to STLSoft 1.10.1 beta-16, it was done as:

#if !defined(COMSTL_ASSUME_VARIANT_UNION_FORCE_ARMS_HAVE_NAMES) && \
    defined(_FORCENAMELESSUNION) && \
    !defined(NONAMELESSUNION)

# define COMSTL_ASSUME_VARIANT_UNION_FORCE_ARMS_HAVE_NAMES
#endif

#if defined(COMSTL_ASSUME_VARIANT_UNION_FORCE_ARMS_HAVE_NAMES)

# define COMSTL_VARIANT_UNION_ARMS_HAVE_NAMES_
#elif defined(STLSOFT_COMPILER_IS_GCC)

   /* GCC has different definitions to the other compilers, so have to treat
    * differently
    */
# if defined(NONAMELESSUNION)

#  define COMSTL_VARIANT_UNION_ARMS_HAVE_NAMES_
# endif /* NONAMELESSUNION */
#else /* ? compiler */

   /* Other compilers use the MS headers, which test against __STDC__,
    * _FORCENAMELESSUNION and NONAMELESSUNION
    */
# if (  __STDC__ && \
        !defined(_FORCENAMELESSUNION)) || \
     defined(NONAMELESSUNION)

#  define COMSTL_VARIANT_UNION_ARMS_HAVE_NAMES_
# endif /* (  __STDC__ && !_FORCENAMELESSUNION) || NONAMELESSUNION */
#endif /* compiler */

However, when compiling for MinGW GCC 8.1 today, this no longer suffices.

Searching the web for _FORCENAMELESSUNION and NONAMELESSUNION yielded little, so I did a thorough search of the implementation headers for a bunch of compilers and the Windows SDK, in order to get a (more) complete and up-to-date definition, arriving at the following - hopefully final - definition, which will appear in STLSoft 1.10.1 beta-17:

#if 1 && \
    !defined(COMSTL_ASSUME_VARIANT_UNION_FORCE_ARMS_HAVE_NAMES) && \
    !defined(_FORCENAMELESSUNION) && \
    defined(NONAMELESSUNION) && \
    1

# define COMSTL_ASSUME_VARIANT_UNION_FORCE_ARMS_HAVE_NAMES
#endif


#if 0
#elif defined(_FORCENAMELESSUNION)

#elif defined(COMSTL_ASSUME_VARIANT_UNION_FORCE_ARMS_HAVE_NAMES)

# define COMSTL_VARIANT_UNION_ARMS_HAVE_NAMES_
#else

 /* The observed extant discriminations are:
  *
  * 1 (VC++ 6):
  *
  *  (__STDC__ && !defined(_FORCENAMELESSUNION)) || defined(NONAMELESSUNION)
  *
  * 2 (*):
  *
  *  (__STDC__ && !defined(_FORCENAMELESSUNION)) || defined(NONAMELESSUNION) || (!defined(_MSC_EXTENSIONS) && !defined(_FORCENAMELESSUNION))
  *
  * 3 (MinGW GCC 4.9):
  *
  *  NONAMELESSUNION
  *  
  * 4 (MinGW GCC 8.1):
  *
  *  (__STDC__ && !defined(__cplusplus) && !defined(_FORCENAMELESSUNION)) || defined(NONAMELESSUNION) || (defined (_MSC_VER) && !defined(_MSC_EXTENSIONS) && !defined(_FORCENAMELESSUNION))
  *
  * which may be better understood as:
  *
  * 1 (VC++ 6):

    0 ||

    defined(NONAMELESSUNION) ||

    (  __STDC__ && \
        !defined(_FORCENAMELESSUNION)) ||
    
    0
  *
  * 2 (*):

    0 ||

    defined(NONAMELESSUNION) ||

    (   __STDC__ &&
        !defined(_FORCENAMELESSUNION)) ||

    (   !defined(_MSC_EXTENSIONS) &&
        !defined(_FORCENAMELESSUNION)) ||

    0
  *
  * 3 (MinGW GCC 4.9):

    0 ||

    defined(NONAMELESSUNION) ||

    0

  * 4 (MinGW GCC 8.1):


    0 ||

    defined(NONAMELESSUNION) ||

    (   __STDC__ &&
        !defined(__cplusplus) &&
        !defined(_FORCENAMELESSUNION)) ||

    (   defined (_MSC_VER) &&
        !defined(_MSC_EXTENSIONS) &&
        !defined(_FORCENAMELESSUNION)) ||

    0
  */

# if 0
# elif defined(NONAMELESSUNION)

#  define COMSTL_VARIANT_UNION_ARMS_HAVE_NAMES_
# elif defined(STLSOFT_COMPILER_IS_GCC)

#  if STLSOFT_GCC_VER >= 80000 /* NOTE: this number may be wrong - too large, but still old way with 4.9 */

#   if 0
#   elif __STDC__ && \
         !defined(__cplusplus) && \
         !defined(_FORCENAMELESSUNION)

#    define COMSTL_VARIANT_UNION_ARMS_HAVE_NAMES_
#   elif defined(_MSC_VER) && \
         !defined(_MSC_EXTENSIONS) && \
         !defined(_FORCENAMELESSUNION)

#    define COMSTL_VARIANT_UNION_ARMS_HAVE_NAMES_
#   endif /* ? GCC version */
#  endif
# elif __STDC__ && \
       defined(_FORCENAMELESSUNION)

#  define COMSTL_VARIANT_UNION_ARMS_HAVE_NAMES_
# elif !defined(_MSC_EXTENSIONS) && \
       !defined(_FORCENAMELESSUNION)

#  define COMSTL_VARIANT_UNION_ARMS_HAVE_NAMES_
# endif
#endif

If anyone else needs to understand _FORCENAMELESSUNION and NONAMELESSUNION, I hope this post can help.