How to create a std::ofstream to a temp file?

21,306

Solution 1

I think this should work:

    char *tmpname = strdup("/tmp/tmpfileXXXXXX");
    ofstream f;
    int fd = mkstemp(tmpname);
    f.attach(fd);

EDIT: Well, this might not be portable. If you can't use attach and can't create a ofstream directly from a file descriptor, then you have to do this:

char *tmpname = strdup("/tmp/tmpfileXXXXXX");
mkstemp(tmpname);
ofstream f(tmpname);

As mkstemp already creates the file for you, race condition should not be a problem here.

Solution 2

I've done this function:

#include <stdlib.h>
#include <fstream>
#include <iostream>
#include <vector>

std::string open_temp(std::string path, std::ofstream& f) {
    path += "/XXXXXX";
    std::vector<char> dst_path(path.begin(), path.end());
    dst_path.push_back('\0');

    int fd = mkstemp(&dst_path[0]);
    if(fd != -1) {
        path.assign(dst_path.begin(), dst_path.end() - 1);
        f.open(path.c_str(), 
               std::ios_base::trunc | std::ios_base::out);
        close(fd);
    }
    return path;
}

int main() {
    std::ofstream logfile;
    open_temp("/tmp", logfile);
    if(logfile.is_open()) {
        logfile << "hello, dude" << std::endl;
    }
}

You should probably make sure to call umask with a proper file creation mask (I would prefer 0600)- the manpage for mkstemp says that the file mode creation mask is not standardized. It uses the fact that mkstemp modifies its argument to the filename that it uses. So, we open it and close the file it opened (so, to not have it opened twice), being left with a ofstream that is connected to that file.

Solution 3

Maybe this will work:

char tmpname[256];
ofstream f;
sprintf (tmpname, "/tmp/tmpfileXXXXXX");
int fd = mkstemp(tmpname);
ofstream f(tmpname);

I haven't tried it, but you can check.

Share:
21,306
Frank
Author by

Frank

Updated on October 31, 2020

Comments

  • Frank
    Frank over 3 years

    Okay, mkstemp is the preferred way to create a temp file in POSIX.

    But it opens the file and returns an int, which is a file descriptor. From that I can only create a FILE*, but not an std::ofstream, which I would prefer in C++. (Apparently, on AIX and some other systems, you can create an std::ofstream from a file descriptor, but my compiler complains when I try that.)

    I know I could get a temp file name with tmpnam and then open my own ofstream with it, but that's apparently unsafe due to race conditions, and results in a compiler warning (g++ v3.4. on Linux):

    warning: the use of `tmpnam' is dangerous, better use `mkstemp'
    

    So, is there any portable way to create an std::ofstream to a temp file?

  • Frank
    Frank over 15 years
    That doesn't compile with my g++ v3.4.4 on Linux. Apparently only some platforms have that function.
  • Frank
    Frank over 15 years
    Thanks! For your second method (using mkstemp and then ofstream): Is that still efficient in terms of I/O? That will access the file system twice, right? Our file system is super slow and I'm worried that it will put an unnecessary burden on it.
  • villasenor
    villasenor over 15 years
    I wonder if it's safe to just use std::string as template and use (char *) dst.path.c_str(). Seems to be fine for most sensible implementations of std::string.
  • Johannes Schaub - litb
    Johannes Schaub - litb over 15 years
    ididak, it's not safe. the c-string pointed to is not writable :)
  • R Samuel Klatchko
    R Samuel Klatchko almost 14 years
    mkstemp can fail and return -1; your code should catch that case.
  • adl
    adl over 11 years
    Also the second example is leaking the file descriptor returned by mkstemp.
  • X-Istence
    X-Istence almost 11 years
    In C++11 a std::string can have it's first entry dereferenced. Thus you can pass &somestring[0]; to a function expecting a char*. See: en.cppreference.com/w/cpp/string/basic_string
  • TOMKA
    TOMKA about 8 years
    You need to call free after using strdup as well.
  • Lightness Races in Orbit
    Lightness Races in Orbit about 6 years
    @X-Istence: Yeah, but be careful in GCC pre-5.1: stackoverflow.com/q/12199710/560648
  • lmat - Reinstate Monica
    lmat - Reinstate Monica over 5 years
    mkstemp is not portable? std::tmpfile is portable, but doesn't seem very useful.