0


I am trying to implement Websocket client using Cpprest sdk (Casablanca). I am successful in establishing the Web Socket connection and able to send/recieve messages. The response is received as an instance of websocket_incoming_message, which has a method, extract_string() whose definition is,

_ASYNCRTIMP pplx::task<std::string> web::websockets::client::websocket_incoming_message::extract_string (       )   const

Hence it returns, const string.

I am trying to assign this string to a function local string variable, so that I can return it to the calling method. However, I receive the below error,

error: passing ‘const string {aka const std::basic_string<char>}’ as ‘this’ argument of ‘std::basic_string<_CharT, _Traits, _Alloc>& std::basic_string<_CharT, _Traits, _Alloc>::operator=(const std::basic_string<_CharT, _Traits, _Alloc>&) [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]’ discards qualifiers [-fpermissive]

Below is my code block,

   std::string WebSocketUtility::send_command(const char* command){

    websocket_outgoing_message o_msg;
     std::string ws_response;

    o_msg.set_utf8_message(command);
    client->send(o_msg).then([](){
        log("Message Sent!!!\n");
    });

    client->receive().then([ws_response](websocket_incoming_message i_msg){ 
           return i_msg.extract_string();
        }).then([ws_response](std::string ws_body){ 
                 ws_response = ws_body;  
        });

   log("WS_RESPONSE:%s:\n",ws_response.c_str());
    return ws_response;
  }

I have tried
1.Declaring ws_response as a reference
2.Capturing ws_response as a reference in lambda
3.Declaring ws_response as cpprest json, and assigning the response as a json field (ws_response[U("output")] = json::value::string(ws_body.c_str()))

with no luck.

I do not have much coding experience in C++ and struggling with this.

Could someone please help me on how to capture this response outside the lambda so that I can return the value to the calling method?

Thanks & Regards,
Swathi Desai

  • [Possible duplicate](https://stackoverflow.com/questions/5501959/why-does-c11s-lambda-require-mutable-keyword-for-capture-by-value-by-defau). – PaulMcKenzie Mar 30 '20 at 18:04
  • "*Hence it returns, const string*" - that is not what the `const` means in this context. It means `extract_string()` itself is `const`, ie it can be called on a `const websocket_incoming_message` object. The actual return type of `extract_string()` is `pplx::task` (not `std::string`), there is no `const` on the return value. `pplx::task` has a `get()` method to get the actual `std::string`. – Remy Lebeau Mar 30 '20 at 23:03
  • I think the error says that there is a const std::string & A assigned to a std::string & B which is not allowed because a modification of B would also modify A. The line is already complex. Doesn't give the compiler the position? Is _ASYNCRTIMP pplx::task maybe converted to a non const reference? – Ernie Mur Mar 30 '20 at 23:17
  • 1
    @ErnieMur The `const` that the OP refers to is the `const` at the end of the `extract_string()` declaration. Re: the error message, what you describe is not what the error means, either. The error is complaining that `operator=` is being called on a `const std::string` as the destination, not the other way around. And besides, modifying B after assigning to it does not also modify A, that is not how `std::string` works. And `_ASYNCRTIMP` is not part of the return type, it is just a macro that resolves to `__declspec(dllimport|dllexport)`. The return type is simply `pplx::task` – Remy Lebeau Mar 30 '20 at 23:22
  • You mean this, which generates a similar error message here with gcc:: const std::string a; a = someOtherString; ? – Ernie Mur Mar 30 '20 at 23:30
  • @ErnieMur yes, that is basically what is happening here. – Remy Lebeau Mar 31 '20 at 00:03

1 Answers1

1

[ws_response](websocket_incoming_message i_msg){ return i_msg.extract_string(); }

There is no reason for the above lambda to capture ws_response at all, as it does not use ws_response for anything.

[ws_response](std::string ws_body){ ws_response = ws_body; }

The above lambda needs to capture ws_response by reference in order to modify it properly. The way the lambda is currently coded, it captures a copy of ws_response by value, and that copy is const since the lambda is not declared as mutable. This is why you are getting the compiler error - you are trying to assign ws_body to a const std::string object that is local to the lambda, not to the ws_response variable that is declared in send_command(). The above lambda is roughly equivalent to this:

const std::string ws_response_copy = ws_response;
ws_response_copy = ws_body; // <-- ERROR

Try this instead:

client->receive().then([](websocket_incoming_message i_msg){ 
    return i_msg.extract_string();
}).then([&ws_response](std::string ws_body){ 
    ws_response = ws_body;  
});

Alternatively, extract_string() returns a pplx::task<std::string>, which has a get() method to retrieve the actual std::string, waiting until it is available. So the above can be simplified to the following:

client->receive().then([&ws_response](websocket_incoming_message i_msg){ 
    ws_response = i_msg.extract_string().get();
});
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • Nice explanation .I have never used lambda in C++ yet therefore I tried to figure out where the error could be (before I read your answer). I could not find because ws_response is not defined const and it is not obvious to me the there is a const copy made somewhere in between. You have made two modifications. I guess the first use of ws_response without any assignment would not have caused any error? It is only the second lambda? – Ernie Mur Mar 30 '20 at 23:58
  • @ErnieMur Yes, it is the second lambda that is failing to compile, which your compiler should have pointed out to you. Have a look at [this reference](https://en.cppreference.com/w/cpp/language/lambda) for what a lambda is and how it works. – Remy Lebeau Mar 31 '20 at 00:05
  • Thank you for the suggestions. I had tried capture by reference earlier. Compilation would succeed, however the value was not getting captured in ws_response. I was printing the ws_response outside lambda, and it was empty all the time. "Alternatively, extract_string() returns a pplx::task, which has a get() method to retrieve the actual std::string, waiting until it is available.", this triggered an idea that, I may not be waiting for the response to be available. – Swathi Desai Mar 31 '20 at 06:13
  • Added a wait() method as below, client->receive().then([&ws_response](websocket_incoming_message i_msg){ ws_response = i_msg.extract_string().get(); }).wait(); and I am able to capture the response in the variable now. Thank you for the suggestions. – Swathi Desai Mar 31 '20 at 06:14