How do I get a string or stream into a CStreamFile? - c++

this question may seem a bit too specific, but I figure I'd give it a shot here since I've found some great programming answers here in the past.
I am modifying the open-source program TinyCad for a project I'm working on. I've visited the TinyCad message board and posted, but I didn't get the answer I'm looking for. I'm having trouble wrapping my head about how to integrate a small XML converter class I wrote into the loading function of TinyCad.
A little background about me: I have no experience with MFC or Visual Studio, but that is what I have to use. I am used to C++ and was taught using iostream syntax (cout, cin, new, etc.) so I'm not used to older C code (like printf, sprintf, malloc, alloc, etc.) either. I usually write my programs from start to finish in Qt, but I was told that for this project I should modify an existing program to save time. I don't know if it'll save that much time if I have to learn something totally foreign, but I digress.
I wrote a small class to read in an XML file that is structured differently than the XML file that TinyCad reads in. My class converts it and outputs an intermediate XML file. Well, I don't want to spit out an intermediate file. I modified it to save the output as a string (using the string datatype from the standard C++ iostream library). I want to get this string into a stream so that TinyCad can open the file, do the conversion, and then continue loading.
My class is called like so:
std::string blah;
char* filename = "library.xml";
XMLopen myXML(filename, blah);
So it takes in a filename, opens the file, parses the relevant information out of the file, puts the information into TinyCad's XML structure, and saves the XML code as a string that has been passed by reference.
I had an idea to use istringstream to make a stream, but that did not play nice with CFile. I tried it like so:
istringstream ins; // Declare an input string stream.
ins.str(blah);
// First open the stream to save to
CFile theFile(ins);
Below is the code in TinyCad that opens and loads the selected XML file:
void CLibraryStore::LoadXML( const TCHAR *filename )
{
// First open the stream to save to
CFile theFile;
// Open the file for saving as a CFile for a CArchive
BOOL r = theFile.Open(filename, CFile::modeRead);
if (r)
{
CString name;
// Create the XML stream writer
CStreamFile stream( &theFile, CArchive::load );
CXMLReader xml( &stream );
// Get the library tag
xml.nextTag( name );
if (name != "Library")
{
Message(IDS_ABORTVERSION,MB_ICONEXCLAMATION);
return;
}
xml.intoTag();
CTinyCadApp::SetLockOutSymbolRedraw( true );
while ( xml.nextTag( name ) )
{
// Is this a symbol?
if (name == "SYMBOL")
{
// Load in the details
xml.intoTag();
CTinyCadMultiSymbolDoc temp_doc;
drawingCollection drawing;
CLibraryStoreNameSet s;
// this is where the stream gets sent to be loaded into the data structure
s.LoadXML( &temp_doc, xml );
xml.outofTag();
// ... and store the symbol
Store( &s, temp_doc );
}
}
xml.outofTag();
CTinyCadApp::SetLockOutSymbolRedraw( false );
}
}
Edit 7/28/2010 5:55PM
So I tried to make a stream, but it fails.
CStreamFile takes in a filename and then gets set as a CArchive:
m_pArchive = new CArchive( theFile, nmode );
I tried to make a CStream like so (since CStreamFile is an overloaded CStream):
CString test = blah.c_str();
CStreamMemory streamCS;
streamCS << test;
CXMLReader xml( &streamCS );
But at streamCS << test; it doesn't put the stream in at all. test gets assigned correctly with blah so I know that's working.
Any ideas on how to approach this?

Related

How to handle file I/O outside of main when redirecting input and output?

