4

If i wrote this line of code:

std::thread t(EchoServer(socket));

How can the compiler interpret this instruction? It can be a function declaration or simply an initialization. I've the followinc code:

#include <iostream>
#include <thread>
#include <boost/asio.hpp>

#include <boost/asio.hpp>

typedef boost::asio::ip::tcp::socket Socket;
auto socket_deleter = [] (Socket* s) {s->close(); delete s;};
typedef std::unique_ptr<Socket, decltype(socket_deleter)> socket_ptr;

class EchoServer {
public:
    static void Listen(unsigned int port)
    {

        using namespace std;
        using namespace boost::asio;

        io_service ios;

        // create an endpoint to listen to a certain port
        ip::tcp::endpoint endpoint(ip::tcp::v4(), port);

        cout << "Listening to TCP Socket on port " << port << " ..." << endl;

        // Start opening a socket
        ip::tcp::acceptor acceptor(ios, endpoint);

        // this loop must be infinite... but we accept only 3 connections
        auto socket = socket_ptr(new Socket(ios));

        std::thread t(EchoServer(socket));
    }

    EchoServer(socket_ptr&& s) : m_socket(std::move(s))
    {
    }

    void operator ()() const
    {
    }

private:
    socket_ptr m_socket;
};

But the compiler give me the following warning:

C4930: 'std::thread t(EchoServer(socket))': std::thread t(EchoServer(socket)) function not called (was a variable definition intended?).

So how can I explain that this line is an object of type std::thread creation instead a function declaration.

UPDATE 1: I'm using visual studio 2012 that don't support uniform initialization so I've changed the code from std::thread t((EchoServer(socket))); to std::thread t((EchoServer(socket))); but this time I've a compil time error that i don't understand:

error C2440: '<function-style-cast>': cannot convert from 'std::unique_ptr<_Ty,_Dx>' to 'EchoServer'

What am I missing?

UPDATE 2 I've probably have to understand better move-semantic the problem is with the declaration of socket_ptr. I've changed the code in this (ugly) way... but now compiles.

#include <iostream>
#include <thread>
#include <boost/asio.hpp>

typedef boost::asio::ip::tcp::socket Socket;
auto socket_deleter = [] (Socket* s) {s->close(); delete s;};
/*
typedef std::unique_ptr<Socket, decltype(socket_deleter)> socket_ptr;
*/
typedef Socket* socket_ptr;

class EchoServer {
public:
    static void Listen(unsigned int port)
    {

        using namespace std;
        using namespace boost::asio;

        io_service ios;

        // create an endpoint to listen to a certain port
        ip::tcp::endpoint endpoint(ip::tcp::v4(), port);

        cout << "Listening to TCP Socket on port " << port << " ..." << endl;

        // Start opening a socket
        ip::tcp::acceptor acceptor(ios, endpoint);

        // this loop must be infinite... but we accept only 3 connections
        auto socket = new Socket(ios);

        std::thread t((EchoServer(socket)));
    }

    EchoServer(socket_ptr s) : m_socket(s)
    {
    }

    ~EchoServer()
    {
        m_socket->close();
        delete m_socket;
    }

    void operator ()() const
    {
    }

private:
    socket_ptr m_socket;
};

Changine socket_ptr as a simple pointer instead of an unique_ptr the code works.

Elvis Dukaj
  • 7,142
  • 12
  • 43
  • 85

5 Answers5

7

It's a function declaration. If you want to declare an object with direct initialization, you can use one of the following methods:

std::thread t(EchoServer { socket });
std::thread t { EchoServer(socket) };
std::thread t { EchoServer { socket} };
std::thread t((EchoServer(socket)));

Brace-initialization is unambiguously initialization, and in the last line you have a parenthesized expression, which cannot work as a function parameter declaration.

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
5

As you mentioned, there are two possible interpretations of that statement, but the standard explicitly mandates that in the case of this ambiguity the compiler has to interpret the statement as a function definition (dropping the extra set of parenthesis):

