boost::filesystem::create_directories(".") fails - c++

boost::filesystem::create_directories(".") always fails on my system. It seems like this might be a bug, but after reading the documentation I'm not entirely sure. Here's an example program:
#include <iostream>
#include <boost/filesystem.hpp>
int main(int argc, char* argv[])
try
{
namespace fs = boost::filesystem;
std::cerr << "is_directory: " << fs::is_directory(argv[1]) << '\n';
std::cerr << "create_directory: " << fs::create_directory(argv[1]) << '\n';
std::cerr << "create_directories: " << fs::create_directories(argv[1]) << '\n';
}
catch (const std::exception& ex)
{
std::cerr << ex.what() << '\n';
}
If I run this with the argument . (meaning the current directory), it prints:
is_directory: 1
create_directory: 0
boost::filesystem::create_directories: Invalid argument
The first two lines are unsurprising: . is a directory, and create_directory() is documented saying:
Creation failure because p resolves to an existing directory shall not be treated as an error.
But the third line is a surprise to me: create_directories(".") failed even though . exists. There is no such failure if you use a different name, like foo - it will happily create that directory or just return false if it exists already.
The docs have this to say:
Effect: Establishes the postcondition by calling create_directory() for any element of p that does not exist.
Postcondition: is_directory(p)
We have shown that the postcondition is true beforehand, so why does it fail?
Edit: I'm using Boost 1.63. I see now that a commit three months ago to "Fix #12495" probably causes this problem: https://github.com/boostorg/filesystem/commit/216720de55359828c2dc915b50e6ead44e00cd15 - and even includes a unit test which demands that create_directories(".") must fail while create_directory(".") must succeed, which is bizarre .

Related

error C2312 is thrown for ifstream::failure and ofstream::failure exceptions

I am writing a small application that modifies a text file. It first creates a copy of the file in case something goes wrong.
The following function creates this copy in the same directory. It takes the file's name as an argument and returns true if the copy is successfully created, and false if it fails.
#include <iostream>
#include <filesystem>
#include <fstream>
#include <string>
using std::ifstream;
using std::ofstream;
using std::string;
using std::cerr;
using std::cin;
using std::cout;
using std::endl;
bool backupFile(string FileName) {
cout << "Creating backup for " << FileName << "..." << endl;
try { // for debugging purposes
string NewName = "bkp_" + FileName;
string CurLine;
ifstream FileCopy(FileName);
ofstream FileBackup(NewName);
if (FileCopy.fail()) { // Could specify how file copy failed?
cerr << "Error opening file " << FileName << ".";
return false;
}
while (getline(FileCopy, CurLine)) { // Copy lines to new file
//cout << "Copying " << CurLine << "\" to " << NewName << "." << endl;
FileBackup << CurLine << "\n";
}
cout << "File successfully backed up to " << NewName << endl;
return true;
}
catch (const ifstream::failure& iE) {
cerr << "Exception thrown opening original file: " << iE.what() << endl;
return false;
}
catch (const ofstream::failure& oE) {
cerr << "Exception thrown outputting copy: " << oE.what() << endl;
}
catch (...) {
cerr << "Unknown exception thrown copying file." << endl;
return false;
}
}
I've used a few catch statements to indicate if there is an issue with the input (ifstream::failure), the output (ofstream::failure), or neither.
During compilation, however, the following error appears:
error C2312: 'const std::ios_base::failure &': is caught by 'const std::ios_base::failure &' on line 42
To me, the error implies that both ifstream::failure and ofstream::failure are caught on ifstream::failure, which seems strange. When I remove the catch for ofstream::failure, it runs fine.
Why is this the case?
ifstream::failure and ofstream::failure are both the same type defined in the std::ios_base base class std::ios_base::failure, you can't catch the same type in two separate catch clauses.
Note that neither of your streams will actually throw any exceptions, by default std::fstream doesn't throw any exceptions. You have to turn exceptions on by calling exceptions:
FileCopy.exceptions(f.failbit);
FileBackup.exceptions(f.failbit);
The above will cause an std::ios_base::failure to be thrown when the stream enters the failed state. As you are already checking for FileCopy.fail() you could just expand that checking to cover other failure cases (e.g. check that FileCopy doesn't fail during getline and that FileBackup also doesn't fail) rather than enabling exceptions.

