【问题标题】:mkstemp() and fdopen() in Cygwin 1.7.28Cygwin 1.7.28 中的 mkstemp() 和 fdopen()
【发布时间】:2014-03-08 11:42:20
【问题描述】:

在使用 GNU GCC 4.8.2 在 Cygwin(1.7.28-2,64 位)下构建一些基于 C++ 的代码的过程中,我遇到了以下错误:

...
SortDetails.cpp: In function ‘FILE* create_tmpfile(const char*, char**)’:
SortDetails.cpp:127:20: error: ‘mkstemp’ was not declared in this scope
   fd = mkstemp(tmpl);
                    ^
SortDetails.cpp:133:24: error: ‘fdopen’ was not declared in this scope
   fp = fdopen(fd, "wb+");
...

编译失败的具体代码块是:

FILE *
create_tmpfile(char const* path, char** fileName)
{
  FILE* fp;
  int fd;
  char* tmpl;

  if ( path == NULL )
      {
          fileName = NULL;
          return tmpfile();
      }

  tmpl = (char*)malloc(1 + strlen(path) + L_tmpnam);
  strcpy(tmpl, path);
  strcpy(tmpl+strlen(path), "/sb.XXXXXX");
  fd = mkstemp(tmpl);                        /* <----- here... */
  if(fd == -1)
      {
          fprintf(stderr, "unable to create temp file!\n");
          return NULL;
      }
  fp = fdopen(fd, "wb+");                    /* <----- ...and here */
  *fileName = (char*)malloc(strlen(tmpl) + 1);
  strcpy(*fileName, tmpl);
  free(tmpl);
  return fp;
}

malloc 的结果正在转换,因为此代码位于一个更大的基于 C++ 的项目中。)

回归

此代码在 Linux 主机上与 GNU GCC 4.8.x 以及在 OS X 下与 Clang/++ 5.0 一起编译并成功运行。

环境

我正在使用以下版本的 Cygwin:

$ uname -a
CYGWIN_NT-6.1 CygFoo-PC 1.7.28(0.271/5/3) 2014-02-09 21:06 x86_64 Cygwin

这是我使用的 GCC 版本:

$ gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/libexec/gcc/x86_64-pc-cygwin/4.8.2/lto-wrapper.exe
Target: x86_64-pc-cygwin
Configured with: /cygdrive/i/szsz/tmpp/cygwin64/gcc/gcc-4.8.2-2/src/gcc-4.8.2/configure --srcdir=/cygdrive/i/szsz/tmpp/cygwin64/gcc/gcc-4.8.2-2/src/gcc-4.8.2 --prefix=/usr --exec-prefix=/usr --bindir=/usr/bin --sbindir=/usr/sbin --libexecdir=/usr/libexec --datadir=/usr/share --localstatedir=/var --sysconfdir=/etc --libdir=/usr/lib --datarootdir=/usr/share --docdir=/usr/share/doc/gcc --htmldir=/usr/share/doc/gcc/html -C --build=x86_64-pc-cygwin --host=x86_64-pc-cygwin --target=x86_64-pc-cygwin --without-libiconv-prefix --without-libintl-prefix --enable-shared --enable-shared-libgcc --enable-static --enable-version-specific-runtime-libs --enable-bootstrap --disable-__cxa_atexit --with-dwarf2 --with-tune=generic --enable-languages=ada,c,c++,fortran,lto,objc,obj-c++ --enable-graphite --enable-threads=posix --enable-libatomic --enable-libgomp --disable-libitm --enable-libquadmath --enable-math-support --enable-libssp --enable-libada --enable-libgcj-sublibs --disable-java-awt --disable-symvers --with-ecj-jar=/usr/share/java/ecj.jar --with-gnu-ld --with-gnu-as --with-cloog-include=/usr/include/cloog-isl --without-libiconv-prefix --without-libintl-prefix --with-system-zlib
Thread model: posix
gcc version 4.8.2 (GCC)

问题

  1. Cygwin 的 GCC 4.8.2 中是否支持 mkstemp()fdopen()

  2. 如果没有,是否有我可以添加的包或我可以相对容易编译的库来添加对这些功能的支持?

  3. 如果没有,我是否可以使用 mkstemp()fdopen() 的替代品在 Cygwin 下复制它们的功能?

可能的修复

这里是这个函数的修改版本:

