I want to create an output file in a threadsafe manner, and only if it does not exist. I want to use the file system for synchronization. With open()
I would use the flags O_RWRONLY|O_CREAT|O_EXCL
. Is there a way to do this in C++17 using the iostream
or fstream
?
Prior to C++23 there is no way of opening an ofstream
in exclusive mode.
Workaround: Use std::fopen
which has this capability since C++17.
Example:
#include <cstdio>
// Mode "x" to make it fail if it already exists
std::FILE* fp = std::fopen("filename", "wx");
if(fp) {
// created exclusively
// work with fp ...
std::fclose(fp);
}
If you really want an ofstream
you could create a helper function:
template<class Stream>
Stream open_exclusively(const std::string& filename) {
Stream rv;
if(std::FILE* fp = std::fopen(filename.c_str(), "wx"); fp) {
std::fclose(fp);
// overwrite the file that was created exclusively:
rv.open(filename);
} else {
// could not create file exclusivly, set the failbit in the stream:
rv.setstate(Stream::failbit);
}
return rv;
}
int main() {
auto os = open_exclusively<std::ofstream>("filename");
if(os) {
std::cout << "file created exclusively\n";
}
}
Even though the above demo is compliant and works on all platforms I've tested it - wine
(v6.16) can't handle it, so I opened a bug report at bugs.winehq.org. You can follow the progress here:
Standard library call fopen(..., "wx")
not recognized - causes destruction of data
The Wine bugfix ucrtbase: Add support for x mode in fopen is now included in Wine 6.20 so after upgrading to 6.20 (or later), this will be working as it should in Wine too.
From C++23 you can use the std::ios::noreplace
openmode
:
std::ofstream os("filename", std::ios::noreplace);
if(os) {
std::cout << "file created exclusively\n";
}