Re: vector of fstream type
Quote:
Originally Posted by Paul MacKenzie
Or have a map of filenames to fstream instances
Oh, so you can't put fstream into a vector but you can put it into a map?
Quote:
Originally Posted by Paul McKenzie
The bottom line is that there is no simple class that is the solution. You should scrub the idea of keeping fstream instances in a vector (unless they are fstream pointers) or creating fstream from scratch. All it takes is to use the existing fstream and creating your own classes that manages fstreams.
For example:
Code:
#include <map>
#include <fstream>
#include <string>
typedef std::map<const std::string, std::fstream *> StreamMap;
class FileWrapper
{
//...
private:
std::string TheName;
std::fstream *TheStream;
static StreamMap s_AllStreams;
public:
FileWrapper( ) : pStream(NULL) { }
fstream *OpenStream( const std::string& filename )
{
if ( s_AllStreams.find( filename ) != s_AllStreams.end() )
{
return s_AllStreams[filename];
}
TheStream = new fstream( filename );
s_AllStreams[filename] = pStream;
TheName = filename;
return TheStream;
}
void CloseStream( )
{
StreamMap::iterator it = s_AllStreams.find( TheName );
if ( it != s_AllStreams.end() )
{
if ( TheStream == it->second )
{
delete TheStream;
TheStream = NULL;
s_AllStreams.remove( it );
}
}
}
void CloseAllStreams( )
{
// write code to iterate through the static map and delete streams
StreamMap::iterator it = s_AllStreams.begin();
StreamMap::iterator it2 = s_AllStreams.end();
while ( it != it2 )
{
delete it->second;
++it;
}
s_AllStreams.clear();
}
};
Why bother with all this complicated class design and using fstream pointers? Why noy just simply have a wrapper class that has an fstream *object*, not pointer, and implements a copy constructor and assignment operator that closes the original stream and opens up the new one to the same file and file mode as the old one? Then have a vector of those objects. True, this means that once you insert an fstream object into a vector, vector will automatically close the original fstream, but that's all well since you shouldn't have 2 fstreams connected to the same file anyways.
Quote:
Paul, thank you. You have helped me understand what I must do now. I do still have a question. I noaticed in a few places you wrote std:: instead of just writting using namespace std; a the top. Is that a habit or maybe it causes the complier or the program longer to complie/load.
When you start using namespaces other than std, such as boost or your own, it becomes important that you don't just import all the names from all the namespaces into your .cpp files, because this could lead to ambiguities if, say you had both an std::sort and a my_own_namespace::sort.
Re: vector of fstream type
Quote:
Originally Posted by gorregisguy
Also you seem to have missunderstood a few of my quotes. When I said "Im not sure if it is my complier or just the way the STD is set up" I wasn't inferring to the errors I was getting but the fact that I could not find the source code of fstream. And in fact it is hard, in the fstream header on my computer all it states is what other headers to include. And each one of those only show the function and its arguments. The acutall source code is in other files with strange names, and it becomes hard to choose the right one.
This is slighlty off-topic, but I've had similar problems when trying to analyze the source code of standard libraries. When I want to look at the source code of a library, I'm interested in the actual code, not in all the #if and #else directives that ensure the code is portable and so on. Is there a way I can have just the preprocessor process a file and output the result somewhere? In other words, is there a way I can obtain the final source code of the library that is passed to the compiler, with all the necessary files included and all the preprocessor directives dealt with?
Re: vector of fstream type
Quote:
Originally Posted by HighCommander4
Oh, so you can't put fstream into a vector but you can put it into a map?
Fstream pointers, not instances.
Quote:
Why bother with all this complicated class design and using fstream pointers?
Ask the OP -- this is what I interpreted what the OP is looking for. You're interpretation may be different. Only the OP can tell you exactly what they want to do.
Quote:
Why noy just simply have a wrapper class that has an fstream *object*, not pointer, and implements a copy constructor and assignment operator that closes the original stream and opens up the new one to the same file and file mode as the old one?
This should be directed to the OP. Let him/her explain exactly what they are trying to do or accomplish. Also, what if you are in the middle of writing to the file and the stream closes? Do you reopen the file for writing again, thereby destroying the original contents?
Regards,
Paul McKenzie
Re: vector of fstream type
Quote:
Also, what if you are in the middle of writing to the file and the stream closes? Do you reopen the file for writing again, thereby destroying the original contents?
Care would be taken in the implementation of the copy constructor and the assignment operator, not to re-open the file in trunc mode even if it was previously opened in trunc mode. And the file pointer's position (assuming random-access mode) would also be recorded and the new stream's pointer would be set to that position.
The only reason I suggest this solution instead of using fstream pointers, is that with fstream pointers, you have to worry about new-ing and deleting each object and so on. IMO vectors of pointers should be avoided wherever another alternative is available.
Re: vector of fstream type
1) Do not use vector<char *>. Instead use vector<std::string>. The reason is that the pointer may go out of scope or point to another string during the running of the program, meaning that your vector will have pointers to wrong entries. For example, I could do something like this with your current code:
Code:
FileStream fs;
int foo()
{
char *p = "file1";
return fs.textOpenRead(p);
} // p is now out of scope
int main()
{
int fd = foo();
fs.textReadString(fd); // trouble
}
Hopefully you see the problem. The pointer you added to the vector no longer exists when textReadString is called. With std::string, these problems are alleviated.
2) Do not place "using namespace std" in a header file. If I want to use your File class, it isn't a good thing that I get the entire std:: namespace pulled in just because I included your class.
3) You can use operator [] instead of the at() function. The difference being that [] does not do any bounds checking, while at() does (and throws an exception if the index is out of bounds, making it slightly slower to use). Anyway, the choice is yours.
4) Use code tags when posting.
Here is basically your code with the corrections suggested:
Code:
// File myfstream.h
#ifndef MYFILESTREAM_H
#define MYFILESTREAM_H
#include <fstream>
#include <vector>
#include <string>
class FileStream
{
private:
std::vector<int>ID;
std::vector<std::string> fileName;
std::vector<ios:penmode> flag;
public:
FileStream(){}
~FileStream(){}
int textOpenRead(const std::string&);
void textReadString(int);
};
#endif
//---------------------------------------------------------------------------------
// If this is in a CPP file, you can introduce the entire namespace here
#include "myfstream.h"
#include <iostream>
using namespace std;
int FileStream::textOpenRead(const std::string& fName)
{
fileName.push_back(fName);
flag.push_back(ios::in);
ID.push_back(ID.size());
return ID.size()-1;
}
void FileStream::textReadString(int id)
{
char ch;
fstream file(fileName[id],
flag[id]);
while(file.eof()==false)
{
file.get(ch);
cout<<ch;
}
cout<<endl;
}
Regards,
Paul McKenzie
Re: vector of fstream type
Dear Paul,
Regaurding the error you mentioned involving foo(). I do not understand what you mean at all. I even tested, and it worked fine.
I will try and remember the using namespace std thing from now even though I do not plan on distributing my classes to others.
Thank you again Paul.
[gorregisguy]
Re: vector of fstream type
Quote:
Originally Posted by gorregisguy
Dear Paul,
Regaurding the error you mentioned involving foo(). I do not understand what you mean at all.
Knowledge of C++ scope rules are part of the fundamentals of C++ programming.
In the example I gave, I should have used an array instead of a string-literal. Then you would see the problem.
Code:
int foo()
{
char p[] = "Test";
return fs.textOpenRead(p);
} // p is out of scope.
Assume the value of p is 0x12345678. The value of p, which is local to the foo() function, is pointing to junk once you leave the foo() function, since that char array is local to the function.
Your vector now has 0x12345678, but that is pointing to who-knows-what. When you call the subsequent function to textReadString, your using a vector value that is garbage.
Here is another example:
Code:
FileStream fs;
char p[100];
int main()
{
int fd1, fd2;
strcpy(p, "file1");
fd1 = fs.textOpenRead( p );
strcpy(p, "file2");
fd2 = fs.textOpenRead( p );
fs.textReadString(fd1);
//....
}
What do you think will happen when you call the function textReadString(fd1)? Your vector will have two entries, but both entries contain the same pointer value! Your vector will not have "file1" and "file2" as the two entries -- both entries are "file2".
Quote:
I even tested, and it worked fine.
You do not determine what is right because of testing. This also works "right" on many compilers, but is undefined behavior:
Code:
int main()
{
char *p = new p[10];
delete p; // wrong form of delete
char *p2 = "abcdef";
p2[0] = 'x'; // undefined behavior -- writing to a string-literal
}
In C++, this is called undefined behavior, meaning anything can happen. This means that it may work, may crash, it can do anything depending on the compiler.
So change to vector<string> to ensure safety.
Regards,
Paul McKenzie
Re: vector of fstream type
Quote:
Originally Posted by gorregisguy
Paul, thank you. You have helped me understand what I must do now. I do still have a question. I noaticed in a few places you wrote std:: instead of just writting using namespace std; a the top. Is that a habit or maybe it causes the complier or the program longer to complie/load.
It is just one way of mapping the namespace 'std' to your application....
The following shows you the four different methods to map a namespace...
Code:
// Using the full member name, including the namespace it belongs to:
std::cout << "Hello World";
// By taking advantage of Using-Declarations:
using std::cout; // This declares cout in the current
// scope as synonym for std::cout
cout << "Hello World";
// By taking advantage of Using-Directives:
using namespace std; // Which specifies that the current
// scope can refer to names in the
// 'std' namespace without using
// full qualifiers. This is mostly
// used when porting legacy code.
cout << "Hello World";
// Using aliases:
namespace X
{
namespace Y
{
class Z { ... };
}
}
X::Y::Z // The full qualifier for 'Z' is
// 'X::Y::Z'
namespace w = X::Y; // This declares 'w' as an alias for
// namespace 'X::Y'
w::Z // Access 'Z' using 'w::Z'
As being mentioned....you should basically avoid using anything else than the full qualifier in header files...