I'm writing a class for working with text files. I would like to "copy" ifstream-object properties. The code below shows how I'm doing it. I have a problem with a function w.m_fin.tellg():
error C2662: 'std::basic_istream<_Elem,_Traits>::tellg' : cannot convert 'this' pointer from 'const std::ifstream' to 'std::basic_istream<_Elem,_Traits> &'
I want to set a file position in the destination object as in the source.
If I make the argument non-const [ Word(Word& w) ] all is OK. But I don't want to make it non-const. What should I do in order to solve this problem?
Thank you
class Word
{
private:
std::ifstream m_fin;
std::string m_in_filename;
public:
Word(const Word& w): m_in_filename( w.m_in_filename )
{
m_fin(m_in_filename);
m_fin.copyfmt( w.m_fin );
m_fin.clear( w.m_fin.rdstate() );
m_fin.seekg( w.m_fin.tellg() );//here I get an error
}
}
Since btellg (by potentially setting fail status) changes the state of the stream (as does seek and any form of read or write operation of course), you can't do that on a const object. However, I expect you could, if you wanted to solve it that way, declare m_fin as mutable, which means the compiler allows it to be changed even for const object.
Related
Long title, but I can't think how to make it any more concise. Same goes for actually asking the question really. Unfortunately, I don't really know the right words to ask the question I want, so I'm hoping if I can describe it, someone will know what I'm trying to ask...
Basically, I'm building a game and have various classes for reading and writing various types of file. Config files, saved games, asset packages etc. For the example of asset packages (models, sounds, etc) I have an AssetPackReader class and an AssetPackWriter class, that use an ifstream and ofstream object respectively. Incomplete classes at the moment, just showing the basic theory.
class AssetPackWriter
{
protected:
unsigned int FilesToAddOnCommit;
std::string * NewFileCommitList;
AssetPackStruct AssetPackData;
std::ofstream * AssetPackFileStream;
bool ChangesSaved;
public:
AssetPackWriter(std::ofstream * OutputFileStream)
{
AssetPackFileStream = OutputFileStream;
ChangesSaved = false;
}
virtual ~AssetPackWriter()
{
if(!ChangesSaved) Save();
}
void AddFile(std::string FileName);
virtual void Save(void); //Perform full save
};//The writer only writes new files (overwriting ones that already exist)
class AssetPackReader
{
protected:
AssetPackStruct AssetPackData;
std::ifstream * AssetPackFileStream;
public:
AssetPackReader(std::ifstream * InputFileStream)
{
AssetPackFileStream = InputFileStream;
}
unsigned long long FindFile(std::string FileNameWithPath);
void ExtractFile(unsigned long long FileEntry, CryptoPP::BufferedTransformation * OutputSink = 0);
//First 4 bytes = Folder name index
//remaining 4 bytes = file name index
//When the game extracts something, the extract output will be an ArraySink into memory
//but when invoked from command line, the extract output can either be an existing FileSink, or left blank to create a file on the hard disk of the same name
void ListContents(CryptoPP::BufferedTransformation * OutputSink = 0); //If output is left blank it will be to a string sink which prints to stdout.
};//The reader only reads existing files (read only - no adding new files to an existing archive)
Now I want to create a class that has the functionality of both. That can:
create a new package from scratch (like the writer),
open an already existing package (like the reader),
add more assets to the package, either
without rewriting it all from scratch (which is slower)
rewriting it all from scratch (which should make the file faster to read)
A user may prefer a rewrite-from-scratch (FullSave) because not modifying the existing packaged data means a second table of contents needed (which without super optimisation, is far less efficient when it comes to seeking between the tables of contents).
Whilst this FullSave should make reading the file a bit faster (not sure how much by), it takes considerably longer than a FastSave, especially where the existing archive being modified is already quite large.
So, here is my attempt at using multiple inheritance to create such a class that contains both the reading and writing operations of each superclass. This to me sounds more efficient than copying and pasting the functionality of the reader and writer class, but I'm clearly not quite understanding something...
class AssetPackageManager: public AssetPackWriter, public AssetPackReader
{
protected:
std::fstream * AssetPackFileStream;
bool DefaultFastSave;
public:
AssetPackageManager(std::fstream * PackageFileStream) : AssetPackWriter(PackageFileStream), AssetPackReader(PackageFileStream)
{
DefaultFastSave = true;
}
~AssetPackageManager()
{
Save();
}
void Save(void); //Overrides inherited function. AssetPackWriter::Save can only perform a FullSave, because it always starts with an empty file.
//The default for a AssetPackageManager is the FastSave, which only adds new data to the end
void FullSave(void)
{
AssetPackWriter::Save();
//Rewrites the entire asset package (and all uncommitted changes) from scratch.
}
void FastSave(void); //Adds uncommitted changes to the end of the existing file
}; //The packager can write new files, and read (and update) existing files (new files can be added to an existing archive)
As you see, I have changed the ifstream and ofstream objects from each of the base classes and replaced with them with the fstream, but something is now telling me that I'm following a dead end.
If the ExtractFile function in the reader class takes an ifstream, can it (when inherited by package manager class) use the fstream?
I thought multiple inheritance was the right way about to go about it due to the fact that:
ifstream inherits from istream
ofstream inherits from ostream
fstream inherits from iostream (which in turn multiply inherits from istream and ostream)
...But I'm just not sure any more, not least because I get errors in the Package manager constructor... It says that the types (ifstream and ofstream versus fstream) are not related, but I thought they were...
error C2664: 'AssetPackWriter::AssetPackWriter(std::ofstream *)' : cannot convert parameter 1 from 'std::fstream *' to 'std::ofstream *'
1> Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast
error C2664: 'AssetPackReader::AssetPackReader(std::ifstream *)' : cannot convert parameter 1 from 'std::fstream *' to 'std::ifstream *'
1> Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast
I have a class TypeData and want to store objects of that type in a QMap then I want to get a specific object out of the map and call a memberfunction of this object.
But when I try to do that i get the following error message:
error C2662: 'TypeData::write': cannot convert 'this' pointer from 'const TypeData' to 'TypeData &'
here are the relevant code snippets:
QMap<QString, TypeData> typeDataList;
typeDataList.insert(currentID, temp);
typeDataList.value(currentID).write();
Can anyone tell what I', doing wrong here? And how I could fix this?
QMap::value returns a const T, i.e. a both a copy of the element in the map, and a non-modifyable one. Your write() method is probably not const, thus calling write() on the const T is not allowed. If value returned just T, it would work, but any changes write() does to the temporary object would be lost right away. (As the copy is destroyed right after).
So you can either make write() const if it doesn't modify the TypeData.
That's preferable, if possible.
Alternatively you can do:
typeDataList[currentID].write() // modifies the object in the map but also will insert a TypeData() if there is no entry for key currentID yet.
or, more verbose but without inserting a new element if none is found:
QMap<QString,TypeData>::Iterator it = typeDataList.find(currentID);
if ( it != typeDataList.constEnd() ) {
it->write();
} else {
// no typedata found for currentID
}
/* Thanks to anyone looking at this who might attempt to answer it. I'm really not trying to waste anyone's time here, but I have beat my head on this for about three days. I realize it is probably very simple for someone who understands it. I have tried most every possible combination I can think of and still get compiler errors.
C:\random\RNDNUMTEST.cpp(41) : error C2102: '&' requires l-value
I am trying to pass a pointer as a parameter to a function makeRndmNumber() for the member function fstream.open(). I want to open the file in RNDNUMTEST.cpp and then pass it to makeRndmNumber() so that it can be modified in some way. I have looked online for help, including this website, but I feel like I am overlooking something important or simple or maybe I am just missing the concept altogether.
This isn't for homework, I'm not a college student. Although I did go to school for it, it has been over 10 years since I've done any programming and I never really understood this that well to begin with. Any suggestions would be appreciated.
// These are only excerpts from the actual files.
// RndmNum_Class.h file
typedef void(fstream::*fStream_MPT)(const char*); // fStream_MPT (Member Pointer Type)
class RandomNumber {
public:
RandomNumber();
~RandomNumber() {};
static void loadDigits(double, double, char array[]);
static int getLastNDigits(char array[], int);
static int makeRndmNumber(int, int, fStream_MPT);
};
//*************************************************************8
//RndmNum_Class.cpp file
int RandomNumber::makeRndmNumber(int seed, int _fileSize, fStream_MPT FILE) {
......
}
//**************************************************************/
// RNDNUMTEST.cpp file
#include "RndmNum_Class.h"
int main() {
const char* RNDM_FILE = "c:\\RandomFile.txt";
fstream FStream_Obj;
// FStream_Obj.open(RNDM_FILE);
fStream_MPT FileMembPtr = &FStream_Obj.open(RNDM_FILE);
//fStream_MPT FileMembPtr = &fstream::open;
int seed = 297814;
int size = 20000;
cout << RandomNumber::makeRndmNumber(seed, size, FileMembPtr);
return 0;
}
This: &FStream_Obj.open(RNDM_FILE) is not taking the address of the function, it's trying to take the address of the return value of a call to that function. But that function returns void, hence the error message.
First, change the function definition from typedef void(fstream::*fStream_MPT)(const char*); to typedef void(fstream::*fstream_MPT)(const char*,ios_base::openmode), there is a default parameter you are forgetting.
Change the fStream_MPT FileMembPtr = &FStream_Obj.open(RNDM_FILE); to fStream_MPT FileMembPtr = &fstream::open; as per your comment, and add an additional parameter to makeRndNumber, a pointer to an fstream to operate on.
int RandomNumber::makeRndmNumber(int seed, int _fileSize, fStream_MPT FILE, fstream *file)
{
((*file).*FILE)("ExampleText",ios_base::in | ios_base::out);
}
FILE = fstream::open;
EDIT
This could also be done a little cleaner with std::function objects.
First redefine your type.
typedef std::function<void(const char*)> fStream_MPT;
Then when you assign, be sure to bind your objects.
fStream_MPT FILE = std::bind(&fstream::open,&file,std::placeholders::_1, ios_base::in | ios_base::out);
Then in your function you simply call the function
int RandomNumber::makeRndmNumber(int seed, int _fileSize, fStream_MPT FILE)
{
FILE("Example text");
}
It doesn't make any sense: member function pointers is used so you can apply different member functions somewhere without knowing which exact function is called. It is like passing the function's name around (except that the name is resolved at compile-time). It doesn't seem that this is what you want to do!
Even if you would correctly obtain the function's address (rather than trying to get the address of the result of calling open()), it wouldn't work because std::fstream::open() takes two arguments: the second argument is for the open-mode and it is defaulted to std::ios_base::in | std::ios_base::out.
I'm not quite sure what you really want to d but it seems you want to pass the file stream around. The normal way to do this is to pass a reference to a std::iostream as argument to the function. Well, actually you probably want to use a std::ifstream initially and hence pass the argument as std::istream&.
I have a class constructor like so:
DotDashLogMatcher( std::stringstream const& pattern );
I call it like so:
std::stringstream s;
DotDashLogMatcher( s << "test" );
This is an over-simplified example, but that is essentially what is going on. Here is the exact compiler error I'm getting. Note that for some reason the resulting object that is passed in is a basic_ostream, I am not sure if this is normal. It isn't able to cast it to an std::stringstream like my function expects.
error C2664: 'DotDashLogMatcher::DotDashLogMatcher(const stlpd_std::stringstream &)' : cannot convert parameter 1 from 'stlpd_std::basic_ostream<_CharT,_Traits>' to 'const stlpd_std::stringstream &'
with
[
_CharT=char,
_Traits=stlpd_std::char_traits<char>
]
Reason: cannot convert from 'stlpd_std::basic_ostream<_CharT,_Traits>' to 'const stlpd_std::stringstream'
with
[
_CharT=char,
_Traits=stlpd_std::char_traits<char>
]
No constructor could take the source type, or constructor overload resolution was ambiguous
I'm using VS2003 and STLport on Windows.
Anyone know where I'm going wrong here? Why won't this code compile? I apologize in advance if I am lacking information. I will update my question for those that request more info.
operator<< does not return a std::stringstream because it is inherited from std::ostream. See:
http://www.cplusplus.com/reference/iostream/stringstream/
You may use:
DotDashLogMatcher( s );
Or you may change your method declaration in order to match the return type.
I believe you should split the statement into two separate commands:
s << "test";
DotDashLogMatcher( s );
as the parameter is passed by reference and thus needs to be modifiable, therefore an l-value.
Maybe you want to change:
DotDashLogMatcher( std::stringstream const& pattern );
Into:
DotDashLogMatcher( std::ostream const& pattern );
The problem is that operator << is overloaded for std::ostream and returns a std::ostream.
If you can;t change it there are several workarounds.
std::stringstream s;
s << "test"
DotDashLogMatcher( s );
// slightly more dangerious but should work.
std::stringstream s;
DotDashLogMatcher( static_cast<std::stringstream const&>(s << "test") );
I've already found a workaround to this problem, but was just wondering if anyone knew what was actually happening to cause the problem I was seeing. My guess is that it has something to do with mutability of strings, but I thought the CString object accounted for that in the copy constructor.
The following code causes mFileName to be overwritten:
class File {
public:
...
CString GetFilename() {return mFileName;}
private:
CString mFileName;
};
class FileContainer {
private: File* mFile;
public:
FileContainer() {
mFile = new File("C:\temp.txt");
}
GetFilename(CString& fileName) {
fileName = mFile->GetFileName();
}
}
void UpdateText() {
FileContainer fileCnt;
CString filePath(L"");
this->fileCnt.GetFilename(filePath);
...
::DrawText(hDC, filePath, -1, &destRect, DT_PATH_ELLIPSIS | DT_MODIFYSTRING | DT_CALCRECT);
}
What happens is that the first time UpdateText is called, GetFilename returns C:\temp.txt. Assuming that the bounding rect caused the text to be truncated to "...\temp.txt" on the first call, "...\temp.txt" is what is returned from GetFilename on the second call to UpdateText.
Even more perplexing is that this didn't cause mFileName to be changed:
void UpdateText() {
FileContainer fileCnt;
CString filePath(L"");
this->fileCnt->GetFilename(filePath);
filePath = L"TEST";
}
GetFilename always returned C:\temp.txt. So it would seem that the DrawText function is somehow finding the original CString and modifying it. But how?
UPDATE: I figured I'd throw another odd chunk of code that also causes mFileName to be overwritten:
class File {
public:
...
CString GetFilename() {return CString(mFileName);}
private:
CString mFileName;
};
That seems like it should create a new object and return that new object. Yet, somehow, DrawText still overwrites mFileName.
If I change the code to the following, I don't have any issues:
class File {
public:
...
CString GetFilename() {return CString(mFileName.GetBuffer());}
private:
CString mFileName;
};
The only thing that seems to solve the problem is to construct a new CString the way I showed in the workaround. What is DrawText doing when I pass the DT_MODIFYSTRING option?
First, note that CString can be used as a raw string pointer in two ways:
operator LPCSTR - gives a pointer which should never be modified.
GetBuffer - gives a pointer to memory specifically for the purpose of modifying the string.
Now, DrawText is declared to accept a LPCSTR. So when you pass a CString object directly as in your code, it implicitly uses operator LPCSTR to give the function what it says it wants, a constant string pointer.
However, DT_MODIFYSTRING says that DrawText can modify the string it was given. So internally, DrawText must be throwing away the constness of the pointer and modifying the string anyway.
This combination is a bad thing. But the fault is mainly in the implmentation of DrawText which is violating its own declaration.
As for why this modifies other CString objects: Apparently when a CString object is copied, it delays copying the internal string memory until something tries to modify the string through a CString member function. But until that happens, the operator LPCSTR of each CString object would still point to the same shared internal memory. This is normally fine, as long as any code using it is obeying the rules of const-correctness. However, as we've already seen, DrawText with DT_MODIFYSTRING is not playing by the rules. Thus, it is overwriting memory shared by multiple CString objects.
So to fix this problem, you either need to stop using DT_MODIFYSTRING if you don't actually need the modified text. Or else you need to pass the string to DrawText using filePath.GetBuffer() and then call filePath.ReleaseBuffer() afterwards.
Well there are some discrepancies in the code that you posted:
In 'class File':
GetFileName() {return mFileName;}
There is no return type here? Also in the FileContainer class you define the stored 'File' object as a pointer, but in the GetFileName function you access it as if it was not a pointer?
File* mFile;
...
mFile.GetFileName();
As far as why this is happening well right now I can't really tell. Another work around however would be to change the GetFileName function to return a const ref, this should ensure that the returned value can never be changed.
const CString& GetFileName() { return mFileName; }