C++ 17 copy and delete files with special characters in their names on Windows 7 x64?

I wrote a program that copy a file from a given path(s) to another. It runs well until it meets special character in directory names or in file names. At that moment it stops and throws the error that "No such file or directory".
This is what I done until now:
#include <iostream>
#include <vector>
#include <filesystem>
#include <cxxabi.h>
#include <string>
#include <sstream>
#include <memory>
#include <windows.h>
#include <chrono>
#include <thread>
using namespace std;
namespace fs = std::filesystem;
int main(int argc, char **argv) {
vector<string> args(argv + 1, argv + argc);
auto target = args[args.size() - 1];
fs::path path = target;
cout << "Destination path: " << target << endl;
args.erase(args.end());
for (const auto &source : args) {
try {
for (const auto &entry : fs::recursive_directory_iterator(source)) {
std::string new_path = target + "\\" + entry.path().relative_path().string();
//if entry is directory:
while (true) {
if (GetDriveType(const_cast<char *>(path.root_path().string().c_str())) != DRIVE_NO_ROOT_DIR) {
if (fs::is_directory(entry)) {
//only if it NOT exists:
if (!fs::exists(new_path)) {
//create it only if not empty:
if (!fs::is_empty(entry)) {
//Creating directory tree structure with empty folders:
try {
fs::create_directories(new_path);
} catch (const std::exception &e) // caught by reference to base
{
std::cout << "When trying to create directory" << new_path
<< "A standard exception was caught, with message '"
<< e.what() << "'\n";
}
}
}
}
break;
} else {
cout << "Destination path is not available. Sleeping for 3 minutes!" << endl;
std::this_thread::sleep_for(180000ms);
}
}
while (true) {
if (GetDriveType(const_cast<char *>(path.root_path().string().c_str())) != DRIVE_NO_ROOT_DIR) {
if ((fs::is_regular_file(entry)) && (fs::exists(entry))) {
if (!fs::is_empty(entry)) {
if (!fs::exists(new_path)) {
//file does NOT exists in new path:
try {
fs::copy_file(entry.path().string(), new_path);
cout << "Copy file: " << entry.path().string() << endl;
fs::remove(entry);
} catch (const std::exception &e) // caught by reference to base
{
std::cout
<< "When trying to get file size and source a standard exception was caught, with message '"
<< e.what() << "'\n";
}
} else {
//if it exists in new path:
//first try to get file size and if this gives an error then do not copy:
if (fs::file_size(entry.path().string()) >
fs::file_size(entry.path().string())) {
try {
fs::copy_file(entry.path().string(), new_path);
cout << "Replacing file: " << entry.path().string() << endl;
fs::remove(entry);
} catch (const std::exception &e) // caught by reference to base
{
std::cout
<< "When trying to get file size and source a standard exception was caught, with message '"
<< e.what() << "'\n";
}
}
}
}
}
break;
} else {
cout << "Destination path is not available. Sleeping for 3 minutes!" << endl;
std::this_thread::sleep_for(180000ms);
}
}//end while!
}
} catch (const std::exception &e) // caught by reference to base
{
std::cout << "When recursive through directory tree a standard exception was caught, with message '"
<< e.what() << "'\n";
}
}
return 0;
}
After searching on Google and mostly on stackoverflow for a solution conclusion is that none works.
I tried adding #define UNICODE and #define _UNICODE at the top of it but it gives even more errors.
I also added -municode flag in CMakeLists in CLion but also not working (it compiles but gives runtime error).
Also tried to replace all possible string to wstring or wchar_t * with L"where possible" and to convert this entry.path().relative_path().string() to entry.path().relative_path().wstring() and also cout to wcout. Still not working.
Also changed main to wmain( int argc, wchar_t *argv[ ]) or to wmain( int argc, wchar_t *argv[ ], wchar_t *envp[ ] ) and still not working.
Also added setlocale(LC_ALL, ""); after the main function as the other article on stackoverflow says and still no improvement.
I am asking for help because I didn't find a solution for this problem and also, more of the other solution are for printing special unicode characters to console but I need more to work with them (read files names and paths that contain special unicode characters) instead of printing them.
More than this, after I tried all of these possible not working solutions I am talking about above and reverted my program back to the original code that I just posted above now it is not working at all. It says "no such file or directory" even for normal latin characters and doesn't copy or delete anything at all anymore.
Go to the header file in which std::filesystem::path is defined.
(possibly in: PATH_TO_MINGW/usr/include/c++/YOUR_VERSION/bits/fs_path)
Look for using value_type =
Look for compiler macros that define which value_type is ultimately used.
an example from the version from my system:
#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
using value_type = wchar_t;
static constexpr value_type preferred_separator = L'\\';
#else
When the macro _GLIBCXX_FILESYSTEM_IS_WINDOWS is set to 1 then a wchar_t will be used, which should solve your issue.