A restriction of the program I am working on is that it should be invoked as: ./a.out < input.txt > output.txt. The input of this program should be read from the first file, and the output should be written to the second.
So, this redirects standard input and output from and to these two files. I could simply, from main() for example, call std::cin and std::cout. However, I have a dedicated component which adapts my input from a file to an intermediate structure that I use elsewhere in my program.
In order to build this struct I could #include <iostream> in this component and read with std::cin from input.txt. However, I don't like the idea of including iostream here, and I am not sure how I would test this.
My issue comes from the I/O redirect, if the executable were invoked with filenames as strings, I would do something along the lines of
InputAdapter inputAdapter;
ifstream infile;
infile.open(filename ,std::ios_base::in);
auto structHoldingParsedInput = inputAdapter.adapt(infile);
How can I achieve something similar here?
I would suggest you make your adapter parameters std::istream& and std::ostream& so you can pass in either the standard std::cin/std::cout or files you open yourself like std::ifstream.
A bit like this:
class InputAdapter
{
public:
void adapt(std::istream& in)
{
// code to convert input to output here
return created_object;
}
};
// ...
InputAdapter inputAdapter;
std::ifstream in("input_file");
auto structHoldingParsedInput = inputAdapter.adapt(in);
Now you are coding to streams rather than files you can use any stream, for example the standard input stream:
auto structHoldingParsedInput = inputAdapter.adapt(std::cin);
And, for testing you could use std::istringstream:
std::istringstream test_stream(R"(
put your test data in here
)");
auto structHoldingParsedInput = inputAdapter.adapt(test_stream);

How to get stream object identified by a FILE?

Documentation states that FILE is object type that identifies a stream. So, is it possible to get the stream object associated with a FILE?
For example, I'd like to get std::cout object from stdout FILE pointer, or std::cerr from stderr etc. More generally I want to write a function that redirects a given stream and sets the custom streambuf to it, something like this:
void redirect(FILE* file, std::ios stream) {
freopen_s((FILE**)file, "CONOUT$", "w", file);
stream.rdbuf(customBuffer);
}
used to redirect streams
redirect(stdout, std::cout);
redirect(stderr, std::cerr);
It seems redundant to have 2 parameters, since both parameters are always associated with each other.
The C++ standard library includes the C standard library. A FILE is a C stream, which is quite a different animal than a C++ iostream. It is possible for an std::stream implementation to rely of an underlying FILE, but this is not required by the standard, and even in that case there is no way to retrieve it.
What is possible is to build a custom std::streambuf that explicitly uses an underlying FILE *, and use it in a std::stream. std::basic_streambuf is one of the few classes from the C++ standard library that is explicitely designed as a base class for custom derivation. Unfortunately I could not find a tutorial for it, but the class contains a number of virtual methods that you just have to override. It is not exactly an easy path, but is possible with some works, heavy testing, and eventually some help from SO if you get stuck somewhere. But a full implementation is far beyond a SO answer.
TL/DR: there is no underlying std::stream associated with a FILE but with some work you can build a custom stream_buffer that will use an underlying FILE *. Though those are rather advanced operations...
While it is not possible to cleanly do this in C++ you could do something like this.
FILE * file = popen("someFile")
const unsigned BUFF = 2048;
string total;
bool done = false;
while (!done) {
vector<char> cBuf[BUFF];
size_t read = fread((void *)&cBuf[0], 1, BUFF, f);
if (read)
{
total.append(cBuf.begin(), cBuf.end());
}
if (read < BUFF)
{
done = true;
}
}
pclose(f);
istringstream filey(total);
Hope this helps.

Segmentation fault when calling fread() c++

