C ++ binary input/output stream interface design, binary input/output

Source: Internet
Author: User

C ++ binary input/output stream interface design, binary input/output

When it comes to input and output streams, as CPPer, it is natural to think of std: iostream. For text stream processing, iostream can be said to be powerful enough to deal with general complexity requirements without pressure. Binary stream processing can only be described as "simple". Sadly, as a programmer who has worked hard for many years in the multimedia software field, the most common thing is binary stream.

A popular book in the past few years is called what men come from Mars and women come from Venus. Similarly, if the text stream comes from Mars, the binary stream comes from Venus. For a text stream, we may expect such an interface function:

1 string text = stream. get_line (); // basic interface 2 string word = stream. get_word (); // enhancement Interface

For binary streams, the interface we expect may look like this:

1 int read_bytes = stream. read (buffer, size); // basic interface 2 int value = stream. read_int32 (); // enhancement Interface

As the insert/extract operator ("<"/">") of iostream soul, the overload is a God-like existence for text streams, but it is completely unavailable in binary streams, because the binary stream needs to be precise to the control of byte (or even bit), there will be the following interface:

Int v1 = stream. read_uint16 (); // read short int v2 = stream. read_int16 (); // read unsigned short intint v3 = stream. read_uint24 (); // read 3-byte unsigned integer, which is a 3-byte 24-bit int v4 = stream. read_int24 (); // think about how abnormal this function is!

The operator overloading Based on the derivation during compilation is difficult to meet similar requirements. In my opinion, it is not worth the candle to merge the two methods into the same class. There is almost no similarity between the core requirements and the non-core requirements are irrelevant, basically, there will be no text stream or binary stream. Iostream is just doing this. For this, only (wo) can (cuo) OH (le) OH (ma.

Binary streams can be divided into input streams and output streams (nonsense) in the direction of the stream. At another latitude, binary streams can be divided into sequential access streams and random access streams, the main difference between the two is whether positioning operations (seek) are supported. The former is not supported, and the latter is supported. For example, standard input and output streams are sequential access streams, file streams generally allow random access to the stream. At a higher level, the sequential access stream has a built-in Time Arrow, neither turning back nor jumping. A Random Access stream is a built-in space axis, no directionality (or weak directionality). If you want to, you can read from the end of a file to the header. Therefore, real-time streams with Time Attributes are generally sequential access streams, such as the data streams generated by recording and screen recording, such as live streams of online live videos.

We can design a newbility architecture by combining two types of dimensions and two categories:

 

Haha, is it very powerful ...... Afraid ?...... If you are afraid, this is just an abstract interface class. If you consider implementation classes and various derivatives, the complexity of the system will be increased by at least two times.

......

The figure above is a joke. The system in the figure is a typical hypothetical abstraction, and it cannot even be used for over-design. Although exaggerated, in reality, it is not a system that has not made similar errors. For example, the implementation code inside the base classes of DirectShow is quite a bit of a god-like place (to say this, how deep is my resentment against DirectShow ......).

The first step to solve any problem is to simplify the problem, that is, to grasp the main contradiction and ignore the secondary contradiction.At least for some time, CPPer was particularly keen on exquisite design, and exquisite design often first complicate the simple problem, do you still remember the Classic C ++ edition Hello World? The purpose of the exquisite design is to reuse the code, but the reality is: the simpler the code, the easier it is to reuse, and the more elaborate the code, the more complicated it is to reuse. Back to our questions, we must first find the main contradiction, that is, the core needs:A group of simple input and output streams that can handle most daily tasks,Note that "input and output streams" are used instead of "input and output streams ". In fact, in actual development work, there are few requirements for a stream, that is, the input stream and the output stream. If it is encountered, it is often because of complicated business requirements, in this case, it is not unacceptable to write a stream specifically for special needs.

Therefore, the requirement of "both the input stream and the output stream" was cut down as a secondary contradiction. We have not yet considered the clear inheritance relationship and temporarily cut down the derivative extensions such as the file stream, the rest of the system is simple and clear:

There are only four isolated abstract classes. The output stream is similar to the input stream but not associated with it. Therefore, we take the input stream as an example to further investigate the two abstract classes: random_istream and sequential_istream. As mentioned above, the main difference between a sequential access stream and a random access stream is that a sequential access Stream does not support location operations (seek) and a random access stream. That is to say, if sequential_istream is designed as follows:

1 class sequential_istream2 {3 public:4     virtual void read(void* buffer, int bytes) = 0;5 };

Then random_istream is like this:

class random_istream{public:    virtual void read(void* buffer, int bytes) = 0;    virtual void seek(int offset) = 0;};

Nothing found? Random_istream is the superset of sequential_istream, which means that random_istream can be inherited from sequential_istream. It neither needs to be designed to be two isolated classes nor to forcibly extract a public base class for two classes for general purpose. In terms of concept, it is also perfect. a Random stream can be accessed as a Sequential stream. This is a typical "is-a" relationship. The adjusted design is as follows:

 1 class input_stream 2 { 3 public: 4     virtual void read(void* buffer, int bytes) = 0; 5 }; 6  7 class random_istream : public input_stream 8 { 9 public:    10     virtual void seek(int offset) = 0;11 };

Here, the sequential keyword of the ordered stream is removed to make the concept inheritance logic smoother.

In fact, there is another design scheme that can design input_streamFat interface)And supports both sequential streams and random access streams:

1 class input_stream2 {3 public:    4     virtual void read(void* buffer, int bytes) = 0;5     virtual void seek(int offset) = 0;6     virtual bool seekable() const = 0;              7 };

Note the seekable method. It returns a Boolean value indicating whether the seek method is valid. Valid values indicate that this is a random stream and Sequential stream.