FILE *
create_tmpfile(char const* path, char** fileName)
{
  FILE* fp;
  char* tmpl;

  if ( path == NULL )
      {
          fileName = NULL;
          return tmpfile();
      }

#if defined(__CYGWIN__) && !defined(_WIN32)
  const char *cygwinPrefix = "/sb.";
  const char *cygwinTmpDir = "/tmp";
  char *cygwinTmplSuffix = (char *)malloc(1 + L_tmpnam);
  tmpnam(cygwinTmplSuffix);
  tmpl = (char *)malloc(1 + strlen(path) + strlen(cygwinPrefix) + strlen(cygwinTmplSuffix + strlen(cygwinTmpDir) + 1));
  strcpy(tmpl, path);
  strcpy(tmpl+strlen(path), cygwinPrefix);
  strcpy(tmpl+strlen(path)+strlen(cygwinPrefix), cygwinTmplSuffix + strlen(cygwinTmpDir) + 1);
  fp = fopen(tmpl, "wbx+"); /* we add the 'x' extension to apply the O_EXCL flag, to avoid a security hole described in the GNU C library docs */
  free(cygwinTmplSuffix);
#else
  tmpl = (char*)malloc(1 + strlen(path) + L_tmpnam);
  strcpy(tmpl, path);
  strcpy(tmpl+strlen(path), "/sb.XXXXXX");
  int fd = mkstemp(tmpl);
  if(fd == -1)
      {
          fprintf(stderr, "unable to create temp file!\n");
          return NULL;
      }
  fp = fdopen(fd, "wb+");
#endif
  *fileName = (char*)malloc(strlen(tmpl) + 1);
  strcpy(*fileName, tmpl);
  free(tmpl);
  return fp;
}

这太丑了。如果有办法使用 POSIX 函数,如果可以的话,我想使用它们。感谢您的建议。

【问题讨论】:

    标签: c++ c gcc cygwin


    【解决方案1】:

    Cygwin 有一组类似于 Linux 的 Feature Test Macros。但是,在使用 C++ 的 Linux 上,_GNU_SOURCE 是无条件定义的,基本上否定了所有此类保护措施。在 Cygwin 上,我们这样做,这意味着您实际上也必须尊重 C++ 上各种标志的含义。

    如前所述,使用任何-std=c++* 标志将定义__STRICT_ANSI__,它可以被宏识别。在命令行上取消定义是不正确的。相反,要么为您希望使用的函数定义正确的记录标志(在这种情况下,-D_POSIX_C_SOURCE=200809L 应该涵盖两者),或者使用 -std=gnu++* 标志代替(顺便说一句,这样做 not em> 定义 _GNU_SOURCE) 以不声明符合 ANSI。

    【讨论】:

      【解决方案2】:

      最近在编译 git-crypt 时遇到了同样的问题。上述答案的解决方案有效,只是它必须通过“make”而不是通过“g++”进行部署,如下所示:

      make CXXFLAGS="-U__STRICT_ANSI__ -std=c++11"
      

      【讨论】:

        【解决方案3】:

        在 Cygwin 上使用 g++ 4.8.2 编译时,我在三种情况下记录了宏的扩展:

        $ g++ -std=c++11 -E -Dd foo.cpp > foo.log.c++11
        $ g++ -ansi -E -Dd foo.cpp > foo.log.ansi
        $ g++ -E -Dd foo.cpp > foo.log.noFlag
        

        比较日志很有用。 -std=c++11-ansi 案例中存在“漏洞”,而包含 mkstemp() 声明的块出现在“无标志”案例中。这让我可以将标题中处理方式不同的部分归零。

        在文件/usr/include/stdlib.h 中,如果定义了__STRICT_ANSI__,则mkstemp() 和其他一些函数的声明将被拒绝——例如当我们使用编译时标志-ansi-std=c++11 时。

        同样,在文件/usr/include/stdio.h 中,fdopen() 的声明将出于同样的原因而被跳过。

        C++ 头文件&lt;cstdlib&gt;&lt;cstdio&gt; 都包括stdlib.hstdio.h 头文件,并将这两个函数(以及其他)的声明留给这两个头文件。所以如果我们使用-ansi 和/或-std=c++11 那么这两个函数将不会被声明并且我们会得到编译错误。

        似乎适用于玩具代码示例的解决方案是在编译之前取消定义__STRICT_ANSI__

        $ g++ -std=c++11 -U__STRICT_ANSI__ foo.cpp
        

        目前尚不清楚这会产生什么副作用,但从谷歌搜索来看,这似乎是一个常见问题,也是其他需要针对 Cygwin 的开发人员应用的常见修复。

        【讨论】:

          【解决方案4】:

          我的朋友 Vincent 制作了一个与 Cygwin 一起使用的简单版本。 http://code.google.com/p/xpost/source/browse/src/lib/xpost_compat.c#113

          它可能无法涵盖所有​​情况。

          #include <stdio.h>
          #include <fcntl.h>
          #include <sys/stat.h>
          
          # include <windows.h>
          # include <io.h>
          
          int mkstemp(char *template)
          {
              char *temp;
          
              temp = _mktemp(template);
              if (!temp)
                  return -1;
          
              return _open(temp, _O_CREAT | _O_TEMPORARY | _O_EXCL | _O_RDWR, _S_IREAD | _S_IWRITE);
          }
          

          至于fdopen,我不确定。我必须做一些研究/思考。

          【讨论】:

            猜你喜欢
            • 2014-06-20
            • 2011-12-09
            • 2023-03-20
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2010-11-30
            相关资源
            最近更新 更多