Search code examples
c++mingwc++17mingw-w64c11

Does MinGW-w64 g++ expose microsoft ucrt's implementation of C11 `getenv_s` function? If yes, what header to be included?


According to cppreference.com, getenv_s gets supported since C11

errno_t getenv_s( size_t *restrict len, char *restrict value,
                  rsize_t valuesz, const char *restrict name );

With MinGW-w64 8.1, g++ reports an error with both #include with cstdlib and stdlib.h

use of undeclared identifier 'getenv_s'; did you mean '_wgetenv_s'?
    errcode = getenv_s(&envsize, NULL, 0, name);
              ^~~~~~~~
              _wgetenv_s

I wonder why MinGW-w64 g++ seems not to expose microsoft ucrt's C11 getenv_s?

In c++, do we already have a portable way to retrieve environment variables safely?


Solution

  • Edit:

    The originally amended answer below is not entirely correct. Currently there is no declaration for getenv_s in <sec_api/stdlib_s.h> on MinGW-w64 implementations, but you can declare it yourself:

    #ifdef __cplusplus
    extern "C" {
    #endif
    
    #include <sec_api/stdlib_s.h> /* errno_t, size_t */
    errno_t getenv_s(
        size_t     *ret_required_buf_size,
        char       *buf,
        size_t      buf_size_in_bytes,
        const char *name
    );
    
    /*
     * You can omit this section if you don't want to use the safer
     * C++ template function in your C++ code.
     */
    #ifdef __cplusplus
    extern "C++" {
      template <size_t size>
      getenv_s(
          size_t *ret_required_buf_size,
          char (&buf)[size],
          const char *name
      ) { return getenv_s(ret_required_buf_size, buf, size, name); }
    }
    #endif
    
    #ifdef __cplusplus
    }
    #endif
    

    On MSVC, you'd still just use #include <stdlib.h> as getenv_s is declared there.

    There are also several other C++ template functions missing from the <sec_api/stdlib_s.h> already, presumably due to lack of need, and the lack of declaration for getenv_s entirely is perhaps just something nobody needed as getenv worked just fine.

    It's worth mentioning that there is a Windows-only function called _dupenv_s that is much easier to use in place of getenv_s, and you'd just free the memory using the standard free function. It is declared in <sec_api/stdlib_s.h>, so you can use it without issue.


    Amended original answer:

    At the time of this answer, MinGW-w64 built from source allows you to enable or disable the exposure of the secure CRT functions by default, but even when enabled, it does not appear to mark most standard C functions with secure replacements as "deprecated" the way that Visual C++'s CRT headers do (actually, it appears that it does mark some of them as deprecated, but the macros expand to nothing in the build I'm using).

    To more directly address the question, the MinGW-w64 implementation currently stores the prototypes for the secure CRT functions in a separate header file in the sec_api directory, and that header file is not included from the standard C header, which means the corresponding C++ header <cstdlib> will not declare the functions either since it includes only the standard header.

    Instead, you need to explicitly include the C headers you need such as <sec_api/stdlib_s.h>, <sec_api/stdio_s.h>, etc., which will only declare the functions if the secure API has been enabled (i.e. MINGW_HAS_SECURE_API is defined to 1 in _mingw.h). As the functions are likely available for linking, you can just use #define MINGW_HAS_SECURE_API 1 before any includes to enable the use of the secure functions that are declared, or declare the functions yourself in the event that they're undeclared.

    I feel it's worth mentioning that many, though not all, of the C++-only template functions such as

    // #include <sec_api/string_s.h> in MinGW-w64.
    // #include <string.h> (C) or <cstring> (C++) in MSVC.
    template <size_t size>
    errno_t strcpy_s(
        char      (&dest)[size],
        const char *src
    );
    

    are declared and implemented.

    Based on examples in Microsoft's documentation and preprocessor output in the case of MinGW-w64, both implementations place the secure functions in the global C++ namespace, not the std namespace (e.g. strcpy_s in its fully qualified form is ::strcpy_s) as they're not standard C++ functions.