file read() const correctness - c++

Assume I am writing a class to wrap windows' file HANDLE to give functionality to read/write from files easily. The class has a functions called read(buffer& out) that reads data from the file.
The question is, does read() need to be const or not?
On one hand, it should be const because it doesn't change the file.
On the other hand, it shouldn't be const because it changes the HANDLE (the HANDLE to the file which shows where to read).
What do you think?

Not const. Modifies the file handle position.

It sounds like your class - I'll call it FileAccessor - is effectively orchestrating operations on the file. Say you want to have a function implementing some algorithm processing the file, and it wants to display some diagnostic information by calling output_diagnostics(const FileAccessor& h), what information could it reasonable display that couldn't interfere with the algorithm calling output_diagnostics? It could perhaps output the current position / bytes-offset in the file. A function to get that position should be const. There might be other things, such as whether the file's locked in some way, the file size or filesystem path - all seem fair to make const. But, if it does a read from the file it's modifying what the algorithm calling output_diagnostic can expect should it later do a read, a "truncate from the current position" and all manner of other operations, and for that reason the read function should not be const.

Related

Retrieve information about an open file

Can I retrieve information about a file previously opened with fopen() using only the pointer it returned?
The reason I ask is that I am trying to write a RAII-style wrapper class for FILE *s, and I want to make it as general as possible, and one of the functions I imagined for it was a copy-like operation, that would take a FILE * as an argument, and create a new reference to the same file.
Under POSIX, I can create a duplicate of a file descriptor with dup()/dup2(), and even get how the file is being accessed with fnctl()'s F_GETFL operation. However, even if I do that to the underlying descriptor of a FILE *, it isn't enough for guessing properties such as if the stream is text or binary (under POSIX, there no real difference, but I want to be general), or its orientation towards char- or wchar_t-based text.
So, is there is a way of learning about the stream I'm about to create a wrapper for, how far can I go, and how should I do it?
Thank you for you attention.

Is opening the SAME file in two different fstreams Undefined Behaviour?

