Question

To realize the idiom for the type SOCKET, I have created a wrapper. The wrapper calls connect in the constructor and closesocket in its destructor. A std::map holds all used sockets. Unfortunatelly inserting a new socket into the container calls the destructor of a temporary and, in fact, closes the just opened socket. Is there a common way to overcome this?

Here is the code:

#include <iostream>
#include <stdexcept>
#include <map>
#include <winsock2.h>

struct Socket {
    SOCKET mSock;
    Socket() : mSock(INVALID_SOCKET) {}
    Socket(std::string ip, int port);
    ~Socket();
};

Socket::Socket(std::string ip, int port) {
    mSock = socket(AF_INET, SOCK_STREAM, 0);
    if (mSock == INVALID_SOCKET)
        throw std::runtime_error("socket()");
    SOCKADDR_IN addr = {0};
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = inet_addr(ip.c_str());
    if (connect(mSock, reinterpret_cast<SOCKADDR*>(&addr), sizeof(addr))
        == SOCKET_ERROR)
        throw std::runtime_error("connect()");
    std::cout << mSock << " connected" << std::endl;
}

Socket::~Socket() {
    if (mSock != INVALID_SOCKET) {
        closesocket(mSock);
        std::cout << mSock << " closed" << std::endl;
    }
}

int main() {
    WSADATA wsa;
    WSAStartup(MAKEWORD(2, 0), &wsa);
    std::map<int, Socket> outbound;
    // calls constructur but also destructor
    outbound[0] = Socket("192.168.128.125", 4023);
    WSACleanup();
    return 0;
}

And the Output is:

1952 connected
1952 closed
1952 closed
Was it helpful?

Solution

Resource guards may be stored in STL containers if you wrap them in a smart pointer. Which smart pointer to use exactly is up to your environment, I'd recommend to go with boost::shared_ptr as a starting point if you're not sure.

Following that way you will conform with both resource guard semantics (only one instance that manages resource's lifetime) and STL container (keep equivalent copy of passed item).

OTHER TIPS

As others have mentioned, you should be preventing the copying of your Socket object, which can be done by adding:

private:
  Socket(const Socket &){}
  Socket & operator=(const Socket &){return *this;}

Then to actually connect, you could do so after default construction with the implementation of a connect method, which could be called explicitly, or by the remaining constructor:

bool Socket::connect(std::string ip, int port) {
    mSock = socket(AF_INET, SOCK_STREAM, 0);
    if (mSock == INVALID_SOCKET)
        return false;
    SOCKADDR_IN addr = {0};
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = inet_addr(ip.c_str());
    if (connect(mSock, reinterpret_cast<SOCKADDR*>(&addr), sizeof(addr))
        == SOCKET_ERROR)
        return false;
    std::cout << mSock << " connected" << std::endl;
    return true;
}

Socket::Socket(std::string ip, int port) {
    if (!connect(ip, port))
        throw std::runtime_error("socket()");
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top