C ++ Наследование от набора битов iostream - PullRequest
0 голосов
/ 16 мая 2019

С чего начать? Начало, я полагаю.

Я новичок в программировании сокетов. Я хотел дать ему шанс. После исследования я обнаружил, что, похоже, нет стандартной библиотеки сокетов C ++ oop ( было несколько сторонних библиотек , которые могли бы быть 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, которая приводит к ее сбою и по существу игнорирует мой код? Я уверен, что я делаю что-то не так, но что именно?

1 Ответ

0 голосов
/ 21 мая 2019

Как оказалось, я не могу просто пройти.Это не так, как устроен streambuf. * ​​1001 *

Как я могу сказать (и кто-то, пожалуйста, поправьте меня, если я ошибаюсь), streambuf фактически соответствует своему названию.Предполагается, что он является буфером для потока.

Что мне неясно, так или иначе, так это то, что streambuf, хотя он должен обрабатывать буферизацию, не имеет фактического внутреннего способа хранения или обработки данных.,Вот и весь смысл его перегружать.ВЫ должны справиться с буферизацией.Вы не можете передать (опять же, кто-то поправит, если я ошибаюсь) до родителя.Это просто не так, как это задумано.

То, что вам нужно (или мне нужно, я полагаю), было хорошей реализацией underflow и правильно ее реализовывать.К счастью, после нескольких копаний я смог найти один.Короче говоря, я пошел с чем-то вроде этого:

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

}
...