file of integer
, file of real
, etc.). In some cases (with in-depth study of programming in schoolchildren or junior university), when the study of the C ++ language begins, there is a desire to solve problems for processing “T-type files” already in the new environment of this language. And then the question arises, what means of this use.read
/ write
methods of the standard stream types istream
/ ostream
. In addition to other obvious flaws, this fact does not allow the full use of STL-style programming (that is, first of all, part of the standard C ++ library related to algorithms and iterators).T
, as with the STL sequence ( vector<T>
, etc.). For T
, meanings of basic types are implied, as well as the so-called POD-types (everywhere you can only think of basic types, if you are not familiar with the concept of POD).ios_base::binary
, but those who met, know well that this tool will not help in any way. The language standard is extremely brief on what effect can be expected from specifying this flag when opening a stream, but you can find an explanation in the network that it simply disables the platform-specific translations of newline characters and some more characters, which is not has a direct relationship to our task.ofstream
/ ifstream
file streams are synonymous with the explicit instances of the basic_ofstream
/ basic_ifstream
. These templates have two similar type parameters: the type of characters of the stream (we call this parameter Ch
) and the type of characteristics of the type of characters — the default is std::char_traits<Ch>
. For ofstream
/ ifstream
, the type char
taken as Ch
.T
we want to read from a binary file, specifying it as the value of the template parameter Ch
. The simplest code that tries to read from this type of stream values ​​of type int
falls with the exception of the runtime bad_cast
. Perhaps something could be changed by writing my own specialization for char_traits<int>
and transferring it with the second parameter to the class template of the file stream, however this path seemed to be unpromising (to write the specialization of a very extensive template char_traits
for each type T
...) and I did not deal with it further.ofstream
/ ifstream
, saving the maximum definitions from the ancestors, and see what happens. (I note in parentheses that this task in itself is not devoid of meaning, if only because it is marked by a rather high rating of difficulty in the list of exercises from the book by B. Straustrup - exercise 15, paragraph 21.10 in the third and special editions of the book C ++ ".)<<
and >>
for its thread classes was obvious. It seemed to be enough. The problem arose in the following. To work with a stream using standard library algorithms, you should use input / output iterators. According to the statements of the authors of STL, its means are all generalized from themselves and I expected that as soon as my flow class meets some implicit library requirements, it will gladly work with it — static polymorphism ... In particular, I expected the standard iterators to be parameterized by the flow type, with whom they work. Not here it was! In the definition of the I / O iterator patterns, the standard types basic_ofstream
/ basic_ifstream
are basic_ofstream
- basic_ifstream
.<<
and >>
: for the base types, they are implemented as member functions of the flow class templates. If, in addition, they were declared virtual, then one could rely on dynamic polymorphism (standard iterators would store objects of my streams by reference to the base class) - a partial solution of the original problem would work, which worked only for basic types (int, double etc.). However, these member functions are not virtual. Here one could speculate about the logic of the standard library device or the lack thereof (for example, it is known that STL did not initially assume to use the full power of OOP, inheritance and polymorphism, but the stream library is built on OOP ...), but let's move on to the final solution.<<
and >>
operations, which would hide the low-level work with files using read
/ write
. It is enough to provide your overload of these operations and make sure that she was called. This can be achieved by using special types in the arguments. We have already failed to manipulate the types of threads - it remains to come up with special types for input / output. This suggests the use of what is called "wrappers."T
: we can restrict ourselves to one class template that stores the field of type parameter T
and that can be converted to a reference to this field - constant and non-constant. A constructor with one parameter of type T
, which is not declared explicit
, will allow implicitly converting values ​​of type T
to a wrapper type. Parameterless constructor is an STL requirement. The resulting code is shown below. #include <iostream> using std::istream; using std::ostream; template<typename T> class wrap { T t; public: wrap() : t() {} wrap(T const & t) : t(t) {} operator T&() { return t; } operator T const &() const { return t; } }; template<typename T> istream & operator>>(istream & is, wrap<T> & wt) { is.read(reinterpret_cast<char *>(&static_cast<T &>(wt)), sizeof(T)); return is; } template<typename T> ostream & operator<<(ostream & os, wrap<T> const & wt) { os.write( reinterpret_cast<char const *>(&static_cast<T const &>(wt)), sizeof(T)); return os; }
static_cast
requires the compiler to call the type conversion operation defined in the class template body to get a link to the information field, and reinterpret_cast
leads the address of this field to a pointer to char
, preparing us for low-level work with read
/ write
. #include <algorithm> #include <fstream> #include <functional> #include <iostream> #include <iterator> #include <numeric> #include <cassert> int main() { int arr[] = {1, 1, 2, 3, 5, 8}; // std::ofstream out("f.dat"); std::copy(arr, arr + 6, std::ostream_iterator< wrap<int> >(out)); out.close(); // : , std::ifstream in("f.dat"); assert( std::inner_product( std::istream_iterator< wrap<int> >(in), std::istream_iterator< wrap<int> >(), arr, true, std::equal_to<int>(), std::logical_and<bool>()) ); }
read
/ write
. Perhaps there are those who say that binary files are the past, that they are terribly intolerable, or something similar. Maybe this is partly so, but the exercise itself in solving such a task seemed to me interesting and informative. std::ifstream in("f.dat"); int arr2[6]; std::copy(std::istream_iterator< wrap<int> >(in), std::istream_iterator< wrap<int> >(), arr2);
Source: https://habr.com/ru/post/134788/
All Articles