std::thread t(EchoServer socket);

If you want to force that to be the creation of a std::thread you can add an extra set of parenthesis that make the statement not a valid function declaration:

std::thread t((EchoServer(socket)));

Use a different syntax for the initialization:

std::thread t = std::thread(EchoServer(socket));

Or since you are using C++11 you can use uniform initialization:

std::thread t{EchoServer(socket)};

The first two options are valid C++03, but in a C++11 compiler you should probably use the third option (and the use of std::thread indicates that you are using C++11 features)

David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
  • 1
    That third snippet is not just a different syntax for the initialization, it's a different kind of initialization entirely. – Ben Voigt Dec 06 '12 at 01:26
  • @BenVoigt: Each of the three is slightly different initialization, and in this particular case they amount to the same thing. The first is *direct-initialization* and only requires a constructor that takes a `EchoServer`, the second is *copy-initialization*. §8.5/15 actually makes the third case the *same* as the first case, and also names it *direct-initialization*, although in §12.6.2/2 it states that this syntax will have *list-initialization* semantics for objects of class type, and in particular *direct-list-initialization*. – David Rodríguez - dribeas Dec 06 '12 at 12:47
  • [...] the semantics are the same as *direct-initialization* unless the type has a *initializer-list-constructor* (a constructor whose first parameter is a `std::initializer_list<>` and no other parameter without a default value). Since that is not the case in `std::thread`, in this case *direct-list-initialization* is equivalent to *direct-initialization* (i.e. will call the same constructor). Whether this detailed description is appropriate for the answer or not is a different question. I originally considered this out of scope, but here it is :) – David Rodríguez - dribeas Dec 06 '12 at 12:49
  • I was more worried about the *copy-initialization* case, which has additional requirements. It works here because `std::thread` does have a move constructor. – Ben Voigt Dec 06 '12 at 12:52
3

In C++ 2011 the easiest way in the given situation is to replace the parenthesis by braces:

std::thread t{EchoServer(socket)};

Note, however, that this is guaranteed to call std::terminate() because the thread is neither detached nor joined.

Dietmar Kühl
  • 150,225
  • 13
  • 225
  • 380
  • The problem was excatly that. I couldn't call t.detach() – Elvis Dukaj Dec 05 '12 at 23:41
  • In general, I'd think you actually don't *want* to call `detach()` anyway because it just means you have a rogue thread you can't clean up properly. ... but this is a separate issue ;) – Dietmar Kühl Dec 05 '12 at 23:45
1

static_cast can be used to trigger user-defined conversions, so try

std::thread t(static_cast<EchoServer>(socket));

instead of constructor-call syntax for the cast.

To fix the conversion failure, change your constructor to:

EchoServer(socket_ptr&& s) : m_socket(s)
{
}
Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • socket is not of the same type of EchoServer so it cannot compile – Elvis Dukaj Dec 06 '12 at 00:41
  • 1
    @elvis: Did you try it? It doesn't need to be the same type, it just needs to be convertible... which was also required for the code you were trying to write. – Ben Voigt Dec 06 '12 at 01:25
  • Reading my own post almost 9 years later it's so cool :D! @BenVoigt I accepted the reply and it's the best solution :D – Elvis Dukaj Nov 30 '21 at 08:15
1

C and C++ compilers must build semantic knowledge about declarations and scope as they parse, and consult that knowledge in order to know how to parse something.

When an identifier is scanned, it can be converted to a token whose lexical category based on how that identifier has been declared with respect to the scope in which the scanning is taking place.

An example which is even simpler than yours is this:

A ( B );

This might be a function call: function A is being called with an argument which is the value of primary expression B. Or, it might be a declaration of name B, which is to be an object of type A.

If our lexical analyzer is able to peer into the declarations that are visible in the current scope, it can determine whether A is a type name, or whether it is declared as a function. Then, it can pass the appropriate kind of token to the parser, and so the appropriate phrase structure rule will match.

Kaz
  • 55,781
  • 9
  • 100
  • 149