THRIFT-2631 c_glib: Support service inheritance in clients
Patch: Simon South
diff --git a/compiler/cpp/src/generate/t_c_glib_generator.cc b/compiler/cpp/src/generate/t_c_glib_generator.cc
index 5195224..576c60b 100644
--- a/compiler/cpp/src/generate/t_c_glib_generator.cc
+++ b/compiler/cpp/src/generate/t_c_glib_generator.cc
@@ -1046,6 +1046,42 @@
string service_name_lc = to_lower_case(initial_caps_to_underscores(service_name_));
string service_name_uc = to_upper_case(service_name_lc);
+ string parent_service_name;
+ string parent_service_name_lc;
+ string parent_service_name_uc;
+
+ string parent_class_name = "GObject";
+ string parent_type_name = "G_TYPE_OBJECT";
+
+ // The service this service extends, or NULL if it extends no
+ // service
+ t_service *extends_service = tservice->get_extends();
+ if (extends_service) {
+ // The name of the parent service
+ parent_service_name = extends_service->get_name();
+ parent_service_name_lc =
+ to_lower_case(initial_caps_to_underscores(parent_service_name));
+ parent_service_name_uc = to_upper_case(parent_service_name_lc);
+
+ // The names of the client class' parent class and type
+ parent_class_name = this->nspace + parent_service_name + "Client";
+ parent_type_name =
+ this->nspace_uc + "TYPE_" + parent_service_name_uc + "_CLIENT";
+ }
+
+ // The base service (the topmost in the "extends" hierarchy), on
+ // whose client class the "input_protocol" and "output_protocol"
+ // properties are defined
+ t_service *base_service = tservice;
+ while (base_service->get_extends()) {
+ base_service = base_service->get_extends();
+ }
+
+ string base_service_name = base_service->get_name();
+ string base_service_name_lc =
+ to_lower_case(initial_caps_to_underscores(base_service_name));
+ string base_service_name_uc = to_upper_case(base_service_name_lc);
+
// Generate the client interface dummy object in the header.
f_header_ <<
"/* " << service_name_ << " service interface */" << endl <<
@@ -1139,10 +1175,17 @@
"/* " << service_name_ << " service client */" << endl <<
"struct _" << this->nspace << service_name_ << "Client" << endl <<
"{" << endl <<
- " GObject parent;" << endl <<
- endl <<
- " ThriftProtocol *input_protocol;" << endl <<
- " ThriftProtocol *output_protocol;" << endl <<
+ " " << parent_class_name << " parent;" << endl;
+ if (!extends_service) {
+ // Define "input_protocol" and "output_protocol" properties only
+ // for base services; child service-client classes will inherit
+ // these
+ f_header_ <<
+ endl <<
+ " ThriftProtocol *input_protocol;" << endl <<
+ " ThriftProtocol *output_protocol;" << endl;
+ }
+ f_header_ <<
"};" << endl <<
"typedef struct _" << this->nspace << service_name_ << "Client " <<
this->nspace << service_name_ << "Client;" << endl <<
@@ -1152,7 +1195,7 @@
f_header_ <<
"struct _" << this->nspace << service_name_ << "ClientClass" << endl <<
"{" << endl <<
- " GObjectClass parent;" << endl <<
+ " " << parent_class_name << "Class parent;" << endl <<
"};" << endl <<
"typedef struct _" << this->nspace << service_name_ << "ClientClass " <<
this->nspace << service_name_ << "ClientClass;" << endl <<
@@ -1308,79 +1351,83 @@
endl <<
"G_DEFINE_TYPE_WITH_CODE (" << this->nspace << service_name_ <<
"Client, " << this->nspace_lc << service_name_lc << "_client," << endl <<
- " G_TYPE_OBJECT, " << endl <<
+ " " << parent_type_name << ", " << endl <<
" G_IMPLEMENT_INTERFACE (" <<
this->nspace_uc << "TYPE_" << service_name_uc << "_IF," << endl <<
" " <<
this->nspace_lc << service_name_lc << "_if_interface_init));" << endl <<
endl;
- // Generate client properties
- f_service_ <<
- "enum _" << this->nspace << service_name_ << "ClientProperties" << endl <<
- "{" << endl <<
- " PROP_0," << endl <<
- " PROP_" << this->nspace_uc << service_name_uc <<
- "_CLIENT_INPUT_PROTOCOL," <<
- endl <<
- " PROP_" << this->nspace_uc << service_name_uc <<
- "_CLIENT_OUTPUT_PROTOCOL" <<
- endl <<
- "};" << endl <<
- endl;
+ // Generate property-related code only for base services---child
+ // service-client classes have only properties inherited from their
+ // parent class
+ if (!extends_service) {
+ // Generate client properties
+ f_service_ <<
+ "enum _" << this->nspace << service_name_ << "ClientProperties" << endl <<
+ "{" << endl <<
+ " PROP_0," << endl <<
+ " PROP_" << this->nspace_uc << service_name_uc <<
+ "_CLIENT_INPUT_PROTOCOL," <<
+ endl <<
+ " PROP_" << this->nspace_uc << service_name_uc <<
+ "_CLIENT_OUTPUT_PROTOCOL" <<
+ endl <<
+ "};" << endl <<
+ endl;
- // generate property setter
- f_service_ <<
- "void" << endl <<
- this->nspace_lc << service_name_lc << "_client_set_property (" <<
- "GObject *object, guint property_id, const GValue *value, " <<
- "GParamSpec *pspec)" << endl <<
- "{" << endl <<
- " " << this->nspace << service_name_ << "Client *client = " <<
- this->nspace_uc << service_name_uc << "_CLIENT (object);" << endl <<
- endl <<
- " THRIFT_UNUSED_VAR (pspec);" << endl <<
- endl <<
- " switch (property_id)" << endl <<
- " {" << endl <<
- " case PROP_" << this->nspace_uc << service_name_uc <<
- "_CLIENT_INPUT_PROTOCOL:" << endl <<
- " client->input_protocol = g_value_get_object (value);" << endl <<
- " break;" << endl <<
- " case PROP_" << this->nspace_uc << service_name_uc <<
- "_CLIENT_OUTPUT_PROTOCOL:" << endl <<
- " client->output_protocol = g_value_get_object (value);" << endl <<
- " break;" << endl <<
- " }" << endl <<
- "}" << endl <<
- endl;
+ // generate property setter
+ f_service_ <<
+ "void" << endl <<
+ this->nspace_lc << service_name_lc << "_client_set_property (" <<
+ "GObject *object, guint property_id, const GValue *value, " <<
+ "GParamSpec *pspec)" << endl <<
+ "{" << endl <<
+ " " << this->nspace << service_name_ << "Client *client = " <<
+ this->nspace_uc << service_name_uc << "_CLIENT (object);" << endl <<
+ endl <<
+ " THRIFT_UNUSED_VAR (pspec);" << endl <<
+ endl <<
+ " switch (property_id)" << endl <<
+ " {" << endl <<
+ " case PROP_" << this->nspace_uc << service_name_uc <<
+ "_CLIENT_INPUT_PROTOCOL:" << endl <<
+ " client->input_protocol = g_value_get_object (value);" << endl <<
+ " break;" << endl <<
+ " case PROP_" << this->nspace_uc << service_name_uc <<
+ "_CLIENT_OUTPUT_PROTOCOL:" << endl <<
+ " client->output_protocol = g_value_get_object (value);" << endl <<
+ " break;" << endl <<
+ " }" << endl <<
+ "}" << endl <<
+ endl;
- // generate property getter
- f_service_ <<
- "void" << endl <<
- this->nspace_lc << service_name_lc << "_client_get_property (" <<
- "GObject *object, guint property_id, GValue *value, " <<
- "GParamSpec *pspec)" << endl <<
- "{" << endl <<
- " " << this->nspace << service_name_ << "Client *client = " <<
- this->nspace_uc << service_name_uc << "_CLIENT (object);" << endl <<
- endl <<
- " THRIFT_UNUSED_VAR (pspec);" << endl <<
- endl <<
- " switch (property_id)" << endl <<
- " {" << endl <<
- " case PROP_" << this->nspace_uc << service_name_uc <<
- "_CLIENT_INPUT_PROTOCOL:" << endl <<
- " g_value_set_object (value, client->input_protocol);" << endl <<
- " break;" << endl <<
- " case PROP_" << this->nspace_uc << service_name_uc <<
- "_CLIENT_OUTPUT_PROTOCOL:" << endl <<
- " g_value_set_object (value, client->output_protocol);" << endl <<
- " break;" << endl <<
- " }" << endl <<
- "}" << endl <<
- endl;
-
+ // generate property getter
+ f_service_ <<
+ "void" << endl <<
+ this->nspace_lc << service_name_lc << "_client_get_property (" <<
+ "GObject *object, guint property_id, GValue *value, " <<
+ "GParamSpec *pspec)" << endl <<
+ "{" << endl <<
+ " " << this->nspace << service_name_ << "Client *client = " <<
+ this->nspace_uc << service_name_uc << "_CLIENT (object);" << endl <<
+ endl <<
+ " THRIFT_UNUSED_VAR (pspec);" << endl <<
+ endl <<
+ " switch (property_id)" << endl <<
+ " {" << endl <<
+ " case PROP_" << this->nspace_uc << service_name_uc <<
+ "_CLIENT_INPUT_PROTOCOL:" << endl <<
+ " g_value_set_object (value, client->input_protocol);" << endl <<
+ " break;" << endl <<
+ " case PROP_" << this->nspace_uc << service_name_uc <<
+ "_CLIENT_OUTPUT_PROTOCOL:" << endl <<
+ " g_value_set_object (value, client->output_protocol);" << endl <<
+ " break;" << endl <<
+ " }" << endl <<
+ "}" << endl <<
+ endl;
+ }
// Generate client method implementations
for (f_iter = functions.begin(); f_iter != functions.end(); ++f_iter) {
@@ -1405,7 +1452,7 @@
f_service_ <<
indent() << "gint32 cseqid = 0;" << endl <<
indent() << "ThriftProtocol * protocol = " <<
- this->nspace_uc << service_name_uc <<
+ this->nspace_uc << base_service_name_uc <<
"_CLIENT (iface)->output_protocol;" << endl <<
endl <<
indent() << "if (thrift_protocol_write_message_begin (protocol, \"" <<
@@ -1454,7 +1501,7 @@
indent() << "gchar * fname = NULL;" << endl <<
indent() << "ThriftMessageType mtype;" << endl <<
indent() << "ThriftProtocol * protocol = " <<
- this->nspace_uc << service_name_uc <<
+ this->nspace_uc << base_service_name_uc <<
"_CLIENT (iface)->input_protocol;" << endl <<
endl <<
indent() << "if (thrift_protocol_read_message_begin " <<
@@ -1620,9 +1667,13 @@
"static void" << endl <<
this->nspace_lc << service_name_lc << "_client_init (" <<
this->nspace << service_name_ << "Client *client)" << endl <<
- "{" << endl <<
- " client->input_protocol = NULL;" << endl <<
- " client->output_protocol = NULL;" << endl <<
+ "{" << endl;
+ if(!extends_service) {
+ f_service_ <<
+ " client->input_protocol = NULL;" << endl <<
+ " client->output_protocol = NULL;" << endl;
+ }
+ f_service_ <<
"}" << endl <<
endl;
@@ -1631,36 +1682,40 @@
"static void" << endl <<
this->nspace_lc << service_name_lc << "_client_class_init (" <<
this->nspace << service_name_ << "ClientClass *cls)" << endl <<
- "{" << endl <<
- " GObjectClass *gobject_class = G_OBJECT_CLASS (cls);" << endl <<
- " GParamSpec *param_spec;" << endl <<
- endl <<
- " gobject_class->set_property = " << this->nspace_lc <<
- service_name_lc << "_client_set_property;" << endl <<
- " gobject_class->get_property = " << this->nspace_lc <<
- service_name_lc << "_client_get_property;" << endl <<
- endl <<
- " param_spec = g_param_spec_object (\"input_protocol\"," << endl <<
- " \"input protocol (construct)\"," <<
- endl <<
- " \"Set the client input protocol\"," <<
- endl <<
- " THRIFT_TYPE_PROTOCOL," << endl <<
- " G_PARAM_READWRITE);" << endl <<
- " g_object_class_install_property (gobject_class," << endl <<
- " PROP_" << this->nspace_uc <<
- service_name_uc << "_CLIENT_INPUT_PROTOCOL, param_spec);" << endl <<
- endl <<
- " param_spec = g_param_spec_object (\"output_protocol\"," << endl <<
- " \"output protocol (construct)\"," <<
- endl <<
- " \"Set the client output protocol\"," <<
- endl <<
- " THRIFT_TYPE_PROTOCOL," << endl <<
- " G_PARAM_READWRITE);" << endl <<
- " g_object_class_install_property (gobject_class," << endl <<
- " PROP_" << this->nspace_uc <<
- service_name_uc << "_CLIENT_OUTPUT_PROTOCOL, param_spec);" << endl <<
+ "{" << endl;
+ if (!extends_service) {
+ f_service_ <<
+ " GObjectClass *gobject_class = G_OBJECT_CLASS (cls);" << endl <<
+ " GParamSpec *param_spec;" << endl <<
+ endl <<
+ " gobject_class->set_property = " << this->nspace_lc <<
+ service_name_lc << "_client_set_property;" << endl <<
+ " gobject_class->get_property = " << this->nspace_lc <<
+ service_name_lc << "_client_get_property;" << endl <<
+ endl <<
+ " param_spec = g_param_spec_object (\"input_protocol\"," << endl <<
+ " \"input protocol (construct)\"," <<
+ endl <<
+ " \"Set the client input protocol\"," <<
+ endl <<
+ " THRIFT_TYPE_PROTOCOL," << endl <<
+ " G_PARAM_READWRITE);" << endl <<
+ " g_object_class_install_property (gobject_class," << endl <<
+ " PROP_" << this->nspace_uc <<
+ service_name_uc << "_CLIENT_INPUT_PROTOCOL, param_spec);" << endl <<
+ endl <<
+ " param_spec = g_param_spec_object (\"output_protocol\"," << endl <<
+ " \"output protocol (construct)\"," <<
+ endl <<
+ " \"Set the client output protocol\"," <<
+ endl <<
+ " THRIFT_TYPE_PROTOCOL," << endl <<
+ " G_PARAM_READWRITE);" << endl <<
+ " g_object_class_install_property (gobject_class," << endl <<
+ " PROP_" << this->nspace_uc <<
+ service_name_uc << "_CLIENT_OUTPUT_PROTOCOL, param_spec);" << endl;
+ }
+ f_service_ <<
"}" << endl <<
endl;
}
diff --git a/lib/c_glib/test/testdebugproto.c b/lib/c_glib/test/testdebugproto.c
index 689e001..f2ea3af 100644
--- a/lib/c_glib/test/testdebugproto.c
+++ b/lib/c_glib/test/testdebugproto.c
@@ -17,6 +17,7 @@
* under the License.
*/
+#include <assert.h>
#include <math.h>
#include <glib-object.h>
@@ -24,10 +25,15 @@
#define M_PI 3.1415926535897932385
#endif
+#include <thrift/c_glib/protocol/thrift_protocol.h>
+#include <thrift/c_glib/protocol/thrift_binary_protocol.h>
+
#include "gen-c_glib/t_test_debug_proto_test_types.h"
+#include "gen-c_glib/t_test_srv.h"
+#include "gen-c_glib/t_test_inherited.h"
static void
-test_debug_proto(void)
+test_structs(void)
{
TTestOneOfEach *ooe = NULL;
TTestNesting *n = NULL;
@@ -79,13 +85,55 @@
return 0;
}
+static void
+test_service_inheritance (void)
+{
+ ThriftProtocol *protocol;
+ TTestInheritedClient *inherited_client;
+ GObject *input_protocol, *output_protocol;
+
+ protocol = g_object_new (THRIFT_TYPE_BINARY_PROTOCOL, NULL);
+ inherited_client = g_object_new (T_TEST_TYPE_INHERITED_CLIENT,
+ NULL);
+
+ /* TTestInheritedClient inherits from TTestSrvClient */
+ assert (g_type_is_a (T_TEST_TYPE_INHERITED_CLIENT,
+ T_TEST_TYPE_SRV_CLIENT));
+
+ /* TTestInheritedClient implements TTestSrvClient's interface */
+ assert (g_type_is_a (T_TEST_TYPE_INHERITED_CLIENT,
+ T_TEST_TYPE_SRV_IF));
+
+ /* TTestInheritedClient's inherited properties can be set and
+ * retrieved */
+ g_object_set (inherited_client,
+ "input_protocol", protocol,
+ "output_protocol", protocol,
+ NULL);
+
+ g_object_get (inherited_client,
+ "input_protocol", &input_protocol,
+ "output_protocol", &output_protocol,
+ NULL);
+
+ assert (input_protocol == G_OBJECT(protocol));
+ assert (output_protocol == G_OBJECT(protocol));
+
+ g_object_unref (output_protocol);
+ g_object_unref (input_protocol);
+ g_object_unref (inherited_client);
+ g_object_unref (protocol);
+}
+
int
main(int argc, char *argv[])
{
g_type_init();
g_test_init (&argc, &argv, NULL);
- g_test_add_func ("/testdebugproto/DebugProto", test_debug_proto);
+ g_test_add_func ("/testdebugproto/DebugProto/Structs", test_structs);
+ g_test_add_func ("/testdebugproto/DebugProto/ServiceInheritance",
+ test_service_inheritance);
return g_test_run ();
}