RAII when using the stream reassignments

More GUI elements than streambuf and RAII to clean up

this section shouldn't be visible 
 

 

Just before the holiday, another mail. As already announced, I just want to use more controls for output, on the other hand, manual cleanup is still necessary so far. I explained that this is not necessary thanks to RAII and can be prevented by C++ itself in the post RAII - mouse pointer for VCL + FMX. So in this post I want to start to design a RAII wrapper. Further controls I will look at in the next post.

With our RAII- Wrapper the creation of the concrete auxiliary classes and the assignment should be taken over by the specialization of a template method Activate(), and the necessary temporary variable should be stored as a private element. This is used to restore the previous state when leaving the scope. 

This results in the following class.

later add a link to RAII - Mpointer for VCL + FMX


class TMyStreamWrapper {
   private:
     std::ostream&   str;
     std::streambuf* old;
   public:
     TMyStreamWrapper(std::ostream& ref) : str(ref) { old = 0; }
     ~TMyStreamWrapper(void) {
       if(old != 0) delete str.rdbuf(old);
       }

     void Reset(void) {
        if(old != 0) {
           delete str.rdbuf(old);
           old = 0;
           }
        }

     template <typename ty> void Activate(ty* element);

     operator std::ostream& (void) { return str; };

   private: // pre C++11
     TMyStreamWrapper(TMyStreamWrapper const& ref) : str(ref.str) { };

     void Check(void) { if(old != 0) throw std::runtime_error("Attention, prior activation"); }
   };

template<>
inline void TMyStreamWrapper::Activate<std::streambuf>(std::streambuf* buff) {
   Check();
   old = str.rdbuf(buff);
   }

#if defined BUILD_WITH_VCL || defined BUILD_WITH_FMX
template<>
inline void TMyStreamWrapper::Activate<TMemo>(TMemo* element) {
   Check();
   old = str.rdbuf(new MyMemoStreamBuf(element));
   }

#endif

template<typename ty>
inline void TMyStreamWrapper::Activate(ty* element) {
   throw std::runtime_error("no specification for TMyStreamWrapper::Activate");
   return;
   }

Modifications of the previous implementation for VCL- TListView

Let's take a closer look at the class "TMyStreamWrapper". We define two data elements. The first one is a reference to an ostream "str", thus also to any class derived from it, and represents the stream to be redirected. Second, we define an "old" pointer to the previous streambuf element of the stream, which is cached. In the constructor, we pass a reference to an ostream as a parameter.  This is to be reassigned. Since it is a reference, the assignment must be made to the data element "str" in the constructor list, i.e. before the opening curly bracket of the function block. The second data element "old" is initialized with 0. I have decided here for compatibility with C++98, should you only use a current compiler, this can and should of course be nullptr. Or you can use the conditional compilation to distinguish between the assignment of 0 and nullptr.

In the destructor the previous cached data element "old" is used to restore the previous state. For this purpose it is checked whether the data element is not equal to 0,
and then the previous state is restored by calling rdbuf() and the passed data element is deleted.

The method Reset() resets the previous state and deletes the previously passed streambuf object. This makes it possible to make a new assignment.

The template Activate() takes over this assignment and forms the most important part of it. There are later the specializations, which are implemented as explicit inline methods below the class. Specializations extend the concept of the templates considerably and provide us with many new possibilities. But it is amazing that many do not use them. 

What is important is the method that takes a pointer to a streambuf object, and thus can take any object derived from streambuf. It is a universal access to this wrapper. The final part is the general template definition (without specialization), but this will cause a runtime error. Everything between these two can be added later, in the above listing it is the redirection to a memo field already done by us using the class MyMemoStreamBuf from the post Use of standard streams for output in memo fields.

The function operator is used to be able to use the redirected stream from outside. Therefore a reference to ostream is simply returned here, with the data element "str".

The check method is used to prevent multiple assignment during activation. If the data element "old" is not equal to 0, a runtime error is triggered here.

Finally, the copy constructor. With C++11 I could simply delete it, but because I wanted to stay compatible to older compilers, I decided to use the well known trick to prevent that a copy of the instance is initiated from outside. Therefore the copy constructor is simply moved to the private part, so that only the methods of the class itself could create copies. Again, if you only use a current compiler, please delete the copy constructor.

Now we can use this wrapper to undo the assignment at the end of the program and make sure that no errors occur. 

Attention, add link to use standard streams for output in memo fields

Kommentar verfassen

* These fields are required

Kommentare

Keine Kommentare

About the Author

 


Über den Autor

Volker Hillmann

CEO, Developer, Germany, Berlin
Volker Hillmann was born in 1965 and holds a degree in mathematics with a focus on databases and data security. He has been programming in C since 1988. After first touches on a Unix machine with Turbo C 1.5 on PCs. That's how he got to know C++ in 1991 and since then he is programming in different areas with C++. After some experience in the insurance and banking industry, he founded adecc Systemhaus GmbH in 2000, of which he is still the CEO. He is also MVP at embarcadero Germany.

More posts by this author