I dont understand the mistake I am making.
I tryed alot but I am unable to read my FILE.
Basically I write an structure into a file named 0.txt / 1.txt / 2.txt ... based of account amound.
I realy seached hours to fix my problem but I dont understand how I can fix and why I get the ERROR.
Also I have no problem in complining my code (with dev c++) but when I press on Load Accounts Button I get the ERROR "Segmentation Fault" (using windows 7).
I noticed that the problem is at fread() line in function ladeAccounts().
The name of my Structure is "iAccount".
The variable infoma is as iAccount typed and the "number of accounts existing" typed as int anzahl in newAccount() decides the path.
iAccount looks like this:
struct iAccount
{
string ID;
string password;
int level;
};
This is how I write my STRUCT into the FILE:
void Account::newAccount(int anzahl, string username, string pw, int lvl)
{
iAccount neu;
neu.ID = username;
neu.password = pw;
neu.level = lvl;
ss.str("");
ss<<anzahl;
s = ss.str();
s = "Accounts/"+s+".txt";
f1 = fopen(s.c_str(), "w");
fseek(f1, 0, SEEK_SET);
fwrite(&infoma, sizeof(iAccount), 1, f1);
fclose(f1);
}
This is how I read the File (ERROR APPEARS when I call fread()
void Account::ladeAccount(int nummer)
{
stringstream sa;
iAccount account_geladen;
sa.str("");
sa<<nummer;
s = sa.str();
s = "Accounts/"+s+".txt";
f2 = fopen(s.c_str(), "r");
fseek(f2, 0, SEEK_SET);
fread(&infoma, sizeof(infoma), 1, f2);
fclose(f2);
}
Thank you for your help. I have no clue where my problem is and as I said I am searching for hours.
EDIT:
The file gets opened I tryed it (f2 is true!).
EDIT":
ERRNO = 0 !!!
SEE HERE:
ostringstream Str;
Str << errno;
infoma.ID = Str.str();
Just did this to see the result of errno in my wxtextlabel.
Reason
You are most probably calling fread on a NULL file handle. So you have two problems here:
In your code (you don't check if fread succeeds or returns a NULL value)
Your file can't be opened for some reason (this, you should investigate...)
Explication
fopen (see documentation) can return a NULL handle for different reasons. If you don't check the validity of the handle before calling fread you will have a segmentation fault.
Tips
As you can read in the official documentation I linked above, on most library implementations the errno variable can help you giving the system-specific error code on failure. This could help you debugging your error in opening the file.
Side Issues
Once you solve this bug in our code you will have other issues. As people (notably #Christophe) remarked in other answers, there is a structural problem in your code because you try to serialize/deserialize on your file objects non POD (aka your strings). Since string are complex objects you can't serialize them directly.
The approach of using an array of characters will work correctly, as simple types can be handled the way you coded.
For this reason, you can use the std::string c_str() method to obtain a null terminated array of chars from your string and store it in the file.
The opposite operation is even more straightforward, as you can initialize a std::string simply passing the deserialized array of chars:
std::string str(the_array);
You have a problem because you use fread() to load binary data. But this works only with plain old data (POD) objects.
It uses to give desastrous results with less trivial objects especially if the internals of these manage dynamic memory allocaton and/or pointers like it's the case here with strings.
By the way:
If you read/write binary data, you should really use "rb"/"wb" as mode for fopen(). If you don't you would'nt necessary have a seg.fault, but your data might be incorrect on some systems.
Edit:
Sorry, I didn't read well enough: if it happens right at fread() the reason provided by Alex will certainly help. However I leave this answer because as soon as you've solved your fopen() issue, you might get segmentation errors if you try to work with the object that you've read. If you're not conviced, look at sizeof(iAccount) and compare it to the size your string content.
EDIT
if(f2) is true so I am wrong and file got opened successfull right?
I found out that the file is not opened/the fopen can not handle with the path for example 0.txt .
Also I tryed to enter the path directly without building it (without stringstream and so on). Still I have the problem of the segmentation fault. I checked everything the file exists in the folder Accounts. I have an other file called "Accounts.txt" in the same folder and there I have no problem reading the amound of accounts existing (also using a struct). There I dont even check if the fopen had success but it works anyway I will write the code for the file-open-check later.
The code for the reading/writing into Accounts/Accounts.txt is:
struct init{
int anzahl_1;};
init anzahl;
FILE* f;
static string ss = "Accounts/Accounts.txt";
int account_anzahl1()
{
f = fopen(ss.c_str(), "r");
fread(&anzahl, sizeof(init), 1, f);
fseek(f, 0, SEEK_END);
fclose(f);
return anzahl.anzahl_1;
}
void account_anzahl_plus()
{
anzahl.anzahl_1 = anzahl.anzahl_1 +1;
f = fopen(ss.c_str(), "w");
fwrite(&anzahl, sizeof(init), 1, f);
fclose(f);
}
There I have no problem!

Trash characters when using buffers in c++

I have a DLL that I need to handle in C++. I'm using WxWidgets (standard compilation, but I also tried Unicode on/off) and NetBeans. I also tried dealing with this without WxWidgets (windows.h) and had same problems.
Here is how I access the DLL functions using WxWidgets:
// -------------------- POINTERS TO FUNCTIONS
typedef bool(*TYPE_DLL_SetLicense)(char*, char*);
typedef bool(*TYPE_DLL_PingConnection)(char*);
typedef char*(*TYPE_DLL_ERR_DESCRIPTION)(void);
class DLL_Library
{
public:
// pointers to functions inside dll
TYPE_DLL_SetLicense DLL_SetLicense; //initialize - will wor fine as it returns only true/false (buffer only provide data)
TYPE_DLL_PingConnection DLL_PingConnection; //ping to serwer. Will return trahs, becouse it uses buffer to provide data ang get answear back
TYPE_DLL_ERR_DESCRIPTION DLL_ERR_DESCRIPTION; //error description. No buffer, no trouble. Returns correct string.
wxDynamicLibrary dynLib2;
int initialize(void)
{
//patch to dll
wxString path = wxStandardPaths::Get().GetExecutablePath().BeforeLast('\\') + _("\\DLL_dll\\DLLMOK.dll");
if(!wxFile::Exists(path)) return -1;
//load dll
if(!dynLib2.Load(path)) return -2;
//Assign functions in dll to variable
DLL_SetLicense=(TYPE_DLL_SetLicense) dynLib2.GetSymbol(wxT("DLL_SetLicense"));
DLL_PingConnection=(TYPE_DLL_PingConnection) dynLib2.GetSymbol(wxT("DLL_PingConnection"));
DLL_ERR_DESCRIPTION=(TYPE_DLL_ERR_DESCRIPTION) dynLib2.GetSymbol(wxT("DLL_ERROR_DESCRIPTION"));
return 0;
}
};
And here is the function I run. It should return and XML content, that I try to save to the file.
//DLL_PingConnection
//result ping to be save in file
wxFile file_ping_xml;
plik_ping_xml.Open(wxT("C:\\dll\\ping.xml"),wxFile::write);
char buffor_ping_xml[2000];
//I run the function here
bool is_ping = DLL_PingConnection(buffor_ping_xml);
if(is_ping)
{
tex_box->AppendText(wxT("DLL_PingConnection True\n"));
//we save result to file
bool is_write_ping_ok = file_ping_xml.Write(buffor_ping_xml,2000);
if (is_write_ping_ok){tex_box->AppendText(wxT("Save to file is ok ok\n"));}
else {tex_box->AppendText(wxT("Save to file failed :( \n"));}
}
else
{
tex_box->AppendText(wxT("DLL_PingConnection False\n"));
}
std::cout << "Error description: " << DLL_ERR_DESCRIPTION() << "\n"; //will work fine both in saving to file, and in streaming to screen.
The problem is that inside the file instead of good content I get rubbish like this:
NOTE that this only happens in functions that use buffers like:
char buffer[2000] //buffer will contain for example file xml
function do_sth_with_xml(buffer) //buffer containing xml will (should) be overwriten with xml results of the function - in our case DLL_PingCONNECTION should save in buffer xml with connection data
Documentation say that the DLL operates on Windows-1250. File ping.xml I have set to windows ANSI, but I don't think problem lies here.
EDIT: I have written problem without WxWidgets (I load DLL using windows.h) - same problems. Here is the code: Getting trash data in char* while using it as buffer in function . Please help :(
This
DLL_PingConnection=(TYPE_DLL_PingConnection)
shouldn't it be
DLL_PingConnection=(TYPE_DLL_PingConnection) dynLib2.GetSymbol(wxT("DLL_PingConnection"));
?
seems otherwise you will not get a valid pointer to the function in the DLL.
as a general rule you should check return values, especially from a DLL
you load dynamically since it happens that you sometimes get another version
of the DLL which may have a function with same name but other signature or
where is missing entirely.
You named a function
DLL_PingConnection=(TYPE_DLL_PingConnection) dynLib2.GetSymbol(....
and call it with
OSOZ.OSOZ_PingConnection(buffor_ping_xml);
you typedef a function
typedef bool(*TYPE_DLL_PingConnection)(char*);
you create a variable
char buffor_ping_xml[2000];
in your typedef it is char* and your buffor_ping_xml is char
how can that work ?
try
char *buffor_ping_xml = new char[2000];
/* or */
wchar_t *buffor_ping_xml = new wchar_t[2000];
/* or */
wxChar *buffor_ping_xml = new wxchar[2000];
bool is_ping = DLL_PingConnection(buffor_ping_xml);
wxString mystring = wxString::FromUTF8(buffor_ping_xml);
write mystring to file.
To Do:
look in your wxwidgets\libs folder for your libs
are there libwxmsw29ud_* with a 'u' in the name (after version number here 29)?
If not You can not use unicode
If yes next steps
for all different test char *, wchar_t *, wxChar * give the files different name.
for example file_ping_xml.Open(wxT("C:\dll\ping_w_t_FromUTF8.xml"), ...
for wchar_t * in combination with
wxString mystring = wxString::FromUTF8(buffor_ping_xml);
also in combination with
wxString mystring(buffor_ping_xml);
Then check out the look like, of the files in a browser .
To test you can go to your wxWidgets sample folder . Compile in the folder C:\wxWidgets\samples\docview\docview.cpp . Open with docview.exe a unicode file . How does it look.
Unicode download file
Unicode-related compilation settings
You should define wxUSE_UNICODE to 1 to compile your program in Unicode mode. This currently works for wxMSW, wxGTK, wxMac and wxX11. If you compile your program in ANSI mode you can still define wxUSE_WCHAR_T to get some limited support for wchar_t type.
Here is answear: Getting trash data in char* while using it as buffer in function.
Thanks everyone - expecially for patience.

Write data into .txt file created by CFileDialog, in C++

I wanna Write data into .txt file created by CFileDialog, in C++.
The problem I am facing is that below codes doesn't work, although there is no build error. The .txt file created by CFileDialog can not be found for some reason. What's wrong the code?
what's the efficient way to Write data into .txt file created by CFileDialog, in C++?
Thanks
CFileDialog dlg(FALSE, NULL, NULL, OFN_OVERWRITEPROMPT,
_T("My Data File (*.txt)|*.txt||"));
if(dlg.DoModal() != IDOK)
return;
CString filename = dlg.GetPathName();
ofstream outfile (filename);
int mydata = 10;
outfile << "my data:" << mydata << endl;
outfile.close();
Why are you trying to use ofstream when you using MFC? You could use a CFile isn't it? Any specific reason why you are using ofstream?
Without knowing about some settings I can only do a qualified guess.
E.g. depending on how you compile this, UNICODE or !UNICODE the CString behaves differently, i.e. maps to etiher CStringA or CStringW. The CString also behaves differently depending on the MFC version, in some cases there is a operator to implicit convert to a c string, in some not.
An ofstream normally expects a const char* as argument, so you may want to change it to
ofstream outfile(filename.GetBuffer(255));
in that case.
EDIT:
Did you check if you could open the file? from the above code it seems you assume success...
if ( outfile.is_open() )
...