MinGW-w64 g++ 是否公开了微软 ucrt 对 C11 `getenv_s` 函数的实现?如果是,要包括什么header?

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

根据 cppreference.com,getenv_s 自 C11

以来得到支持
errno_t getenv_s( size_t *restrict len, char *restrict value,
                  rsize_t valuesz, const char *restrict name );

MinGW-w64 8.1 中,g++ 报告 #includecstdlibstdlib.h

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

我想知道为什么 MinGW-w64 g++ 似乎没有暴露微软 ucrt 的 C11 getenv_s

在 c++ 中,我们是否已经有了 可移植 安全检索环境变量的方法?

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

您可以使用 getenv。如果不想让指向他人拥有的 C 字符串的原始指针泄漏到您的代码中,您可以使用 std::optional:

#include <cstdlib>
#include <optional>
std::optional<std::string> get_env(const char* env) {
    auto t = std::getenv(env);
    if (t) return t;
    return {};
}

Full example.

PS:即使它在 C++ 中可用,我也不确定我是否会使用 getenv_sresitrct 不是标准的 C++,单独传递数组和它们的大小不是非常惯用的 C++。据我了解,getenv_s 是对 C 中 getenv 的改进,您需要以某种方式处理空指针和字符串长度,而在 C++ 中,我们有不同的解决方案(std::string 用于可变长度字符串和 std::optional 可选值)。

编辑:

下面最初修改的答案并不完全正确。目前在 MinGW-w64 实现上 <sec_api/stdlib_s.h> 中没有 getenv_s 的声明,但是你可以自己声明:

#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

在 MSVC 上,您仍然只需使用 #include <stdlib.h>,因为那里声明了 getenv_s

<sec_api/stdlib_s.h> 中还缺少其他几个 C++ 模板函数,大概是由于缺乏需要,而缺少 getenv_s 的声明可能只是没有人需要的东西 getenv 工作得很好。

值得一提的是 a Windows-only function called _dupenv_sgetenv_s 更容易使用,您只需使用标准 free 函数释放内存即可。它在 <sec_api/stdlib_s.h> 中声明,因此您可以毫无问题地使用它。


修改原答案:

在回答这个问题时,从源代码构建的 MinGW-w64 允许您默认启用或禁用安全 CRT 函数的公开,但即使启用,它似乎也没有将大多数标准 C 函数标记为安全替换为 "deprecated",就像 Visual C++ 的 CRT header 所做的那样(实际上,它似乎确实将其中一些标记为已弃用,但宏在我正在使用的构建中扩展为空) .

为了更直接地解决这个问题,MinGW-w64 实现目前将安全 CRT 函数的原型存储在 sec_api 目录中的单独 header 文件中,并且 header 文件不包含在标准 C header 中,这意味着相应的 C++ header <cstdlib> 也不会声明函数,因为它仅包含标准 header.

相反,您需要显式包含所需的 C header,例如 <sec_api/stdlib_s.h><sec_api/stdio_s.h> 等,如果安全 API 已启用(即 MINGW_HAS_SECURE_API_mingw.h 中定义为 1)。由于函数可能可用于链接,您可以在任何包含之前使用 #define MINGW_HAS_SECURE_API 1 以启用已声明的安全函数的使用,或者在未声明的情况下自行声明函数。

我觉得值得一提的是许多(尽管不是全部)C++ 模板函数,例如

// #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
);

已宣布并实施。

基于 examples in Microsoft's documentation 和 MinGW-w64 情况下的预处理器输出,两种实现都将安全函数放在全局 C++ 命名空间中,而不是 std 命名空间(例如 strcpy_s其完全限定形式是 ::strcpy_s),因为它们不是标准的 C++ 函数。