Im no expert of C++ but I have a preety general understanding. Sometimes I try and make effecent code and others I just want to experiment and reevent the wheel. Right now Im trying to take, by take I mean hide all the ugly fstream code, by creating an object, FileStream, that holds various functions ie, textOpenRead that would open a text file for reading. I thought the best way to do this would be to create two vectors, one that olds an int(ID), and another that holds the fstream object.
The ID would serve the purpose of when calling, lets say getLine(int ID). Then the object would match the ID with the correct fstream object and apply getLine to that file. I hope I was able to make myself clear enough, but just for safety's sake Im gonna show an example and source.
//Header
#include <fstream>
#include <vector>
using namespace std;
class FileStream
{
private:
vector<int> ID;
vector<fstream>streams;
public:
FileStream(){}
~FileStream(){}
int textOpenRead(char*);
void getLine(int id);
};
//This is suppose to return the id that is used for later functions for editing
int FileStream::textOpenRead(char *fName)
{
fstream file(fName,ios::out);//Create the fstream object
streams.push_back(file); //Here is where I get errors
ID.push_back(ID.size());
return ID.size()-1;
}
void getLine(int id)
{
char ch;
while(streams[i].get(ch)!=null or enter or endOfFile)
{
cout<<ch;
}
}
//End Header
The getline code I just wrote and is obviously just there for the sake of understanding. For some reason I ger errors at the "streams.push_back(file);" line.
The errors are as follows according to Log:
In copy constructor
`std::basic_ios<char, std::char_traits<char> >::basic_ios(const
std::basic_ios<char, std::char_traits<char> >&)':
C:/Program Files/Dev-Cpp/include/c++/3.3.1/bits/stl_construct.h:78: instantiated from `void std::_Construct(_T1*, const _T2&) [with _T1 = std::fstream, _T2 = std::basic_fstream<char, std::char_traits<char> >]'
C:/Program Files/Dev-Cpp/include/c++/3.3.1/bits/stl_vector.h:599: instantiated from `void std::vector<_Tp, _Alloc>::push_back(const _Tp&) [with _Tp = std::fstream, _Alloc = std::allocator<std::fstream>]'
FileStream.h:21: instantiated from here
C:/Program Files/Dev-Cpp/include/c++/3.3.1/bits/ios_base.h:668: error: `
std::ios_base::ios_base(const std::ios_base&)' is private
C:/Program Files/Dev-Cpp/include/c++/3.3.1/bits/stl_construct.h:78: error: within
this context
C:/Program Files/Dev-Cpp/include/c++/3.3.1/streambuf: In copy constructor `
std::basic_filebuf<char, std::char_traits<char> >::basic_filebuf(const
std::basic_filebuf<char, std::char_traits<char> >&)':
C:/Program Files/Dev-Cpp/include/c++/3.3.1/bits/stl_construct.h:78: instantiated from `void std::_Construct(_T1*, const _T2&) [with _T1 = std::fstream, _T2 = std::basic_fstream<char, std::char_traits<char> >]'
C:/Program Files/Dev-Cpp/include/c++/3.3.1/bits/stl_vector.h:599: instantiated from `void std::vector<_Tp, _Alloc>::push_back(const _Tp&) [with _Tp = std::fstream, _Alloc = std::allocator<std::fstream>]'
FileStream.h:21: instantiated from here
C:/Program Files/Dev-Cpp/include/c++/3.3.1/streambuf:922: error: `
std::basic_streambuf<_CharT, _Traits>::basic_streambuf(const
std::basic_streambuf<_CharT, _Traits>&) [with _CharT = char, _Traits =
std::char_traits<char>]' is private
C:/Program Files/Dev-Cpp/include/c++/3.3.1/bits/stl_construct.h:78: error: within
this context
C:/Program Files/Dev-Cpp/include/c++/3.3.1/bits/ios_base.h: In member function
`std::basic_ios<char, std::char_traits<char> >& std::basic_ios<char,
std::char_traits<char> >::operator=(const std::basic_ios<char,
std::char_traits<char> >&)':
C:/Program Files/Dev-Cpp/include/c++/3.3.1/bits/stl_vector.h:603: instantiated from `void std::vector<_Tp, _Alloc>::push_back(const _Tp&) [with _Tp = std::fstream, _Alloc = std::allocator<std::fstream>]'
FileStream.h:21: instantiated from here
C:/Program Files/Dev-Cpp/include/c++/3.3.1/bits/ios_base.h:671: error: `
std::ios_base& std::ios_base::operator=(const std::ios_base&)' is private
C:/Program Files/Dev-Cpp/include/c++/3.3.1/bits/vector.tcc:230: error: within
this context
C:/Program Files/Dev-Cpp/include/c++/3.3.1/streambuf: In member function `
std::basic_filebuf<char, std::char_traits<char> >& std::basic_filebuf<char,
std::char_traits<char> >::operator=(const std::basic_filebuf<char,
std::char_traits<char> >&)':
C:/Program Files/Dev-Cpp/include/c++/3.3.1/streambuf:925: error: `
std::basic_streambuf<_CharT, _Traits>& std::basic_streambuf<_CharT,
_Traits>::operator=(const std::basic_streambuf<_CharT, _Traits>&) [with
_CharT = char, _Traits = std::char_traits<char>]' is private
C:/Program Files/Dev-Cpp/include/c++/3.3.1/bits/vector.tcc:230: error: within
this context
make.exe: *** [main.o] Error 1
Execution terminated
Any help you can give me on how to properly get a fstream vector. Maybe I shouldn't be using fstream as the ojbect or maybe it just can't be done.
Any light you can shine on the subject is greatly appreciated.
Thank you.
[gorregisguy]
Philip Nicoletti
February 19th, 2005, 09:27 PM
I don't think that you can copy fstream objects. I guess you need to
hold pointers instead ... vector<fstream*>.
gorregisguy
February 19th, 2005, 09:43 PM
I was under the impression that a vector was a better array, and since arrays can hold any data type, vectors can too. But like I said I'm fairly new to this. The problem with the pointers though, as far as I know, is that what the vector is pointing at will no longer exist at the end of the function.
Thank you for the repley.
[gorregisguy]
Paul McKenzie
February 20th, 2005, 01:26 AM
I was under the impression that a vector was a better array, and since arrays can hold any data type, vectors can too.A vector has strict requirements of what can be placed in it.
For vector<T>, type T must be copyable, assignable, and default constructable. An fstream is not copyable or assignable, therefore it cannot be placed in a vector. The reason why this is a requirement is that unlike arrays, vectors are dynamically resizable. To accomplish this, the vector must be able to copy and move elements from one location to another. If your object cannot be copied, then it doesn't satisfy the requirements for a vector.
Basically, this simple test is all you need to see if your type can be stored in a vector:
int main()
{
T a;
// assume a has some "live data"
T b = a;
T c;
c = a;
}
where "T" is the type you want to store in the vector. These are the types of operations that vector will be performing on T.
It the above code doesn't compile, then T cannot be used in a vector. If the code does compile, but doesn't run correctly (from start to finish), then T can not be safely used in a vector (but vector will accept it, regardless of the runtime bugs that will occur).
Note that if T is an int, double, float, i.e. a simple type, the following code works. Replace T with fstream, and you see that the program above won't even compile. If you replace T with a buggy user-defined class (usually a class that needs a user-defined copy constructor, assignment operator, and destructor, but one of those three are missing), the code will compile, but running the program causes problems.
Regards,
Paul McKenzie
gorregisguy
February 20th, 2005, 07:07 AM
Thank you Paul McKenzie. Do you or anyone else know if I could possibly make my own fstream class from scratch. I know I can make other standard classes from scratch, ie the string class and cmath. And if possibly how would you go about doing that.
Any information is greatly appricated.
[gorregisguy]
Andreas Masur
February 20th, 2005, 08:53 AM
Thank you Paul McKenzie. Do you or anyone else know if I could possibly make my own fstream class from scratch. I know I can make other standard classes from scratch, ie the string class and cmath. And if possibly how would you go about doing that.
Well...rather than questioning whether this is possible or not, I would rather question the approach in the first place...in other words, in my eyes this is the wrong conclusion you have generated from the posts.
I would rather say, you should reconsider your design....why do you need a vector of streams in the first place? Furthermore....you can still use pointers to these streams instead as being mentioned.
gorregisguy
February 20th, 2005, 10:34 AM
Thank you for the response Andreas Masur. I had wanted to a vector of streams so that I could simplfy the creating,writing, and opeing of files. By having one object that keeps track of all fstreams open. May seem pointless to some but it's something I need. In regards to why don't I just have a pointer of fstreams, like I said before, I don't belive that is possible because the fstream is created at the beggining of the function and no longer exists at the end.
Thank you for you time.
[gorregisguy]
Paul McKenzie
February 20th, 2005, 12:04 PM
In regards to why don't I just have a pointer of fstreams, like I said before, I don't belive that is possible because the fstream is created at the beggining of the function and no longer exists at the end.This doesn't stop you from placing the address of the fstream that is created in the vector<fstream*>. I don't see how a vector of fstream could have helped you as opposed to a vector of fstream*.
The only difference is that the vector<fstream> will call the destructor of the fstream objects when the vector goes out of scope, and invariably when the vector needs to be resized. This is another reason why vector<fstream> is highly dangerous. A vector is going to call the destructor of your objects, due mostly to vector resizing. The fstream destructor closes the stream, causing more problems if the vector goes ahead and destructs your fstream without you knowing about it.
It seems if you have little choice but to use a vector of fstream pointers, or create a class that manages the fstream's by making that class responsible for creating and destroying the fstreams (similar to a factory class). You still need to use pointers, however.
Thank you Paul McKenzie. Do you or anyone else know if I could possibly make my own fstream class from scratch.I agree with Andreas. This is the wrong approach and you should rethink your design.
I know I can make other standard classes from scratch, ie the string classOnly an experienced C++ programmer can do this correctly. Many "novice" C++ programmers (usually programmers who know other languages well but not C++) have tried this and failed miserably.
Regards,
Paul McKenzie
gorregisguy
February 20th, 2005, 12:41 PM
The string class isn't as hard as some may belive it to be. I modeled the one I made off the Java String class. Making my own fstream class seems impossible, to me at least. Im not sure if it is my complier or just the way the STD is set up but finding the orginal source for fstream is extremily hard, so there is nothing I can look at to get a good general idea.
As far as rethinking my orginal design, I haven't a clue on how to have an(one) object that controls all the file input and output with simplicity. I'll go back to the drawing board and see if there is anything I may have missed.
Thank you again for your replies,
[gorregisguy]
Andreas Masur
February 20th, 2005, 01:05 PM
The string class isn't as hard as some may belive it to be. I modeled the one I made off the Java String class.
And without seeing it, I doubt that it is nearly as efficient as the one provided by the standard STL 'string' class...again...this is a typical error many developer from other languages make...before re-inventing the wheel, simply explore the facilities the other languag already provides.
As for the rest....what compiler are you using?
gorregisguy
February 20th, 2005, 02:17 PM
DevC++ by BloodShed. It's the best free complier I've found. And do you happen to know of a way to test the String class' efficiency, for my own benefit.
[gorregisguy]
Paul McKenzie
February 20th, 2005, 03:49 PM
The string class isn't as hard as some may belive it to be. I modeled the one I made off the Java String class.It is hard, believe me, and I've been using C++ for over 15 years now. A lot goes into making the string class that matches the standard std::string class. Look at the Gnu C++ implementation of std::string (Gnu is used by Dev-cpp). Look at what it takes to get everything done right (it is a reference counted string class).
As I stated before, many novice or new C++ programmers have attempted this, and failed. Bugs, inefficiencies, etc. all caused the home-made string class to be thrown out, and to use just the std::string class. Only an experienced C++ programmer would know exactly what it takes to make any class that rivals what the standard classes gives you.
Making my own fstream class seems impossible, to me at least.I have never seen a programmer have to create their own fstream handling class from scratch. Im not sure if it is my complier or just the way the STD is set upIt has nothing to do with the compiler, but it has everything to do with what the ANSI standard states how fstream is supposed to behave. Copying an fstream object is undefined behavior. Be lucky your compiler caught the error, because you would have been having all sorts of problems if your compiler's fstream implementation exposed (inappropriately) the copy constructor or assignment operator for fstream. You would have then asked why your program is behaving strangely instead of why you are getting the compiler errors.
but finding the orginal source for fstream is extremily hardIt is not hard at all, it's all there in <fstream>. As far as rethinking my orginal design, I haven't a clue on how to have an(one) object that controls all the file input and output with simplicity.Wrap the fstream in a class, and keep a reference count so that if copies are created, you just bump up the reference count, and decrement the reference count when objects are destroyed. Or have a map of filenames to fstream instances, or something like that.
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.
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();
}
};
A very rough idea with a lot of missing code (it also was not compiled), but this is what you should be trying to do. The interface to open and close a stream is OpenStream and CloseStream. A static map of the name and stream created is used. Whenever a new stream is opened or closed, an update of the map is done. If you need to close all the streams, iterate through the static map and delete each stream.
Again, this may not be the ideal design, but it is a design nevertheless that doesn't rely on undefined behavior or vectors of fstream instances (which can't compile anyway, and would be disastrous at runtime if it could compile).
I'll go back to the drawing board and see if there is anything I may have missed.That's the problem -- it isn't that you've missed anything, it is the design that you've chosen can't be used.
Regards,
Paul McKenzie
gorregisguy
February 20th, 2005, 04:08 PM
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.
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.
As far as the string class being hard to make. I don't doubt you, I'm sure mine isn't as effeicent as the standard one but it was only a, see if I can do it, kind of thing.
Thank you again for all the help,
[gorregisguy]
HighCommander4
February 20th, 2005, 05:04 PM
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?
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.
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.
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.
HighCommander4
February 20th, 2005, 05:08 PM
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?
Paul McKenzie
February 20th, 2005, 05:14 PM
Oh, so you can't put fstream into a vector but you can put it into a map?Fstream pointers, not instances.
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. 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
HighCommander4
February 20th, 2005, 05:43 PM
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.
gorregisguy
February 20th, 2005, 07:36 PM
Unfortunetly I am fairly new to all of this so I don't exactly understand all of your solutions, but I am tring. I did go back to the drawing board and rethought my approach and found a workable solution for my purposes.
#include <fstream>
#include <vector>
using namespace std;
Now this isn't everything I want, but now that I have my workable start I plan to add the other functions I originally intended. Thank you all again for the help and I am still open to any suggestions anyone has to make my class for efficient.
Paul McKenzie
February 20th, 2005, 08:28 PM
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:
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:
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>
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]
Paul McKenzie
February 20th, 2005, 10:48 PM
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.
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:
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".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:
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
Andreas Masur
February 21st, 2005, 02:41 AM
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...
// 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...
codeguru.com
Copyright Internet.com Inc., All Rights Reserved.