[Libwebsockets] How to pass a lws_callback_function from a c++ class

Pranjali Chumbhale pranjalic at safeai.ai
Wed Mar 25 20:58:08 CET 2020


Thank you for the information.
Just trying to understand it further,

In my case, I have a single context, single websocket and I am making the
call to lws_client_connect_via_info.
I am checking for a scenario where my server is not up and running.

I am expecting the following sequence of callbacks after few initial ones,
LWS_CALLBACK_PROTOCOL_INIT

LWS_CALLBACK_CLIENT_HTTP_BIND_PROTOCOL,

LWS_CALLBACK_GET_THREAD_ID

LWS_CALLBACK_SERVER_NEW_CLIENT_INSTANTIATED,

LWS_CALLBACK_CLIENT_CONNECTION_ERROR,


I see that LWS_CALLBACK_GET_THREAD_ID has a different wsi address. Does
that conclude its a fake wsi?

And this fake wsi will not be able to retrieve the object pointer I saved
earlier in user field , as it is a fake wsi.


Regards,
Pranjali


On Wed, Mar 25, 2020 at 12:39 PM <andy at warmcat.com> wrote:

>
>
> On March 25, 2020 7:23:55 PM UTC, Pranjali Chumbhale <pranjalic at safeai.ai>
> wrote:
> >So, the way, I am storing the object pointer *"this"* in protocols.user
> >is
> >correct for my use case? My use case is to store a pointer to object
> >and be
> >able to retrieve in the callback, irrespective of the callback.
> >and the correct way to retrieve it in callback would be to use*
> >lws_get_protocol(wsi)->user*
>
> If all the wsi that want to recover the pointer are bound to that
> protocol, you can do it that way.  There's also a similar user pointer you
> can set in the lws context and vhost at context / vhost creation time.
>
>
> https://libwebsockets.org/git/libwebsockets/tree/include/libwebsockets/lws-context-vhost.h#n337-345
>
> then you don't care about wsi protocol.
>
> >I observed that every time I get a callback, the wsi has a different
> >address.
> >I was expecting the wsi in callback to point to the websocket that we
> >create so, every time was expecting it to be the same value.
>
> That's is how it works.  A wsi lives (at the same address) for as long as
> the underlying socket connection.
>
> If your connection fails, you may start another connection and it'll be a
> different pointer.
>
> If you have more than one active wsi, they'll all use the same callback.
>
> Sometimes there are events reported to the callbacks that aren't related
> to a specific wsi, eg cancel service.  A fake wsi is created for that
> purpose, with a valid context pointer.
>
> -Andy
>
> >Regards,
> >Pranjali
> >
> >
> >On Tue, Mar 24, 2020 at 7:46 PM <andy at warmcat.com> wrote:
> >
> >>
> >>
> >> On March 24, 2020 9:52:30 PM UTC, Pranjali Chumbhale
> ><pranjalic at safeai.ai>
> >> wrote:
> >> >Hello Andy,
> >> >
> >> >Following up on this one.
> >> >
> >> >I decided to make my callback function static and use it.
> >>
> >> It's not like there was another choice.
> >>
> >> >Also,
> >> >
> >> >>Likewise there are .opaque_user_data, .user and some accessors in
> >many
> >> >lws objects with user-code defined lifetime.  Put your c++ "this" in
> >> >there.  You're dealing with a C library, it has to be dealt with as
> >C.
> >> >
> >> >I was using this approach.
> >>
> >> Right.
> >>
> >> >I do the assignment in the following way:
> >> >m_protocols[0].name = "my-protocol-name";
> >> >m_protocols[0].callback = callback_ws;
> >> >m_protocols[0].per_session_data_size = 0;
> >> >m_protocols[0].rx_buffer_size = m_max_data_size;
> >> >m_protocols[0].user = this; // user code can access this in callback
> >> >
> >> >// terminator - needed by libwebsocket
> >> >m_protocols[1].name = NULL;
> >> >m_protocols[1].callback = NULL;
> >> >m_protocols[1].per_session_data_size = 0;
> >> >m_protocols[1].rx_buffer_size = 0;
> >>
> >> Ok...
> >>
> >> >I was using gdb to debug:
> >> >The value of this is getting assigned to user:
> >> >
> >> >(gdb)* print(m_protocols[0].user)*
> >> >
> >> >$5 = (void *) 0x7fffa13f9dc0
> >> >
> >> >(gdb) *print(this)*
> >> >
> >> >$6 = (my_class_name * const) 0x7fffa13f9dc0
> >> >
> >> >But when I get callback, I see that the user field is 0x0.
> >> >From gdb:
> >>
> >> No... lots of structs in lws have a .user / .user_data /
> >.opaque_user_data
> >> the user code can set in your terms 'reflecting the this of different
> >> objects'.  Here, it'd be an object that lives as long as the context
> >that's
> >> interesting for all users of that protocol to have access to via
> >> lws_get_protocol(wsi)->user.  That object scope woule be
> >'per-protocol',
> >> not 'per-connection'.
> >>
> >> The 'user' parameter to the protocol callback is unrelated, although
> >I can
> >> see why it's confusing, that reflects a per-wsi object / struct
> >either
> >> allocated by lws when the wsi was bound to a particular protocol
> >(sized by
> >> the protocol's .per_session_data_size) or optionally set by user code
> >at
> >> creation time for client wsi, with this
> >>
> >>
> >>
> >
> https://libwebsockets.org/git/libwebsockets/tree/include/libwebsockets/lws-client.h#n85-86
> >>
> >> -Andy
> >>
> >> >my_class_name::callback_ws (wsi=0x56297b06f678,
> >> >reason=LWS_CALLBACK_EVENT_WAIT_CANCELLED, *user=0x0*, in=0x0, len=0)
> >> >
> >> >my_class_name::callback_ws (wsi=0x56297aafe2c0,
> >> >reason=LWS_CALLBACK_CLIENT_CONNECTION_ERROR,* user=0x0*,
> >> >    in=0x7f60636d46eb, len=17)
> >> >
> >> >Regards,
> >> >Pranjali
> >> >
> >> >
> >> >On Tue, Mar 17, 2020 at 5:58 PM <andy at warmcat.com> wrote:
> >> >
> >> >>
> >> >>
> >> >> On March 18, 2020 12:35:25 AM UTC, Pranjali Chumbhale
> >> ><pranjalic at safeai.ai>
> >> >> wrote:
> >> >> >I do not have the option of using a static method.
> >> >>
> >> >> Lws is C, the compiler is telling you C code doesn't know how to
> >play
> >> >c++
> >> >> games with magically passing "this" object context when invoking
> >the
> >> >> dynamic function ptr.  Static function pointers don't need that
> >and
> >> >are
> >> >> compatible with C.
> >> >>
> >> >> You "don't have the option" to do what you're doing.  Use a static
> >> >> function pointer and explicitly store your dynamic object pointer
> >> >> somewhere, where depends on the object scope but there should be
> >> >> somewhere.  Eg in lws_protocols in your example
> >> >>
> >> >>
> >> >>
> >> >
> >>
> >
> https://libwebsockets.org/git/libwebsockets/tree/include/libwebsockets/lws-protocols-plugins.h#n73-74
> >> >>
> >> >> Likewise there are .opaque_user_data, .user and some accessors in
> >> >many lws
> >> >> objects with user-code defined lifetime.  Put your c++ "this" in
> >> >there.
> >> >> You're dealing with a C library, it has to be dealt with as C.
> >> >>
> >> >> >I was trying more of
> >> >> >https://isocpp.org/wiki/faq/pointers-to-members#memfnptr-vs-fnptr
> >> >>
> >> >> It doesn't mention C.  If it did, it'd tell you what Brice (and
> >eg,
> >> >> libcurl docs) say.
> >> >>
> >> >> -Andy
> >> >>
> >> >> >Regards,
> >> >> >Pranjali
> >> >> >
> >> >> >
> >> >> >On Tue, Mar 17, 2020 at 5:31 PM Brice Hamon <brice at ydotm.com>
> >wrote:
> >> >> >
> >> >> >> Use a static method.
> >> >> >>
> >> >> >> On Tue, Mar 17, 2020 at 8:25 PM Pranjali Chumbhale
> >> >> ><pranjalic at safeai.ai>
> >> >> >> wrote:
> >> >> >>
> >> >> >>> Hello Team,
> >> >> >>>
> >> >> >>> I am trying to pass  a c++ class member function as a callback
> >to
> >> >> >>> protocols.
> >> >> >>>
> >> >> >>> Here is the relevant piece of code.
> >> >> >>>
> >> >> >>> This is how my callback function is declared.
> >> >> >>> int ns::class_name::callback_ws( struct lws *wsi, enum
> >> >> >>> lws_callback_reasons reason,
> >> >> >>> void *user, void *in, size_t len);
> >> >> >>>
> >> >> >>>
> >> >> >>> This is how I am using it to pass it to protocols:
> >> >> >>> std::function<int(struct lws *wsi, enum lws_callback_reasons
> >> >reason,
> >> >> >void
> >> >> >>> *user, void *in, size_t len)> callback_func;
> >> >> >>>
> >> >> >>> callback_func = std::bind(&ns::class_name::callback_ws,this,
> >> >> >>> std::placeholders::_1, std::placeholders::_2,
> >> >> >>> std::placeholders::_3, std::placeholders::_4,
> >> >> >>> std::placeholders::_5);
> >> >> >>>
> >> >> >>> m_protocols[0].name = "named_protocol";
> >> >> >>> m_protocols[0].callback = callback_func;
> >> >> >>> m_protocols[0].per_session_data_size = 0;
> >> >> >>> m_protocols[0].rx_buffer_size = m_max_data_size;
> >> >> >>>
> >> >> >>> // terminator - needed by libwebsocket
> >> >> >>> m_protocols[1].name = NULL;
> >> >> >>> m_protocols[1].callback = NULL;
> >> >> >>> m_protocols[1].per_session_data_size = 0;
> >> >> >>> m_protocols[1].rx_buffer_size = 0;
> >> >> >>>
> >> >> >>> I am getting the following error:
> >> >> >>>
> >> >> >>> error: cannot convert ‘std::function<int(lws*,
> >> >lws_callback_reasons,
> >> >> >>> void*, void*, long unsigned int)>’ to ‘int (*)(lws*,
> >> >> >lws_callback_reasons,
> >> >> >>> void*, void*, size_t) {aka int (*)(lws*, lws_callback_reasons,
> >> >> >void*,
> >> >> >>> void*, long unsigned int)}’ in assignment
> >> >> >>>    m_protocols[0].callback  = callback_func;
> >> >> >>>                               ^~~~~~~~~~~~~
> >> >> >>> I do not understand when the return data type of callback
> >> >function
> >> >> >is
> >> >> >>> int, why is it throwing error looking for  int(*).
> >> >> >>>
> >> >> >>> Regards,
> >> >> >>> Pranjali
> >> >> >>> _______________________________________________
> >> >> >>> Libwebsockets mailing list
> >> >> >>> Libwebsockets at ml.libwebsockets.org
> >> >> >>> https://libwebsockets.org/mailman/listinfo/libwebsockets
> >> >> >>>
> >> >> >>
> >> >>
> >>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://libwebsockets.org/pipermail/libwebsockets/attachments/20200325/c13856f2/attachment.htm>


More information about the Libwebsockets mailing list