This is an automated email from the git hooks/post-receive script. x2go pushed a commit to branch master in repository libx2goclient. commit 021444b16ae34b0f8876b4ea06a8bfeb0a930820 Author: Mihai Moldovan <ionic@ionic.de> Date: Mon Mar 29 10:13:55 2021 +0200 src/x2goclient-network{.{c,h},-ssh.c}: make connect-func and disconnect-func properties return and set via pointer to pointer to function, move typedefs into header file for global use. Function pointers are special in that they cannot be used as object pointers, since their size might not match sizeof (void*). In order to pass them around, we'll need to store them into a data object first and then pass around a pointer to this data object. --- src/x2goclient-network-ssh.c | 40 ++++++++++----------- src/x2goclient-network.c | 82 +++++++++++++++++++++++++++++++++++++------- src/x2goclient-network.h | 23 +++++++++++-- 3 files changed, 110 insertions(+), 35 deletions(-) diff --git a/src/x2goclient-network-ssh.c b/src/x2goclient-network-ssh.c index 5f65076..7e7aae0 100644 --- a/src/x2goclient-network-ssh.c +++ b/src/x2goclient-network-ssh.c @@ -178,16 +178,6 @@ typedef struct sockaddr_ho_ { * An example of a more abstract implementation is #X2GoClientNetwork. */ -/* - * This is a bit unsafe and odd, but we need a function pointer (proto)type - * for later use. - * - * It probably makes sense to keep it as generic as possible, though, to - * improve maintainability. - */ -typedef gboolean (*parent_connect_type) (void * const self, void * const gerr); -typedef gboolean (*parent_disconnect_type) (void * const self, void * const gerr); - /** * X2GoClientNetworkSSH: * @@ -1197,12 +1187,17 @@ gboolean x2goclient_network_ssh_connect (X2GoClientNetworkSSH * const self, GErr (void) parent_class; /* Fetch parent's connect function. */ - parent_connect_type parent_connect = NULL; - g_object_get (G_OBJECT (self), "connect-function", &parent_connect, NULL); - g_assert (parent_connect); + connect_type parent_connect = NULL; + connect_ref_type parent_connect_ref = NULL; + g_object_get (G_OBJECT (self), "connect-function", &parent_connect_ref, NULL); + g_assert (parent_connect_ref); - if (parent_connect) { - ret = parent_connect (parent, gerr); + if (parent_connect_ref) { + parent_connect = *(parent_connect_ref); + + if (parent_connect) { + ret = parent_connect (parent, gerr); + } } return (ret); @@ -1431,12 +1426,17 @@ gboolean x2goclient_network_ssh_disconnect (X2GoClientNetworkSSH * const self, G (void) parent_class; /* Fetch parent's disconnect function. */ - parent_disconnect_type parent_disconnect = NULL; - g_object_get (G_OBJECT (self), "disconnect-function", &parent_disconnect, NULL); - g_assert (parent_disconnect); + disconnect_type parent_disconnect = NULL; + disconnect_ref_type parent_disconnect_ref = NULL; + g_object_get (G_OBJECT (self), "disconnect-function", &parent_disconnect_ref, NULL); + g_assert (parent_disconnect_ref); - if (parent_disconnect) { - ret = parent_disconnect (parent, gerr); + if (parent_disconnect_ref) { + parent_disconnect = *(parent_disconnect_ref); + + if (parent_disconnect) { + ret = parent_disconnect (parent, gerr); + } } return (ret); diff --git a/src/x2goclient-network.c b/src/x2goclient-network.c index d3212f7..164cfc2 100644 --- a/src/x2goclient-network.c +++ b/src/x2goclient-network.c @@ -100,8 +100,8 @@ typedef struct X2GoClientNetworkPrivate_ { X2GoClientNetworkOptions *options; gchar *session_path; /* Will eventually be replaced with a session object, probably. */ gboolean connected; - gboolean (*connect_func) (X2GoClientNetwork * const self, GError ** const gerr); - gboolean (*disconnect_func) (X2GoClientNetwork * const self, GError ** const gerr); + connect_type connect_func; + disconnect_type disconnect_func; } X2GoClientNetworkPrivate; G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (X2GoClientNetwork, x2goclient_network, G_TYPE_OBJECT) @@ -255,33 +255,49 @@ static void x2goclient_network_class_init (X2GoClientNetworkClass * const klass) /** * X2GoClientNetwork:connect-function: * - * This property holds a pointer to the instance's connect function. + * This property holds a pointer to a pointer to the instance's connect + * function. * * It is read-only. * * Use it to fetch the parent's connect function in derived classes. * + * This property transfers ownership of the wrapping pointer - i.e., the + * data returned by it - to the caller, who is responsible for freeing it. + * * Since: 0.0.5 */ - net_obj_properties[X2GO_NET_PROP_CONN_FUNC] = g_param_spec_pointer ("connect-function", _("Pointer to this instance's connect function"), - _("A pointer to the instance's connect function. " - "This is supposed to be immutable."), + net_obj_properties[X2GO_NET_PROP_CONN_FUNC] = g_param_spec_pointer ("connect-function", + _("Pointer to a pointer to this instance's connect function; " + "the ownership of the wrapping pointer is transferred to the caller"), + _("A pointer to a pointer to the instance's connect function. " + "This is supposed to be immutable. " + "The caller is responsible for freeing the wrapping pointer - i.e., " + "the data returned by this property."), G_PARAM_STATIC_STRINGS | G_PARAM_READABLE); /** * X2GoClientNetwork:disconnect-function: * - * This property holds a pointer to the instance's disconnect function. + * This property holds a pointer to a pointer to the instance's disconnect + * function. * * It is read-only. * * Use it to fetch the parent's disconnect function in derived classes. * + * This property transfers ownership of the wrapping pointer - i.e., the + * data returned by it - to the caller, who is responsible for freeing it. + * * Since: 0.0.5 */ - net_obj_properties[X2GO_NET_PROP_DISCONN_FUNC] = g_param_spec_pointer ("disconnect-function", _("Pointer to this instance's disconnect function"), + net_obj_properties[X2GO_NET_PROP_DISCONN_FUNC] = g_param_spec_pointer ("disconnect-function", + _("Pointer to a pointer to this instance's disconnect function; " + "the ownership of the wrapping pointer is transferred to the caller"), _("A pointer to the instance's disconnect function. " - "This is supposed to be immutable."), + "This is supposed to be immutable. " + "The caller is responsible for freeing the wrapping pointer - i.e., " + "the data returned by this property."), G_PARAM_STATIC_STRINGS | G_PARAM_READABLE); g_object_class_install_properties (object_class, X2GO_NET_N_PROPERTIES, net_obj_properties); @@ -437,6 +453,8 @@ static void x2goclient_network_set_property (GObject * const object, guint prop_ X2GoClientNetwork *self = X2GOCLIENT_NETWORK (object); X2GoClientNetworkPrivate *priv = x2goclient_network_get_instance_private (self); + connect_ref_type conn_ref = NULL; + disconnect_ref_type disconn_ref = NULL; switch (prop_id) { case (X2GO_NET_PROP_SOCKET_SPEC): if (priv->socket_spec) { @@ -462,10 +480,30 @@ static void x2goclient_network_set_property (GObject * const object, guint prop_ priv->connected = g_value_get_boolean (value); break; case (X2GO_NET_PROP_CONN_FUNC): - priv->connect_func = g_value_get_pointer (value); + /* Fetch data pointer. */ + conn_ref = g_value_get_pointer (value); + + /* Set function pointer based on this data. */ + priv->connect_func = *(conn_ref); + + /* + * And, importantly, get rid of the data. + * + * Note that this is strictly speaking a + * violation of g_value_get_pointer ()'s + * contract (being transfer_none), but we + * explicitly transfer ownership, so it + * should be fine in this case. + */ + g_free (conn_ref); + conn_ref = NULL; break; case (X2GO_NET_PROP_DISCONN_FUNC): - priv->disconnect_func = g_value_get_pointer (value); + /* Same as for CONN_FUNC. */ + disconn_ref = g_value_get_pointer (value); + priv->disconnect_func = *(disconn_ref); + g_free (disconn_ref); + disconn_ref = NULL; break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, param_spec); @@ -477,6 +515,8 @@ static void x2goclient_network_get_property (GObject * const object, const guint X2GoClientNetwork *self = X2GOCLIENT_NETWORK (object); X2GoClientNetworkPrivate *priv = x2goclient_network_get_instance_private (self); + connect_ref_type conn_ref = NULL; + disconnect_ref_type disconn_ref = NULL; switch (prop_id) { case (X2GO_NET_PROP_SOCKET_SPEC): g_value_set_boxed (value, priv->socket_spec); @@ -494,10 +534,26 @@ static void x2goclient_network_get_property (GObject * const object, const guint g_value_set_boolean (value, priv->connected); break; case (X2GO_NET_PROP_CONN_FUNC): - g_value_set_pointer (value, priv->connect_func); + /* + * Create and pass function pointer as + * data. + */ + conn_ref = g_new0 (connect_type, 1); + *(conn_ref) = priv->connect_func; + g_value_set_pointer (value, conn_ref); + + /* + * Never free conn_ref here! It's the + * caller's responsibility. + */ + conn_ref = NULL; break; case (X2GO_NET_PROP_DISCONN_FUNC): - g_value_set_pointer (value, priv->disconnect_func); + /* Same as for CONN_FUNC. */ + disconn_ref = g_new0 (disconnect_type, 1); + *(disconn_ref) = priv->disconnect_func; + g_value_set_pointer (value, disconn_ref); + disconn_ref = NULL; break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, param_spec); diff --git a/src/x2goclient-network.h b/src/x2goclient-network.h index 54cd44d..2680197 100644 --- a/src/x2goclient-network.h +++ b/src/x2goclient-network.h @@ -52,11 +52,30 @@ struct _X2GoClientNetworkOptionsClass { #define X2GOCLIENT_TYPE_NETWORK (x2goclient_network_get_type ()) G_DECLARE_DERIVABLE_TYPE (X2GoClientNetwork, x2goclient_network, X2GOCLIENT, NETWORK, GObject) +/* + * Function (pointer) prototypes. + * + * We need the reference type because casting function pointers to object + * pointers is illegal in ISO C. This isn't just an oversight in the standard, + * but actually intended to keep developers from shooting themselves in the + * foot. + * + * Function pointers and object pointers are not interchangeable. There are + * platforms on which both have different sizes. + * + * Due to this, the only way to pass them around is through indirection - + * i.e., the address of an object pointer. + */ +typedef gboolean (*connect_type) (X2GoClientNetwork * const self, GError ** const gerr); +typedef connect_type *connect_ref_type; +typedef gboolean (*disconnect_type) (X2GoClientNetwork * const self, GError ** const gerr); +typedef disconnect_type *disconnect_ref_type; + struct _X2GoClientNetworkClass { GObjectClass parent_class; - gboolean (*connect) (X2GoClientNetwork * const self, GError ** const gerr); - gboolean (*disconnect) (X2GoClientNetwork * const self, GError ** const gerr); + connect_type connect; + disconnect_type disconnect; /*< private >*/ GSocketAddress* (*parse_sockspec) (X2GoClientNetwork * const self, const GString * const sockspec); -- Alioth's /home/x2go-admin/maintenancescripts/git/hooks/post-receive-email on /srv/git/code.x2go.org/libx2goclient.git