How to override ostream << operator for already defined type

馋奶兔 提交于 2019-12-25 16:41:30

问题


I'm currently working on a port of an existing library which makes use of ostream to write to a terminal.

The ostream was derived as part of the port.

The ostream derivative class used is defined as so:

class owstream: public std::ostream {
public:
    CTerminal * output;
    giac::context * contextptr;
    owstream(CTerminal *, giac::context * contextptr , int = 0);
    virtual ~owstream();
};

This is used to output some data, typically integers and doubles.

The problem is that the platform I'm working on has a buggy double print routine that causes kernel crash.

So in some cases, if I do:

ostream* mystream = new ostream(...);
(*mystream) << 1.23456

kaboom

so, I tried to override the << operator for certain type, like so:

ostream* GetStream() { return = new MyOStream(...); }

....

ostream* mystream = GetStream()
mystream << 1.23456;

Unfortunately, the operator<< member in ostream isn't virtual so if I created an overridden operator<< member would never be called.

I tried to extend it with something like:

class owstream: public std::ostream {
    friend std::ostream& operator<<(std::ostream& out, double val);
  public:
    CTerminal * output;
    giac::context * contextptr;
    owstream(CTerminal *, giac::context * contextptr , int = 0);
    virtual ~owstream();
};

extern std::ostream& operator<<(std::ostream &out, double val);

But this causes compilation error in regards to operator << being ambiguous, as obviously this is a type already handle by the base iostream class.

I'm starting to wonder if this is at all possible.

How would you implement the << operator to override the behaviour when given a particular, already handled type ?

The aim being able to do something like:

cout << 1.234567

not crashing (I obviously wouldn't be using cout, but GetStream() as defined above, could very well return cout)


回答1:


I see three problems with your current approach/implementation.

Problem #1

Even if you were to successfully get it to use std::ostream as the first parameter and return value it would not work correctly. The problem stems mainly from returning an ostream from the overloaded operator<<. After the first value has been sent to the owstream all subsequent values get sent to the returned ostream. This puts you right back where you started with the original problem. In order to for this to work properly you need to take owstream as the first parameter and return an owstream in the overloaded operator<<.

Problem #2

Another problem is owstream is implicitly convertible to std::ostream. Depending on how owstream is used in your project there is a possibility that the overloads you provide may not make a difference in certain situations. For instance if an object of type owstream is passed to a function that accepts a std::ostream you may end up encountering the problem you are currently experiencing. You can prevent this from happening by using private inheritance. This will prevent any implicit use of owstream as a std::ostream. Using private inheritance also has the benefit of preventing you from unknowingly using functions in std::ostream which may lead you back to your original problem. For instances where a std::ostream is absolutely necessary you can use an accessor function to explicitly retrieve a reference to it.

Problem #3

The last issue is that std::ostream includes an overload of operator<< that handles std::ostream specific IO manipulators such as std::endl. If you do not provide an overload to specifically handle these the one in std::ostream gets used and once again you end up right back where you started. If you use private inheritance described above and do not provide an overload to handle the manipulators it will fail to compile.


Solution

The solution below is similar to the one provided by JRG and includes the accessor function and overloads for double, float, and std::ostream IO manipulators. It is a complete working example and uses the stream buffer from std::cout for simplicity. I included the overload for float as the deficient library implementation may a similar issue with them. It also uses private inheritance to prevent implicit conversions to std::ostream.

I tested it on VC++10 and GCC 4.7.2. You may need to make some adjustments depending on how compliant your compiler and libraries are.

#include <ostream>
#include <iostream>
#include <sstream>

class owstream : private std::ostream
{
public:
    owstream(std::streambuf* sb)
        : std::ostream(sb)
    {}

    // Template based operator...ohhhhhh ahhhhh.
    template <typename T>
    friend owstream& operator<<(owstream&, const T&);

    // Additional overload to handle ostream specific io manipulators 
    friend owstream& operator<<(owstream&, std::ostream& (*)(std::ostream&));

    // Accessor function to get a reference to the ostream
    std::ostream& get_ostream() { return *this; }
};


template <typename T>
inline owstream&
operator<<(owstream& out, const T& value)
{
    static_cast<std::ostream&>(out) << value;
    return out;
}

//  overload for double
template <>
inline owstream&
operator<<(owstream& out, const double& value)
{
    std::stringstream ss;
    ss << value;
    return out << ss.str();
}

//  overload for float
template <>
inline owstream&
operator<<(owstream& out, const float& value)
{
    std::stringstream ss;
    ss << value;
    return out << ss.str();
}

//  overload for std::ostream specific io manipulators
inline owstream&
operator<<(owstream& out, std::ostream& (*func)(std::ostream&))
{
    static_cast<std::ostream&>(out) << func;
    return out;
}

int main()
{
    owstream ows(std::cout.rdbuf());

    ows << std::endl;
    ows << "hello " << 1.0 << " " << 2.0f << std::endl;
}



回答2:


What about something like this:

template <typename T>
owstream& operator<<(owstream&, const T&);

template <typename T>
inline owstream&
operator<<(owstream& os, const T& val)
{
   std::ostream& stdos = static_cast<std::ostream&>(os);
   stdos << val;
   return os;
}

template <>
inline owstream&
operator<<(owstream& os, const double& val)
{
   std::stringstream ss;
   ss << val;
   os << ss.str();
   return os;
}


来源:https://stackoverflow.com/questions/17132289/how-to-override-ostream-operator-for-already-defined-type

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!