0

我目前正在开发一个现有库的端口,该库利用 ostream 写入终端。

ostream 是作为端口的一部分派生的。

使用的 ostream 派生类定义如下:

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

这用于输出一些数据,通常是整数和双精度数。

问题是我正在使用的平台有一个错误的双打印例程,导致内核崩溃。

所以在某些情况下,如果我这样做:

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

卡布姆

所以,我试图覆盖特定类型的 << 运算符,如下所示:

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

....

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

不幸的是,ostream 中的 operator<< 成员不是虚拟的,因此如果我创建了一个重写的 operator<< 成员,就永远不会被调用。

我试图用类似的东西来扩展它:

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);

但这会导致关于 operator << 不明确的编译错误,因为显然这是一个已由基 iostream 类处理的类型。

我开始怀疑这是否可能。

当给定一个特定的、已经处理的类型时,你将如何实现 << 运算符来覆盖行为?

能够执行以下操作的目标:

cout << 1.234567

不会崩溃(我显然不会使用 cout,但上面定义的 GetStream() 可以很好地返回 cout)

4

2 回答 2

1

像这样的东西怎么样:

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;
}
于 2013-06-16T13:22:27.570 回答
1

我发现您当前的方法/实施存在三个问题。

问题 #1

即使您成功地将其std::ostream用作第一个参数并返回值,它也无法正常工作。问题主要源于ostream从重载的operator<<. 在第一个值被发送到owstream所有后续值之后,都被发送到返回的ostream. 这会让您回到最初问题的起点。为了使其正常工作,您需要将owstream其作为第一个参数并owstream在重载的operator<<.

问题 #2

另一个问题是owstream隐式转换为std::ostream. 根据owstream您在项目中的使用方式,您提供的重载可能不会在某些情况下产生影响。例如,如果将类型的对象owstream传递给接受 a 的函数,std::ostream您最终可能会遇到当前遇到的问题。private您可以通过使用继承来防止这种情况发生。这将防止任何隐式使用owstreamas a std::ostream。使用private继承还有一个好处是可以防止您在不知不觉中使用std::ostream可能导致您回到最初问题的函数。对于实例,其中std::ostream是绝对必要的,您可以使用访问器函数显式检索对它的引用。

问题 #3

最后一个问题是包含处理特定 IO 操作符std::ostream的重载,例如. 如果你不提供一个重载来专门处理这些,那么你就会再次使用它,你最终会回到你开始的地方。如果您使用上述继承并且不提供重载来处理操纵器,它将无法编译。operator<<std::ostreamstd::endlstd::ostreamprivate


解决方案

下面的解决方案与JRG提供的解决方案类似,包括访问器函数和doublefloatstd::ostreamIO 操纵器的重载。这是一个完整的工作示例,为了简单起见,它使用了流缓冲区std::cout。我将重载包括在内,float因为有缺陷的库实现可能与它们有类似的问题。它还使用私有继承来防止隐式转换为std::ostream.

我在 VC++10 和 GCC 4.7.2 上对其进行了测试。您可能需要根据编译器和库的兼容程度进行一些调整。

#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;
}
于 2013-06-16T21:41:02.013 回答