This recently asked question has raised another interesting issue, as discussed in the comments to one of its answers.
To summarize: the OP there was having issues with code like that below, when subsequently attempting to read and write data from/to the two streams 'concurrently':
ifstream infile;
infile.open("accounts.txt");
ofstream outfile;
outfile.open("accounts.txt");
Although the issue, in itself, was successfully resolved, it has raised a question to which I cannot find an authoritative answer (and I've made some quite extensive searches of Stack Overflow and the wider web).
It is very clearly stated what happens when calling the open() method of a stream that is already associated with a file (cppreference), but what I cannot find an answer to is what happens when (as in this case) the file is already associated with a (different) stream.
If the stream is already associated with a file (i.e., it is already
open), calling this function fails.
I can see several possible scenarios here:
The second open call will fail and any attempted writes to it will also fail (but that is not the case in the cited question).
The second open call will 'override' the first, effectively closing it (this could explain the issues encountered in said code).
Both streams remain open but enter into a 'mutual clobbering' match regarding their internal file pointers and buffers.
We enter the realm of undefined (or implementation-defined) behaviour.
Note that, as the first open() call is made by an input stream, the operating system will not necessarily 'lock' the file, as it probably would for an output stream.
So, does anyone have a definitive answer to this? Or a citation from the Standard (cppreference will be 'acceptable' if nothing more authoritative can be found)?
basic_filebuf::open (and all things that depend on it, like fstream::open) has no statement about what will happen in this case. A filesystem may allow it or it may not.
What the standard says is that, if the file successfully opens, then you can play with it in accord with the interface. And if it doesn't successfully open, then there will be an error. That is, the standard allows a filesystem to permit it or forbid it, but it doesn't say which must happen. The implementation can even randomly forbid it. Or forbid you from opening any files in any way. All are (theoretically) valid.
To me, this falls even out of the 'implementation defined' field. The very same code will have different behaviour depending of the underlying filesystem or OS (some OSes forbid to open a file twice).
No.
Such a scenario is not discussed by the standard.
It's not even managed by the implementation (your compiler, standard library implementation etc).
The stream ultimately asks the operating system for access to that file in the desired mode, and it's up to the operating system to decide whether that access shall be granted at that time.
A simple analogy would be your program making some API call to a web application over a network. Perhaps the web application does not permit more than ten calls per minute, and returns some error code if you attempt more than that. But that doesn't mean your program has undefined behaviour in such a case.
C implementations exist for many different platforms, whose underlying file systems may handle such corner cases differently. For the Standard to mandate any particular corner-case behavior would have made the language practical only on platforms whose file systems behave in such fashion. Instead, the Standard regards such issues as being outside its jurisdiction (i.e. to use its own terminology, "Undefined Behavior"). That doesn't mean that implementations whose target OS offers useful guarantees shouldn't make such guarantees to programs when practical, but implementation designers are presumed to know more than the Committee about how best to serve their customers.
On the other hand, it may sometime be helpful for an implementation not to expose the underlying OS behavior. On an OS that doesn't have a distinct "append" mode, for example, but code needing an "open for append" could do an "open existing file for write" followed by "seek to end of file", an attempt to open two streams for appending to the same file could result in data corruption when one stream writes part of a file, and the other stream then rewrites that same portion. It may be helpful for an implementation that detects that condition to either inject its own logic to either ensure smooth merging of the data or block the second open request. Either course of action might be better, depending upon an application's purpose, but--as noted above--the choice is outside the Standard's jurisdiction.
I open the zip file as stream twice.The zip file contains some XML files.
std::ifstream("filename") file;
zipstream *p1 = new zipstream(file);
zipstream *p2 = new zipstream(file);
p1->getNextEntry();
auto p3 = p1.rdbuf();
autp p4 = p2.rdbuf();
Then see p3 address = p4 address, but the member variables are different between them. Such as _IGfirst.
The contents of one of the XML files are as follows:
<test>
<one value="0.00001"/>
</test>
When the contents of file are read in two thread at the same time.error happend.
string One = p1.getPropertyValue("one");
// one = "0001two"

How to design a file container in C++?

My plan is to build a fileContainer that consists mainly of a std::map that associate specific files to an ID. Each file has for attributes a ofstream, a path (string) and a few other information.
The problem is that an ofstream cannot be copied (Why copying stringstream is not allowed?) and cannot even be moved (Why can't std::ostream be moved?). One can therefore not create file objects to then insert them into the map of the fileContainer.
What I am trying to do is something like
file f(arguments...); // create a `file`
FC.insert(f); // Note the FC is a global singleton of class `fileContainer`
...
{
file& f = FC.getFile(fileID); // find a specific file in the file container
f.open();
f.write(s1);
f.write(s2);
f.write(s3);
f.close();
}
I fail to see how such functionality could be achieved without having to copy or move a stream. Can you help me out to build this type of functionality?
Response to #πάνταῥεῖ comment
My code can produce about 20 different types of files. The types of files (which I above call ID) that are actually being produce depends upon the user input.
My goal is to have a fileContainer in which, I insert file objects. Those file objects are created while I read the input. Each file object match to a file ID. For any given file object a single file is being produced but for others, several files are being produced (information that can be gathered only during the process).
During the process, I would just look at whether a given ID is present in the fileContainer and if it is, then I write to it the associated file.
I already have a working version of the code, the issue is that it does not compile on every machine (looks like some compilers are fine with moving streams while others aren't)
If you only need std::ofstream
No problem! These are moveable.
If you need any std::ostream
Since std::ostreams cannot be copied or moved, your only option is to go back in time and store pointers instead. Use dynamic allocation to get fine control over the lifetime of your stubborn stream objects.
Abstracting away your custom container, a basic example looks like this:
std::vector<std::unique_ptr<std::ostream>> container;
container.push_back(std::make_unique<std::ostringstream>());
container.push_back(std::make_unique<std::ofstream>("/tmp/lol"));
…and so forth.
Nice, huh? #c++

File is getting really big need to separate data into another file but also need to use private variables. How can I achieve this correctly?

So I have a huge (legacy) file, call it HUGE.cxx. I'm adding new feature, but the file is getting even more big. I tried to create different classes for different jobs, but for some task I need to access the private variables. Here is a rough draft of what is going on
//HUGE.h
class Huge{
NewFeature object;
//...more stuff
};
//HUGE.cxx
Huge::Huge(){
//imagine object keeps track of id->func callback
object.on('uniqueID1', boost::bind(&HUGE::onID1Clicked,this));
}
void Huge::onID1Clicked()const{ return satisfiesSomeCondition(); }
//called internally when user right clicks
void Huge::createPopup()const{
for itr = object.begin to end
callback = itr->second;
//if satisfies condition add to popupmenu
if(callback()) addToPopupMenu( itr->first );
}
//event handler
void Huge::event(id){
//oh uniqueID1 was clicked as a menu item in right click
case 'uniqueID1': doSpecificFunctionality(); break;
}
so you see, I have some dependencies going there, but the file is so big and so are my changes. Do you have any advice on further separating out into more files. I know I can add a friend declaration to Huge file and add another class, but wanted to avoid that option if possible.
Sounds like you actually need a major refactor, separating concerns into their proper places.
But, to solve your immediate problem, there's no particular reason why all of Huge needs to be defined in Huge.cxx. You can split the function definitions into separate files, as long as every function is defined somewhere.
You might end up with:
Huge.h
Huge-private.cxx
Huge-public.cxx
Or however it makes sense to split your code.
As long as all the .cxx files include HUGE.h, and all the used functions are declared there (which should be the case), you can split up the implementation in as many .cxx files as you want. You could even put each function into its own file.
To call a function, the compiler only needs to see the prototype from HUGE.h. Later, when all the compiled files are linked together, the linker will combine the code from the different object files as appropriate.
Serious advice: Learn about refactoring (http://refactoring.com) and design patterns.
Without seeing the whole thing, it is hard or impossible to tell you something really specific. You probably need an arsenal of refactoring ammunition. For some parts, extracting methods and merging common functionality is the right thing; for other parts, dependency inversion may be the tool of choice.
Beyond some critical mass of mud, a (clean) rewrite might be the sanest and most profitable thing to do: Begin with defining what the input and the expected output is (during that, write tests).

What does ifstream::open() really do?

Consider this code:
ifstream filein;
filein.open("y.txt");
When I use the open() function, what happens?
Does the file stream itself get opened?
or does the object's state change to open?
or both?
It's not clear if you want to know implementation details or standard requirements - but as for implementation details - it will call the underlying open system call on the operating system. On Linux for example this is called open. On Windows it is called CreateFile.
The filestream being open or closed is represented by it's state. So if you change the state to open, the filestream is now open. Like a doorway. If you open it, you've changed it's state to the open position. Then you can later close it, which involves changing it's state to the closed position. Changing its state to open and opening the stream are the exact same thing.
The std::ifstream is set up to own a std::filebuf which is a class derived from std::streambuf. The stream buffer is managing buffering for streams in a generic way and abstracts the details of how a stream is accessed. For a std::filebuf the underlying stream is an operating system file accessed as needed. When std::ifstream::open() is called this call is mainly delegated to std::filebuf::open() which does the actual work. However, the std::ifstream will clear() its state bits if the call to std::filebuf::open() succeeds and set std::ios_base::failbit if the call fails. The file buffer will call the system's method to allocate a file handle and, if successful, arrange for this file handle to be released in its destructor or in the std::filebuf::close() function - whatever comes first. When calling std::ifstream::open() with the default arguments the system call will check that the file exists, is accessible, not too many file handles are open, etc. There is an std::ios_base::openmode parameter which can be used to modify the behavior in some ways and when different flags are used when calling std::ofstream::open().
Whether the call to std::filebuf::open() has any other effects is up to the implementation. For example, the implementation could choose to obtain a sequence of bytes and convert them into characters. Since the user can override certain setting, in particular the std::locale (see the std::streambuf::pubimbue() function), it is unlikely that much will happen prior to the first read, though. In any case, none of the state flags would be affected by the outcome of any operation after opening the file itself.
BTW, the mentioned classes are actually all templates (std::basic_ifstream, std::basic_filebuf, std::basic_streambuf, and std::basic_ofstream) which are typedef'ed to the names used above for the instantiation working on char as a character type. There are similar typedefs using a w prefix for instantiations working on wchar_t. Interestingly, there are no typedefs for the char16_t and char32_t versions and it seems it will be a bit of work to get them instantiated as well.
If you think logically, ifstream is just the stream in which we will get our file contents. The parameters, we provide to ifstream.open() will open the file and mark it as open. When the file is marked as open, it will not allow you to do some operations on file like renaming a file as it is opened by some program. It will allow you to do the same after you close the stream. ifstream - imo is only the helper class to access files.