I need to be able to use a single fstream to have platform-independent way of using files. In particular, I need to be able to support file paths with unicode characters on Windows with as minimal intrusion into code to support it as possible. As such, it seemed like boost iostreams could provide the answer. However, upon trying to use it, I get two failures which cause me concern. See the following sample code:
// MinGW (MSYS)
// GCC 4.7.2
// Boost 1.50.0
// g++ -g ifstreamtest.cpp -o test.exe -I /t/tools/boost/boost_1_50_0 -L /t/tools/boost/boost_1_50_0/stage/lib -lboost_system-mgw47-mt-d-1_50 -lboost_filesystem-mgw47-mt-d-1_50 -lboost_locale-mgw47-mt-d-1_50 -lboost_iostreams-mgw47-mt-d-1_50
#include <boost/locale.hpp>
#include <boost/iostreams/device/file_descriptor.hpp>
#include <boost/iostreams/stream.hpp>
#include <boost/filesystem/path.hpp>
namespace MyNamespace
{
typedef ::boost::iostreams::file_descriptor fd;
typedef ::boost::iostreams::stream< ::boost::iostreams::file_descriptor> fstream;
typedef ::boost::iostreams::stream< ::boost::iostreams::file_descriptor_sink> ofstream;
typedef ::boost::iostreams::stream< ::boost::iostreams::file_descriptor_source> ifstream;
} // namespace MyNamespace
int main(int argc, char **argv)
{
// Imbue boost filesystem codepoint conversions with local system
// Do this to ensure proper UTF conversion.
boost::filesystem::path::imbue(boost::locale::generator().generate(""));
// Test file path.
boost::filesystem::path file_path("test.txt");
// Anonymous scope for temporary object.
{
// Open file in ctor, write to output, neglect to clean up until dtor.
MyNamespace::ofstream output(file_path, std::ios_base::out | std::ios_base::app);
if ( output.is_open() == false ) std::cout << "Unable to open #" << __LINE__ << std::endl;
output << "test line 1" << std::endl;
std::cout << "done #" << __LINE__ << std::endl;
}
// Temporary object destroyed while still open.
// Anonymous scope for temporary object.
{
// Open file in ctor, write to output, specifically close file.
MyNamespace::ofstream output1(file_path, std::ios_base::out | std::ios_base::app);
if ( output1.is_open() == false ) std::cout << "Unable to open #" << __LINE__ << std::endl;
output1 << "test line 2" << std::endl;
output1.close();
std::cout << "done #" << __LINE__ << std::endl;
}
// Temporary object destroyed.
// Anonymous scope for temporary object.
{
// Default-ctor; open later. Write to file, neglect to clean up until dtor.
MyNamespace::ofstream output2;
// Next line causes "Assertion failed: initialized_, file t:/tools/boost/boost_1_50_0/boost/iostreams/detail/optional.hpp, line 55"
output2->open(file_path, std::ios_base::out | std::ios_base::app);
if ( output2.is_open() == false ) std::cout << "Unable to open #" << __LINE__ << std::endl;
output2 << "blah test test blah" << std::endl;
}
// Temporary object destroyed.
// MyNamespace::ifstream input;
// Compile success, but linker failure:
// s:\reactor\utf8stream/ifstreamtest.cpp:42: undefined reference to `void boost::iostreams::file_descriptor_source::open<boost::filesystem::path>(boost::filesystem::path const&, std::_Ios_Openmode)'
// input->open(file_path, std::ios_base::in);
std::cout << "done." << std::endl;
return 0;
}
On Windows, I am limited to GCC 4.7.2 and Boost 1.50.
The comments explain the two failures, but I will expand on them here. The first and most problematic to me is when I try to use the stream object as a "normal" fstream object. If I open the fstream in its constructor, then all is fine and dandy (as can be seen in the first two anonymous scopes). But if I allocate the fstream object and then attempt to open it later, "Bad Things" happen.
If I try to invoke boost::iostreams::stream::open(), I get compiler errors saying that it can't convert parameter 1 (boost::filesystem::path) to a boost::iostreams::file_descriptor_sink. Why should that not work when it can be constructed with a boost::filesystem::path? In either case, attempting to call boost::iostreams::file_descriptor_sink::open() by using the stream's operator->() fails an assertion (as shown in the third anonymous scope). This is quite evil as it would seem to me that it should throw an exception instead of failing an assertion. Failing an assertion would indicate to me that there's a bug in the boost code.
The second failure I have is that the typedefed fstream and ofstream seem to work (well, compile and link) just fine. But when I try to use the ifstream, I get a linker failure when trying to call ifstream->open(); I get this on Windows (MinGW configuration as stated before) as well as on OS X 10.8.5 with Apple LLVM version 4.2 (clang-425.0.28) (based on LLVM 3.2svn). Since it compiles fine and the only difference between the two there is whether it's a source or a sink... and both should be able to open a file... it makes me think that this is also a bug in boost.
Any thoughts or suggestions?
Regarding the compiler error:
I can reproduce the linking error on Linux, with g++ and Clang++, so it's not a specific Windows problem (I'm also using Boost 1.55.0, so it's not specific to 1.50).
I suppose it is caused by a template definition that is allowed in the header file, but not implemented in the source/library.
Solution (for the linking problem only): Instead of
input->open(file_path, std::ios_base::in);
use
input->open(file_path.string(), std::ios_base::in);
This circumvents the potentially misdefined template by using a string-based constructor.
Regarding the assertion error:
The issue is you need to initialize the file_descriptor_sink separately, else the iostream initialization won't be handled properly. Use this code:
//We need to initialize the sink separately
boost::iostreams::file_descriptor_sink output2Sink(file_path, std::ios_base::out | std::ios_base::app);
MyNamespace::ofstream output2(output2Sink);
if ( output2.is_open() == false ) std::cout << "Unable to open #" << __LINE__ << std::endl;
output2 << "blah test test blah" << std::endl;
open() doesn't seem to reset the optional that causes the assertion.
The same method needs to be applied to the the MyNamespace::ifstream:
boost::iostreams::file_descriptor_source inputSource(file_path, std::ios_base::in);
MyNamespace::ifstream input(inputSource);
//Test reading back what we wrote earlier
std::string inputContent;
input >> inputContent;
//Prints: blah (only the first word is read!)
std::cout << "Read from test.txt: " << inputContent << std::endl;
Also note that it is not neccessary to apply the solution to avoid the compiler error from above.
With these modifications your program appears to be working on my system.
Related
I'm basically looking for a C++ version of fdopen(). I did a bit of research on this and it is one of those things that seems like it should be easy, but turns out to be very complicated. Am I missing something in this belief (i.e. it really is easy)? If not, is there a good library out there somewhere to handle this?
EDIT: Moved my example solution to a separate answer.
From the answer given by Éric Malenfant:
AFAIK, there is no way to do this in
standard C++. Depending on your
platform, your implementation of the
standard library may offer (as a
nonstandard extension) a fstream
constructor taking a file descriptor
as input. (This is the case for
libstdc++, IIRC) or a FILE*.
Based on above observations and my research below there's working code in two variants; one for libstdc++ and another one for Microsoft Visual C++.
libstdc++
There's non-standard __gnu_cxx::stdio_filebuf class template which inherits std::basic_streambuf and has the following constructor
stdio_filebuf (int __fd, std::ios_base::openmode __mode, size_t __size=static_cast< size_t >(BUFSIZ))
with description This constructor associates a file stream buffer with an open POSIX file descriptor.
We create it passing POSIX handle (line 1) and then we pass it to istream's constructor as basic_streambuf (line 2):
#include <ext/stdio_filebuf.h>
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main()
{
ofstream ofs("test.txt");
ofs << "Writing to a basic_ofstream object..." << endl;
ofs.close();
int posix_handle = fileno(::fopen("test.txt", "r"));
__gnu_cxx::stdio_filebuf<char> filebuf(posix_handle, std::ios::in); // 1
istream is(&filebuf); // 2
string line;
getline(is, line);
cout << "line: " << line << std::endl;
return 0;
}
Microsoft Visual C++
There used to be non-standard version of ifstream's constructor taking POSIX file descriptor but it's missing both from current docs and from code. There is another non-standard version of ifstream's constructor taking FILE*
explicit basic_ifstream(_Filet *_File)
: _Mybase(&_Filebuffer),
_Filebuffer(_File)
{ // construct with specified C stream
}
and it's not documented (I couldn't even find any old documentation where it would be present). We call it (line 1) with the parameter being the result of calling _fdopen to get C stream FILE* from POSIX file handle.
#include <cstdio>
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main()
{
ofstream ofs("test.txt");
ofs << "Writing to a basic_ofstream object..." << endl;
ofs.close();
int posix_handle = ::_fileno(::fopen("test.txt", "r"));
ifstream ifs(::_fdopen(posix_handle, "r")); // 1
string line;
getline(ifs, line);
ifs.close();
cout << "line: " << line << endl;
return 0;
}
AFAIK, there is no way to do this in standard C++. Depending on your platform, your implementation of the standard library may offer (as a nonstandard extension) a fstream constructor taking a file descriptor (This is the case for libstdc++, IIRC) or a FILE* as an input.
Another alternative would be to use a boost::iostreams::file_descriptor device, which you could wrap in a boost::iostreams::stream if you want to have an std::stream interface to it.
There's a good chance your compiler offers a FILE-based fstream constructor, even though it's non-standard. For example:
FILE* f = fdopen(my_fd, "a");
std::fstream fstr(f);
fstr << "Greetings\n";
But as far as I know, there's no portable way to do this.
Part of the original (unstated) motivation of this question is to have the ability to pass data either between programs or between two parts of a test program using a safely created temporary file, but tmpnam() throws a warning in gcc, so I wanted to use mkstemp() instead. Here is a test program that I wrote based on the answer given by Éric Malenfant but using mkstemp() instead of fdopen(); this works on my Ubuntu system with Boost libraries installed:
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <string>
#include <iostream>
#include <boost/filesystem.hpp>
#include <boost/iostreams/device/file_descriptor.hpp>
#include <boost/iostreams/stream.hpp>
using boost::iostreams::stream;
using boost::iostreams::file_descriptor_sink;
using boost::filesystem::path;
using boost::filesystem::exists;
using boost::filesystem::status;
using boost::filesystem::remove;
int main(int argc, const char *argv[]) {
char tmpTemplate[13];
strncpy(tmpTemplate, "/tmp/XXXXXX", 13);
stream<file_descriptor_sink> tmp(mkstemp(tmpTemplate));
assert(tmp.is_open());
tmp << "Hello mkstemp!" << std::endl;
tmp.close();
path tmpPath(tmpTemplate);
if (exists(status(tmpPath))) {
std::cout << "Output is in " << tmpPath.file_string() << std::endl;
std::string cmd("cat ");
cmd += tmpPath.file_string();
system(cmd.c_str());
std::cout << "Removing " << tmpPath.file_string() << std::endl;
remove(tmpPath);
}
}
It actually is quite easy. Nicolai M. Josuttis has released fdstream in conjunction with his book The C++ Standard Library - A Tutorial and Reference. You can find the 184 line implementation here.
I've tried the solution proposed above for libstdc++ by Piotr Dobrogost, and found that it had a painful flaw: Due to the lack of a proper move constructor for istream, it's very difficult to get the newly constructed istream object out of the creating function. Another issue with it is that it leaks a FILE object (even thought not the underlying posix file descriptor). Here's an alternative solution that avoids these issues:
#include <fstream>
#include <string>
#include <ext/stdio_filebuf.h>
#include <type_traits>
bool OpenFileForSequentialInput(ifstream& ifs, const string& fname)
{
ifs.open(fname.c_str(), ios::in);
if (! ifs.is_open()) {
return false;
}
using FilebufType = __gnu_cxx::stdio_filebuf<std::ifstream::char_type>;
static_assert( std::is_base_of<ifstream::__filebuf_type, FilebufType>::value &&
(sizeof(FilebufType) == sizeof(ifstream::__filebuf_type)),
"The filebuf type appears to have extra data members, the cast might be unsafe");
const int fd = static_cast<FilebufType*>(ifs.rdbuf())->fd();
assert(fd >= 0);
if (0 != posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL)) {
ifs.close();
return false;
}
return true;
}
The call to posix_fadvise() demonstrates a potential use. Also note that the example uses static_assert and using which are C++ 11, other than that it should build just fine in C++ 03 mode.
Another non-portable solution is to use mmap (or its Windows' analogue) and then construct std::iostream from a pointer that mmap gave like so.
Yeah, it does not construct exactly an std::fstream, but this requirement rarely needs to be met because every piece of code should depend on stream interfaces (e.g. std::istream) rather than on their implementations.
I think this solution is more portable than use of STL implementation-specific hacks, because this way you only depend on an operating system, rather than on a specific implementation of STL for the same OS.
My understanding is that there is no association with FILE pointers or file descriptors in the C++ iostream object model in order to keep code portable.
That said, I saw several places refer to the mds-utils or boost to help bridge that gap.
I'm trying to call std::cout within lldb in an Xcode 5 C++ project. My project has an #include <iostream> line (and I verified that compiled std::cout commands work fine), but it does not have a using namespace std; line.
When I stop at a breakpoint in lldb, I can't call std::cout:
(lldb) expr std::cout << "test"
error: no member named 'cout' in namespace 'std'
error: 1 errors parsing expression
(lldb) expr cout << "test"
error: use of undeclared identifier 'cout'
error: 1 errors parsing expression
For those interested, I'm trying to use std::cout to print an OpenCV Mat object. But that detail is probably not important.
My lldb version is lldb-300.2.53.
By request, here's the (trivial) code:
#include <iostream>
int main(int argc, const char * argv[])
{
std::cout << "Hello World" << std::endl;
return 0;
}
The breakpoint is at the return 0; line.
maybe you can do it by another way:
1, create a dylib, import all headers needed, write a function like this:
void mylog(const MyObject& obj)
{
//assume MyObject is the type you want to view in Debuger
std::cout << obj << std::endl;
}
build as libdbghelper.dylib in your desktop(or another path which is short).
2,load it in to your debugging project:
(lldb) target modules add /Users/yourName/Desktop/libdbghelper.dylib
3,then you can log it with command
(lldb)expr mylog((const MyObject&)myobj);
here is the running result in my mac:
http://i.stack.imgur.com/LBBLJ.jpg
the code of dylib like that:
http://i.stack.imgur.com/H1Q9v.jpg
you cannot using std::cout in commandline as you cannot WATCH it in ANY Debuger, but you can declare a reference to it like this:
std::ostream& os = std::cout;
so that you can execuate command expr os << "ok" in lldb.
here is the running result in my mac:
http://i.stack.imgur.com/lHvfa.jpg
hope it helpful
I'm not positive this is a dup but I believe the answer from Jim Ingham over in
Evaluating an expression with overloaded operators in c++ lldb
is likely highly relevant to the problem you're seeing here.
I keep getting this error message and don't know why.
dyld: Library not loaded: libboost_thread.dylib
Referenced from: /Users/adhg/Documents/workspace_cpp/Boost_101/Release/Boost_101
Reason: image not found
the code:
#include <iostream>
#include <boost/thread.hpp>
void workerFunction() {
boost::posix_time::seconds workTime(3);
std::cout << "Worker: running" << std::endl;
boost::this_thread::sleep(workTime);
std::cout << "Worker: finished" << std::endl;
}
int main() {
std::cout << "main: startup" << std::endl;
boost::thread workerThread(workerFunction);
std::cout << "main: waiting for thread" << std::endl;
workerThread.join();
std::cout << "main: done" << std::endl;
return 0;
}
What I did is simply follow the instructions here and in many other places, the basics:
download boost
unzip to folder
./bootstrap.sh
./bjam
you will note that the usr/local/boost... is where I actually placed my folder (it exists) and under usr/local/boost_1_54_0/stage/lib I have the libboost_thread and so forth. Still...not sure why I get this error.
My settings looks like this now:
Can anyone point out what am I doing wrong?
This looks to be a dynamic linking issue, and from a little research, it seems that this can be solved by setting the DYLD_LIBRARY_PATH environment variable to point to where the libraries are located (in your case, /usr/local/boost_1_54_0/stage/lib). You may wish to read this similar question and also this external page, the latter of which states
This happend because I built boost from source and kept it local. Thus the path to the library was not the default one. To solve this problem we have to edit the environment variable DYLD_LIBRARY_PATH, which is analogous to linux LD_LIBRARY_PATH
(Also, I'll just mention that if you are only interested in threading capabilities and not Boost as a whole, you could use C++11's <thread> header.)
I'm basically looking for a C++ version of fdopen(). I did a bit of research on this and it is one of those things that seems like it should be easy, but turns out to be very complicated. Am I missing something in this belief (i.e. it really is easy)? If not, is there a good library out there somewhere to handle this?
EDIT: Moved my example solution to a separate answer.
From the answer given by Éric Malenfant:
AFAIK, there is no way to do this in
standard C++. Depending on your
platform, your implementation of the
standard library may offer (as a
nonstandard extension) a fstream
constructor taking a file descriptor
as input. (This is the case for
libstdc++, IIRC) or a FILE*.
Based on above observations and my research below there's working code in two variants; one for libstdc++ and another one for Microsoft Visual C++.
libstdc++
There's non-standard __gnu_cxx::stdio_filebuf class template which inherits std::basic_streambuf and has the following constructor
stdio_filebuf (int __fd, std::ios_base::openmode __mode, size_t __size=static_cast< size_t >(BUFSIZ))
with description This constructor associates a file stream buffer with an open POSIX file descriptor.
We create it passing POSIX handle (line 1) and then we pass it to istream's constructor as basic_streambuf (line 2):
#include <ext/stdio_filebuf.h>
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main()
{
ofstream ofs("test.txt");
ofs << "Writing to a basic_ofstream object..." << endl;
ofs.close();
int posix_handle = fileno(::fopen("test.txt", "r"));
__gnu_cxx::stdio_filebuf<char> filebuf(posix_handle, std::ios::in); // 1
istream is(&filebuf); // 2
string line;
getline(is, line);
cout << "line: " << line << std::endl;
return 0;
}
Microsoft Visual C++
There used to be non-standard version of ifstream's constructor taking POSIX file descriptor but it's missing both from current docs and from code. There is another non-standard version of ifstream's constructor taking FILE*
explicit basic_ifstream(_Filet *_File)
: _Mybase(&_Filebuffer),
_Filebuffer(_File)
{ // construct with specified C stream
}
and it's not documented (I couldn't even find any old documentation where it would be present). We call it (line 1) with the parameter being the result of calling _fdopen to get C stream FILE* from POSIX file handle.
#include <cstdio>
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
int main()
{
ofstream ofs("test.txt");
ofs << "Writing to a basic_ofstream object..." << endl;
ofs.close();
int posix_handle = ::_fileno(::fopen("test.txt", "r"));
ifstream ifs(::_fdopen(posix_handle, "r")); // 1
string line;
getline(ifs, line);
ifs.close();
cout << "line: " << line << endl;
return 0;
}
AFAIK, there is no way to do this in standard C++. Depending on your platform, your implementation of the standard library may offer (as a nonstandard extension) a fstream constructor taking a file descriptor (This is the case for libstdc++, IIRC) or a FILE* as an input.
Another alternative would be to use a boost::iostreams::file_descriptor device, which you could wrap in a boost::iostreams::stream if you want to have an std::stream interface to it.
There's a good chance your compiler offers a FILE-based fstream constructor, even though it's non-standard. For example:
FILE* f = fdopen(my_fd, "a");
std::fstream fstr(f);
fstr << "Greetings\n";
But as far as I know, there's no portable way to do this.
Part of the original (unstated) motivation of this question is to have the ability to pass data either between programs or between two parts of a test program using a safely created temporary file, but tmpnam() throws a warning in gcc, so I wanted to use mkstemp() instead. Here is a test program that I wrote based on the answer given by Éric Malenfant but using mkstemp() instead of fdopen(); this works on my Ubuntu system with Boost libraries installed:
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <string>
#include <iostream>
#include <boost/filesystem.hpp>
#include <boost/iostreams/device/file_descriptor.hpp>
#include <boost/iostreams/stream.hpp>
using boost::iostreams::stream;
using boost::iostreams::file_descriptor_sink;
using boost::filesystem::path;
using boost::filesystem::exists;
using boost::filesystem::status;
using boost::filesystem::remove;
int main(int argc, const char *argv[]) {
char tmpTemplate[13];
strncpy(tmpTemplate, "/tmp/XXXXXX", 13);
stream<file_descriptor_sink> tmp(mkstemp(tmpTemplate));
assert(tmp.is_open());
tmp << "Hello mkstemp!" << std::endl;
tmp.close();
path tmpPath(tmpTemplate);
if (exists(status(tmpPath))) {
std::cout << "Output is in " << tmpPath.file_string() << std::endl;
std::string cmd("cat ");
cmd += tmpPath.file_string();
system(cmd.c_str());
std::cout << "Removing " << tmpPath.file_string() << std::endl;
remove(tmpPath);
}
}
It actually is quite easy. Nicolai M. Josuttis has released fdstream in conjunction with his book The C++ Standard Library - A Tutorial and Reference. You can find the 184 line implementation here.
I've tried the solution proposed above for libstdc++ by Piotr Dobrogost, and found that it had a painful flaw: Due to the lack of a proper move constructor for istream, it's very difficult to get the newly constructed istream object out of the creating function. Another issue with it is that it leaks a FILE object (even thought not the underlying posix file descriptor). Here's an alternative solution that avoids these issues:
#include <fstream>
#include <string>
#include <ext/stdio_filebuf.h>
#include <type_traits>
bool OpenFileForSequentialInput(ifstream& ifs, const string& fname)
{
ifs.open(fname.c_str(), ios::in);
if (! ifs.is_open()) {
return false;
}
using FilebufType = __gnu_cxx::stdio_filebuf<std::ifstream::char_type>;
static_assert( std::is_base_of<ifstream::__filebuf_type, FilebufType>::value &&
(sizeof(FilebufType) == sizeof(ifstream::__filebuf_type)),
"The filebuf type appears to have extra data members, the cast might be unsafe");
const int fd = static_cast<FilebufType*>(ifs.rdbuf())->fd();
assert(fd >= 0);
if (0 != posix_fadvise(fd, 0, 0, POSIX_FADV_SEQUENTIAL)) {
ifs.close();
return false;
}
return true;
}
The call to posix_fadvise() demonstrates a potential use. Also note that the example uses static_assert and using which are C++ 11, other than that it should build just fine in C++ 03 mode.
Another non-portable solution is to use mmap (or its Windows' analogue) and then construct std::iostream from a pointer that mmap gave like so.
Yeah, it does not construct exactly an std::fstream, but this requirement rarely needs to be met because every piece of code should depend on stream interfaces (e.g. std::istream) rather than on their implementations.
I think this solution is more portable than use of STL implementation-specific hacks, because this way you only depend on an operating system, rather than on a specific implementation of STL for the same OS.
My understanding is that there is no association with FILE pointers or file descriptors in the C++ iostream object model in order to keep code portable.
That said, I saw several places refer to the mds-utils or boost to help bridge that gap.
I know there's been a handful of questions regarding std::ifstream::open(), but the answers didn't solve my problem. Most of them were specific to Win32, and I'm using SDL, not touching any OS-specific functionality (...that's not wrapped up into SDL).
The problem is: std::ifstream::open() doesn't seem to work anymore since I've switched from Dev-C++ to Code::Blocks (I've been using the same MinGW-GCC back-end with both), and from Windows XP to Vista. (It also works perfectly with OS X / xcode (GCC back-end).)
My project links against a static library which #includes <string>, <iostream>, <fstream> and <cassert>, then a call is made to functionality defined in the static library, which in turn calls std::ifstream::open() (this time, directly). Following this, the stream evaluates to false (with both the implicit bool conversion operator and the good() method).
Code:
#include "myStaticLibrary.hpp"
int main(int argc, char **argv)
{
std::string filename("D:/My projects/Test/test.cfg");
std::cout << "opening '" << filename << "'..." << std::endl;
bool success(false);
// call to functionality in the static library
{
std::ifstream infile(filename.c_str());
success = infile.good();
// ...
}
// success == false;
// ...
return 0;
}
stdcout.txt says:
opening 'D:/My projects/Test/test.cfg'...
When I open stdcout.txt, and copy-paste the path with the filename into Start menu / Run, the file is opened as should be (I'm not entirely sure how much of diagnostic value this is though; also, the address is converted to the following format: file:///D:/My%20projects/test/test.cfg).
I've also tried substituting '/'s with the double backslash escape sequence (again, slashes worked fine before), but the result was the same.
It is a debug version, but I'm using the whole, absolute path taken from main()'s argv[0].
Where am I going wrong and what do I need to do to fix it?
Please create a minimal set that recreates the problem. For example, in your code above there's parsing of argv and string concatentation, which do not seem like a necessary part of the question. A minimal set would help you (and us) see exactly what's going wrong, and not be distracted by questions like "what's GetPath()?".
Try to do this instead of assert(infile.good()):
assert(infile);
I have overseen the importance of the fact that the function in question has close()d the stream without checking if it is_open().
The fact that it will set the stream's fail_bit (causing it to evaluate to false) was entirely new to me (it's not that it's an excuse), and I still don't understand why did this code work before.
Anyway, the c++ reference is quite clear on it; the problem is now solved.
The following code:
#include <string>
#include <iostream>
#include <fstream>
#include <assert.h>
using namespace std;;
int main(int argc, char **argv)
{
std::string filename("D:/My projects/Test/test.cfg");
std::cout << "opening '" << filename << "'..." << std::endl;
std::ifstream infile(filename.c_str());
assert(infile.good()); // fails
return 0;
}
works fine on my Windows system using MinGW g++ 4.4.0, if I create the required directory structure. Does the file test.cfg actually exist? If you are opening a stream for input, it wioll fail if the file is not there.
Edit: To remove any DevC++ to CB issues:
build using command line only
make sure you rebuild the static library too