In the course of building some C++-based code under Cygwin (1.7.28-2, 64-bit) with GNU GCC 4.8.2, I ran into the following errors:
...
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+");
...
The specific chunk of code that fails to compile is:
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;
}
(The results of malloc
are being cast because this code is within a larger C++-based project.)
Regression
This code compiles and works successfully with GNU GCC 4.8.x on Linux hosts and with Clang/++ 5.0 under OS X.
Environment
I am using the following version of Cygwin:
$ uname -a
CYGWIN_NT-6.1 CygFoo-PC 1.7.28(0.271/5/3) 2014-02-09 21:06 x86_64 Cygwin
Here is the version of GCC I am using:
$ 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)
Questions
Is there support for mkstemp()
and fdopen()
in GCC 4.8.2 for Cygwin?
If not, is there a package I can add or a library I can relatively easily compile to add support for these functions?
If not, are there alternatives to mkstemp()
and fdopen()
that I can make use of to replicate their functionality under Cygwin?
Possible fix
Here's a modified version of this function:
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;
}
This is pretty ugly. If there is a way to use POSIX functions, I'd like to use them, if I can. Thanks for any advice.
When compiling with g++
4.8.2 on Cygwin, I logged the expansion of macros in three cases:
$ 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
Comparing the logs was useful. There were "holes" in the -std=c++11
and -ansi
cases, while a block containing the mkstemp()
declaration shows up in the "flagless" case. This let me zero in on the parts of the headers that were processed differently.
In the file /usr/include/stdlib.h
, declarations of mkstemp()
and some other functions are rejected if __STRICT_ANSI__
is defined — such as when we use the compile-time flags -ansi
and -std=c++11
.
Likewise, in the file /usr/include/stdio.h
, declarations of fdopen()
will get skipped for the same reason.
The C++ headers <cstdlib>
and <cstdio>
both include the stdlib.h
and stdio.h
headers and leave declaration of those two functions (among others) up to those two headers. So if we use -ansi
and/or -std=c++11
then those two functions will not be declared and we get the compile errors.
The solution that seems to work for the toy code samples is to undefine __STRICT_ANSI__
before compiling:
$ g++ -std=c++11 -U__STRICT_ANSI__ foo.cpp
It's not clear what the side effects of this will be, but from googling, it seems like this is a common problem and a common fix applied by other developers who need to target Cygwin.
Cygwin has a Linux-like set of Feature Test Macros. However, on Linux with C++, _GNU_SOURCE
is defined unconditionally, essentially negating all such guards. On Cygwin, we do not do this, meaning you actually have to respect the meaning of the various flags on C++ as well.
As noted, using any -std=c++*
flag will define __STRICT_ANSI__
, which is recognized by the macros. Undefining that on the command line is incorrect. Instead, either define the correct documented flag(s) for the functions you wish to use (in this case, -D_POSIX_C_SOURCE=200809L
should cover both), or use -std=gnu++*
flags instead (which, btw, do not define _GNU_SOURCE
) to not declare ANSI compliance.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With