首页 > 解决方案 > C++ 从 iostream 故障位集继承

问题描述

从哪里开始?开始吧,我猜。

我是套接字编程的新手。我想试一试。经过研究,我发现似乎没有标准的 C++ oop socket 库(有一些第三方库可能是 oop,但我还是决定自己制作,因为这样更有趣,因为它只是无论如何都是个人项目),所以我决定自己做。

我决定将我的新库与 iostream 集成。当然,这通常可能不是一个好主意,但我认为 1)我有一个正当的理由 2)无论如何这只是一个学术练习。至少学会如何去做不会有什么坏处。

因此,我做了一点挖掘并想出了这个设计:

class NetworkStream : public std::iostream {
    public: 
        // class network streambuff
        // should handle the actual writing
        class NetworkBuff : public std::streambuf {
            private:
                SOCKET socket; // I'm working with winsock2, btw
            public:
                NetworkBuff(SOCKET s);

            protected:
                // out

                virtual std::streamsize xsputn(const char* buffer, std::streamsize size);

                virtual std::streamsize overflow(char buffer);

                virtual int underflow();

                virtual std::streamsize xsgetn(char* buffer, std::streamsize size);

        };

        // constructors
        NetworkStream(SOCKET socket); // sets up the socket in NetworkBuff
        virtual ~NetworkStream();

    }; // end network stream

这个 NetworkStream 对象应该处理网络上的实际读写。连接由更高级别的代码维护(我的意思是从 NetworkStream 继承的对象)。该代码正在运行(据我所知,无论如何)并且与我的问题无关,所以我省略了它。我只是提到它,以便您了解我的设计。

无论如何,我的新流对象的实际实现如下所示:

// class network streambuff
// should handle the actual writing

//constructor
NetworkStream::NetworkBuff::NetworkBuff(SOCKET s) {
    socket = s;
}

// out

std::streamsize NetworkStream::NetworkBuff::xsputn(const char* buffer, std::streamsize size) {
    // well, let's send the data
    int result = send(socket,buffer,static_cast<int>(size),0);

    // if that didn't work, throw an error
    if(result == SOCKET_ERROR) throw("Send Failure: " + WSAGetLastError()); 

    // NOTE: I realized after I wrote this that this throw may be useless, 
    // since I think iostream catches any errors thrown at this level, but 
    // just in case

    // and pass through to streambuff, because, well, it can't hurt
    return std::streambuf::xsputn(buffer, size);
}

// basically do the same thing as before...
std::streamsize NetworkStream::NetworkBuff::overflow(char buffer) {
    // well, let's send the data
    int result = send(socket,buffer,sizeof(buffer),0);

    // if that didn't work, throw an error
    if(result == SOCKET_ERROR) throw("Send Failure: " + WSAGetLastError()); 

    // and pass through to streambuff, because, well, it can't hurt
    return std::streambuf::overflow(buffer);
}

据我所知,这就像一个魅力。我单步调试我的调试器,我xsputn()的被调用了。所以我可以做这样的事情:

std::string data = "Hello, world!";
networkstream << data;

它被调用。我认为它发送成功。它没有通过我的调试器,结果没有错误。但是,我还没有完全测试它,因为它是我的接收功能不起作用:

std::streamsize NetworkStream::NetworkBuff::xsgetn(char* buffer, std::streamsize size) {
    // well, let's read the data
    int result = recv(socket,buffer,static_cast<int>(size),0);

    // if that didn't work, throw an error
    if(result == SOCKET_ERROR) throw("Receive Failure: " + WSAGetLastError());

    // Now this I think is wrong, specifically comparing against SOCKET_ERROR.
    // That's not my problem, though. My problem is that this function seems to 
    // never get called, so a wrong comparison doesn't matter yet anyway

    // and pass through to streambuff, because, well, it can't hurt
    return std::streambuf::xsgetn(buffer, size);
}

// Now this guy, I'm pretty extra sure is probably wrong, but it's what I got. I 
// couldn't find a good example of using underflow, so I did my best from the 
// research I did find
int NetworkStream::NetworkBuff::underflow() {
    // well, let's read the data
    int result = recv(socket,gptr(),sizeof(*gptr()),0);

    // if that didn't work, throw an error
    if(result == SOCKET_ERROR) throw("Recieve Failure: " + WSAGetLastError());

    // and pass through to streambuff, because, well, it can't hurt
    return std::streambuf::underflow();
}

它编译得很好。但是,当我尝试使用它时:

std::string data = "";
networkstream >> data; // should wait for data on the network

好像什么都没发生似的。不,实际上,根据我的调试器,它似乎设置了一个失败位,忽略了我虚拟重载的函数,并继续执行,就像什么都没发生一样。

所以,在一段中,我的问题是:我的 underflow/xsgetn 函数到底有什么问题导致它失败并基本上忽略了我的代码?我确定我做错了什么,但究竟是什么?

标签: c++inheritanceiostreamwinsock2

解决方案


事实证明,我不能只是通过。这不是 streambuf 的设计方式。

据我所知(如果我错了,请有人纠正我),streambuf 实际上名副其实。它应该是流的缓冲区。

无论如何,对我来说不清楚的是streambuf,虽然它应该处理缓冲,但它没有实际的内部保存或处理数据的方式。这就是重载它的全部意义所在。你应该处理缓冲。你不能通过(再次,如果我错了,有人纠正)通过父母。这不是它的设计方式。

你需要的(或者我需要的,我想)是一个很好的下溢实现并正确地实现它。幸运的是,经过更多的挖掘,我能够找到一个。长话短说,我做了这样的事情:

// Shoutout to http://www.voidcn.com/article/p-vjnlygmc-gy.html where I found out
// how to do this proper
std::streambuf::int_type NetworkStream::NetworkBuff::underflow() {
    const int readsize = 30;

    // first, check to make sure the buffer is not exausted:
    if(gptr() < egptr()) {
        return traits_type::to_int_type(*gptr());
    }

    // Now, let's read...

    // btw, inputbuffer is a data member that is a std::string
    // clear the buffer
    inputbuffer.clear();
    inputbuffer.resize(readsize);

    // let's read
    int bytesread = recv(socket,&inputbuffer[0],static_cast<int>(readsize)); 

    // return the end of file if we read no bytes
    if(bytesread == 0) {
        return traits_type::eof();
    }

    // set the pointers for the buffer...
    setg(&inputbuffer[0],&inputbuffer[0],&inputbuffer[readsize-1]);

    // finally, let's return the data type
    return traits_type::to_int_type(*gptr());

}

推荐阅读