We didn't adopt this scheme. Although it seems simple to have a lower level of inheritance relationship, the actual application is not simpler than the previous scheme. Seekable is a semanticStatus attribute(Do you have this argument) indicates the object'sLevel 1 StatusA boolean can represent two States. Each increase doubles the application complexity and increases exponentially (don't trust me, I'll just talk about it ). Although there is only one state attribute, it is enough to cause us a lot of troubles:

  • Is the status returned by seekable temporary or permanent? Is it possible to change midway through? At least we cannot see the answer from the interface;
  • If seekable returns false, what if the seek method is still called?
  • A derived class of an ordered Stream does not need to implement the seekable and seek methods at all, even if it simply returns false and throws an exception;
  • Each time you try to call the seek method, you must first call seekable to determine, and finally there will be a bunch of if-else;

The above problem is also inherent in the fat interface, so we must use the fat interface with caution. This time we chose to abandon it.

 

 

 

Add a variety of derived classes and helper classes to obtain the final structure:

 

 

 

 

So far, our work has been basically completed, and the rest are boring physical work.

Input_stream and random_istream interfaces:

 1 class input_stream 2 { 3 public: 4     virtual void        read(void* buffer, uint32_t bytes)                  = 0; 5     virtual void        skip(uint32_t bytes)                                = 0; 6     virtual uint64_t    tell() const                                        = 0; 7 }; 8  9 10 class random_istream : public input_stream11 {12 public:13     virtual void        seek(int64_t offset, seek_origin origin)            = 0;14     virtual uint64_t    size() const                                        = 0;15 };

Note that the skip method is used to skip the specified number of bytes During sequential read operations. The function can also be achieved by discarding data after reading. In random_istream, you can directly use the seek method, the final decision to add this method is mainly for convenience, and the efficiency is equivalent to the optimal substitution method of the current stream.

 

Output_stream and random_ostream:

 1 class output_stream 2 { 3 public: 4     virtual void        write(void* buffer, uint32_t bytes)                    = 0; 5     virtual void        flush()                                                = 0; 6     virtual uint64_t    tell() const                                           = 0; 7 }; 8  9 class random_ostream : public output_stream10 {11 public:12     virtual void        seek(int64_t offset, seek_origin origin)               = 0;13 };

 

Binary_reader and binary_writer are extensions of input_stream and output_stream. External extensions are more flexible than inheritance extensions. If inheritance extension is used, does binary_reader inherit from input_stream or random_istream, or designs binary_reader as an independent interface class, and implements classes such as file_istream inherit both binary_reader and random_istream? These are tangled issues, and every solution is not perfect. The external extension method is perfect and easy to implement. You only need to insert an input_stream pointer to binary_reader, as shown below:

1 input_stream * ist =... 2 3 binary_reader reader (ist); 4 5 int v1 = reader. read_uint8 (); 6 7 reader. skip (4); 8 int v2 = reader. read_uint16_be (); 9... 10 11 // The seek operation can also support 12 13 random_istream * ist =... 14 15 binary_reader reader (ist); 16 17 int v1 = reader. read_uint8 (); 18 19 ist-> seek (4, see_origin: current); 20 int v2 = reader. read_uint16_be (); 21...

 

The complete declaration of binary_reader is roughly as follows:

 1 class binary_reader 2 { 3 public: 4     binary_reader(input_stream* stream); 5  6     void read(void* buffer, uint32_t read_bytes); 7     void skip(uint32_t offset); 8  9     uint64_t tell() const;10 11     uint8_t read_uint8();12 13     uint16_t read_uint16_be();14     uint32_t read_uint24_be();15     uint32_t read_uint32_be();16     uint64_t read_uint64_be();17 18     uint16_t read_uint16_le();19     uint32_t read_uint24_le();20     uint32_t read_uint32_le();21     uint64_t read_uint64_le();22 23         ....24 25 private:26     input_stream*    _stream;27 };

 

......

It took a week to finish writing this article. It is not that easy to explain a simple design scheme. This solution, especially the design of specific interface functions, is still not perfect, and needs to be gradually improved in the process of practical application. Of course, it will not be too difficult to make improvements in a simple solution. Next, I want to write down what is behind this solution-error handling. Specifically, it is an exception-based error handling solution.


C \ c ++ input/output stream

Stream is an image.
For example: cin> I> j. It is the input data to I and j, Which is intuitively derived from the cin object.
Cout <I <j; outputs I and j to the object cout.
"<" And ">" is a special function, operator <(). If it is named output,
Then cout <I <j; can be expressed as output (cout, I), j );
However, <seems more concise.

There is no xuanjicang in the stream, that is, some classes used for input and output.

Standard input/output stream c/c ++

In C, the input and output statements are implemented using scanf () and printf (), while in C ++

Implemented Using classes.

# Include iostream. h
In main () // C ++, the main () function is of the int type by default, while in C language, the main () function is of the void type by default.
{
Int;
Cout <cin> a;/* enter a value */
Cout <return 0;
}

Cin, cout, and endl objects are not part of the C ++ language. Although they are already

The SI Standard C ++ is defined, but they are not an internal component of the language. Not provided internally in C ++

Is different from other languages. The input and output are implemented through the C ++ class.

, Cin and cout are examples of these classes, which are implemented outside the C ++ language.
In the C ++ language, there is a new annotation method, that is, '//'. All the descriptions after this line // are

It is considered by the compiler as a comment and cannot be wrapped in a line break. The comments of the traditional C language are retained in C ++.

Style /*...... */.
C ++ can also adopt the format output method:

# Include iostream. h
Int main ()
{
Int;
Cout <cin>;

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.