问题
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