C++ date library fails with timezone

This used to play once. I am trying to get some data from the C++ date library but an exception is caught. I am compiling with
-DUSE_AUTOLOAD=0 -DHAS_REMOTE_API=0 -DUSE_OS_TZDB=1
what is wrong with the code?
#include <iostream>
#include "date/tz.h"
#include <exception>
using namespace date;
using namespace std::chrono;
int main(int argc, char** argv) {
try {
auto current_time_zone = make_zoned("Europe/Athens", std::chrono::system_clock::now());
auto current_day = date::format("%A", current_time_zone);
auto current_time = date::format("%H:%M", current_time_zone);
std::cout << "day: " << current_day << ", time: " << current_time << " in timezone: " << current_time_zone << std::endl;
//std::cout << " in timezone: " << current_time_zone << std::endl;
} catch ( std::exception& e) {
std::cout << e.what() << std::endl;
}
}
You need to use the -pthread flag. tz.cpp uses call_once to do part of the initialisation. And without -pthread it's not going to work (as underneath it need something like __gthread_once). See this for more details.
You can verify if that's the problem by running your example with gdb (use the catch throw).
I'm not positive what the problem is, but I can tell you that this library does not throw an exception that contains the message "Unknown error".
Try adding -DONLY_C_LOCALE=1 to your build flags. This will avoid your std::lib's time_put facet, but will limit you to only the "C" locale. If this fixes the problem, then it is your std::lib's std::time_put facet that threw the exception.

Checking Output Errors in C and C++

I wrote this simple program. I know that the printf() function in C returns the total number of characters successfully printed so the following C program works fine because any non-zero value is evaluated as true in C.
#include <stdio.h>
int main(void)
{
if (printf("C"))
return 0;
}
But why does the following C++ program compiles & runs fine? If cout is an object not a function, then why is the program giving the expected output?
#include <iostream>
using namespace std;
int main() {
if (cout << "C++")
// your code goes here
return 0;
}
std::cout << "C++";
is a function call to std::operator<<(std::cout, const char*) which returns a reference to std::cout which is convertible to bool. It will evaluate to true if no error on std::cout occurred.
The << operator returns cout. This is why you can chain the << operator like this:
std::cout << "Hello" << " " << "C++" << " " << "World!" << std::endl;
The returned cout will implicitly conver to the value true in the if.
I'm not exactly sure what you are trying to do but assuming you want your program to exit successfully if and only if the printing succeeded, you should write this in C:
#include <stdlib.h>
#include <stdio.h>
int
main()
{
if(printf("C") < 0)
return EXIT_FAILURE;
return EXIT_SUCCESS;
}
The reason is that printf will return a negative number to report errors and –1 is considered “true” as well.
The corresponding C++ program should look like this:
#include <cstdlib>
#include <iostream>
int
main()
{
if (!(std::cout << "C++"))
return EXIT_FAILURE;
return EXIT_SUCCESS;
}
The reason this works is that calling operator << on std::cout returns a reference to the stream itself, which also is why things like
std::cout << "You've got " << 99 << " points" << std::endl;
work. Each invocation returns a reference to std::cout upon which the next invocation is performed.
Now, std::basic_ios which std::cout is derived from, defines a conversion operator. Since C++11, this is declared as
explicit operator bool() const;
This means that if the stream is evaluated in a boolean context such as as the condition in an if statement, it is converted to a bool. The table at the bottom of the linked page shows under what conditions the returned bool will be true or false. In summary: it will return true unless an error occurred on the stream.
Before C++11 (when we didn't have explicit operators), a void * pointer was returned that was non-NULL if and only if no errors on the stream occurred. And again, a void * pointer can be evaluated in a boolean context.
Be aware that in both programs shown above, the tests could indicate success but the output may still fail. The reason for this is that output is usually buffered inside the program and only if enough output is gathered will the operating system be asked to actually do the output. You can request a flush at any time to make this happen now. In C, you would call fflush on the respective FILE * pointer.
#include <stdlib.h>
#include <stdio.h>
int
main()
{
if(printf("C") < 0 || fflush(stdout) < 0)
return EXIT_FAILURE;
return EXIT_SUCCESS;
}
In C++, you can use the std::flush constant.
#include <cstdlib>
#include <iostream>
int
main()
{
if (!(std::cout << "C++" << std::flush))
return EXIT_FAILURE;
return EXIT_SUCCESS;
}
If you also want a newline, you can use std::endl. Writing
std::cout << "hello, world\n" << std::flush;
has about the same effect as writing
std::cout << "hello, world" << std::endl;
Note however that output buffering is used for performance reasons so if you routinely flush your buffers after each and every output statement, you might well degrade the performance of your program.
Finally, in C++ you can also ask a stream to throw an exception if an error occurs. Since I/O errors should not happen during normal operation and repetitively checking for them can clutter your code, this might come in handy.
#include <cstdlib>
#include <iostream>
int
main()
{
std::cout.exceptions(std::ifstream::failbit);
try
{
std::cout << "C++" << std::flush;
return EXIT_SUCCESS;
}
catch (const std::ios_base::failure &e)
{
return EXIT_FAILURE;
}
}
If you want to test whether your program reports the correct exit status for failed I/O, you can try piping standard output to /dev/full on POSIX systems. /dev/full is a special file that pretends that nothing can be written to it because the file system's capacity is exceeded.
Because this is the same as writing
cout << "C++";
if(cout){
//do whatever
}
It just writes "C++" to cout and checks if your stream is still open.

how to attach to an existing shared memory segment

I am having trouble with shared memory. I have one process that creates and writes to a shared memory segment just fine. But I cannot get a second process to attach that same existing segment. My second process can create a new shared segment if I use IPC_CREATE flag but I need to attach to the existing shared segment that was created by the 1st process.
This is my code in the 2nd process:
int nSharedMemoryID = 10;
key_t tKey = ftok("/dev/null", nSharedMemoryID);
if (tKey == -1) {
std::cerr << "ERROR: ftok(id: " << nSharedMemoryID << ") failed, " << strerror(errno) << std::endl;
exit(3);
}
std::cout << "ftok() successful " << std::endl;
size_t nSharedMemorySize = 10000;
int id = shmget(tKey, nSharedMemorySize, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
if (id == -1) {
std::cerr << "ERROR: shmget() failed, " << strerror(errno) << std::endl << std::endl;
exit(4);
}
std::cout << "shmget() successful, id: " << id << std::endl;
unsigned char *pBaseSM = (unsigned char *)shmat(id, (const void *)NULL, SHM_RDONLY);
if (pBaseSM == (unsigned char *)-1) {
std::cerr << "ERROR: shmat() failed, " << strerror(errno) << std::endl << std::endl;
exit(5);
}
std::cout << "shmat() successful " << std::endl;
The problem is that the 2nd process always errors out on the call to shmget() with a "No such file or directory" error. But this is the exact same code I used in the 1st process and it works just fine there. In the 1st process that created the shared segment, I can write to the memory segment, I can see it with "ipcs -m" Also, if I get the shmid from the "ipcs -m" command of the segment and hard code it in my 2nd process and the 2nd process can attach to it just fine. So the problem seems to be generation of the common id that both processes use to identify a single shared segment.
I have several questions:
(1) Is there an easier way to get the shmid of an existing shared memory segment? It seems crazy to me that I have to pass three separate parameters from the 1st process (that created the segment) to the 2nd process just so the 2nd process can get the same shared segment. I can live with having to pass 2 parameters: the file name like "/dev/null" and the same shared id (nSharedMemoryID in my code). But the size of the segment that has to be passed to the shmget() routine in order to get the shmid seems senseless because I have no idea of exactly how much memory was actually allocated (because of the page size issues) so I cannot be certain it is the same.
(2) does the segment size that I use in the 2nd process have to be the same as the size of the segment used to initially create the segment in the 1st process? I have tried to specify it as 0 but I still get errors.
(3) likewise, do the permissions have to be the same? that is, if the shared segment was created with read/write for user/group/world, can the 2nd process just use read for user? (same user for both processes).
(4) and why does shmget() fail with the "No such file or directory" error when the file "/dev/null" obviously exists for both processes? I am assuming that the 1st process does not put some kind of a lock on that node because that would be senseless.
Thanks for any help anyone can give. I have been struggling with this for hours--which means I am probably doing something really stupid and will ultimately embarrass myself when someone points out my error :-)
thanks,
-Andres
(1) as a different way: the attaching process scan the existing segments of the user, tries to attach with the needed size, check for a "magic byte sequence" at the beginning of the segment (to exclude other programs of the same user). Alternatively you can check if the process attached is the one that you expect. If one of the steps fails, this is the first one and will create the segment... cumbersome yes, I saw it in a code from the '70s.
Eventually you can evaluate to use the POSIX compliant shm_open() alternative - should be simpler or at least more modern...
(2) Regarding the size, it's important that the size specified be less/equal than the size of the existing segment, so no issues if it's rounded to the next memory page size. you get the EINVAL error only if it's larger.
(3) the mode flags are only relevant when you create the segment the first time (mostly sure).
(4) The fact that shmget() fail with the "No such file or directory" means only that it hasn't found a segment with that key (being now pedantic: not id - with id we usually refer to the value returnet by shmget(), used subsequently) - have you checked that the tKey is the same? Your code works fine on my system. Just added a main() around it.
EDIT: attached the working program
#include <iostream>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
int main(int argc, char **argv) {
int nSharedMemoryID = 10;
if (argc > 1) {
nSharedMemoryID = atoi(argv[1]);
}
key_t tKey = ftok("/dev/null", nSharedMemoryID);
if (tKey == -1) {
std::cerr << "ERROR: ftok(id: " << nSharedMemoryID << ") failed, " << strerror(errno) << std::endl;
exit(3);
}
std::cout << "ftok() successful. key = " << tKey << std::endl;
size_t nSharedMemorySize = 10000;
int id = shmget(tKey, nSharedMemorySize, 0);
if (id == -1) {
std::cerr << "ERROR: shmget() failed (WILL TRY TO CREATE IT NEW), " << strerror(errno) << std::endl << std::endl;
id = shmget(tKey, nSharedMemorySize, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH | IPC_CREAT);
if (id == -1) {
std::cerr << "ERROR: shmget() failed, " << strerror(errno) << std::endl << std::endl;
exit(4);
}
}
std::cout << "shmget() successful, id: " << id << std::endl;
unsigned char *pBaseSM = (unsigned char *)shmat(id, (const void *)NULL, SHM_RDONLY);
if (pBaseSM == (unsigned char *)-1) {
std::cerr << "ERROR: shmat() failed, " << strerror(errno) << std::endl << std::endl;
exit(5);
}
std::cout << "shmat() successful " << std::endl;
}
EDIT: output
$ ./a.out 33
ftok() successful. key = 553976853
ERROR: shmget() failed (WILL TRY TO CREATE IT NEW), No such file or directory
shmget() successful, id: 20381699
shmat() successful
$ ./a.out 33
ftok() successful. key = 553976853
shmget() successful, id: 20381699
shmat() successful
SOLUTION - after in-chat (wow SO has a chat!) discussion:
At the end the problem was that in the original code he was calling shmctl() later on to tell to detach the segment as the last process detached it, before the other process was attached.
The problem is that this in fact make the segment private. It's key is marked as 0x00000000 by ipcs -m and cannot be attached anymore by other processes - it's in fact marked for lazy deletion.
I just want to post the result of all the help Sigismondo gave me and post the solution to this issue just in case anyone else has the same problem.
The clue was using "ipcs -m" and noticing that the key value was 0 which means that the shared segment is private and so the 2nd process could not attach to it.
An additional quirk was this: I was calling the following:
int nReturnCode = shmctl(id, IPC_RMID, &m_stCtrlStruct);
My intent was to set the mode for the segment so that it would be deleted when all processes that are using it have exited. However, this call has the side effect of making the segment private even though it was created without using the IPC_EXCL flag.
Hopefully this will help anyone else who trips across this issue.
And, many, many thanks to Sigismondo for taking the time to help me--I learned a lot from our chat!
-Andres