Re: Derived interfaces : An Example Solution



 Murray Cumming wrote:

So, if interfaces can't really be inherited, what does this do:
gpointer g_type_interface_peek_parent (gpointer g_iface)
?


Translating the GTK interfaces into C++ was tricky. You can only derive an interface from G_TYPE_INTERFACE, not an exisitng interface. Interfaces are GTK's equivalent to C++'s idea of the pure virtual function and abstract interface class(see Bjarne Stroustrup, 3rd edition, 25.6 Interface Classes, P 778).

Murray, I thought the easiest way was to share with you the example source I have been working with. All the instructions are in the attached file (14kb). The following is the introduction from this file.

/* This example shows what I think is the basic C++ code needed to get the GTK+ * interface classes encapsulated correctly. This test includes the necessary * skeleton framework of classes. As the example I've encapsulated GtkEditable, * GtkEditableClass and GtkEntry and hooked them up to the GtkEditable insert_text
*  signal.
*
*  You can compile this test file example with:
*
* g++ -Wall -g test.cc -o test `pkg-config gtk+-2.0 --cflags --libs` -lstdc++
*
*  Run test from the console. First you will see the line:
*
*  Inside Gtk::EditableClass::init...
*
* showing you that in fact the init function inside Gtk::EditableClass was called.
*
* Then, after typing a letter into the entry widget you will see the following 3 lines
*
*  Inside Gtk::EditableClass::insert_text_proxy...
*  Inside Gtk::Editable::on_insert_text...
*  Inside Gtk::Editable::on_insert_text's call to parent function...
*
* showing you that the correct sequence of functions were called and that Gtk::Editable, * GtkEditableClass and Gtk::Entry are all hooked up to insert_text correctly.
*
* Gtk::Editable is an abstract class that is essentially stand alone, but is inherited from * Glib::TypeInstance so that it can have access to the GObject instance pointer it needs,
*  which it gets through multiple inheritance with Gtk::Entry.
*
* For simplicity, the class_init() and get_type() functions are only in a separate * class for Gtk::Editable and Gtk::EditableClass, which is the main purpose of * this example. In all the other classes, including GtkEntry, I put them in the one class.
*
* Disclaimer :-) I needed to get this stuff working for my own project. This was the actual test * code I worked with a few months ago, I just cleaned it up a bit before passing it on. This example * is so far my only test of the code but it seems to work OK. If some of the other code in the example * doesn't seem connected to anything thats because it's not complete, its only there to get the example * up and running. The focus is intended to be on Glib::TypeInstance, Glib::TypeInterface, * Glib::Object, Gtk::Editable, Gtk::EditableClass and Gtk::Entry classes only.
*
*  I hope this is of some help,
*  Jeff Franks.
*/






/*  This example shows what I think is the basic C++ code needed to get the GTK+
 *  interface classes encapsulated correctly. This test includes the necessary
 *  skeleton framework of classes. As the example I've encapsulated GtkEditable,
 *  GtkEditableClass and GtkEntry and hooked them up to the GtkEditable insert_text
 *  signal.
 *
 *  You can compile this test file example with:
 *
 *  g++ -Wall -g test.cc -o test `pkg-config gtk+-2.0 --cflags --libs` -lstdc++
 *
 *  Run test from the console. First you will see the line:
 *
 *  Inside Gtk::EditableClass::init...
 *
 *  showing you that in fact the init function inside Gtk::EditableClass was called.
 *
 *  Then, after typing a letter into the entry widget you will see the following 3 lines
 *
 *  Inside Gtk::EditableClass::insert_text_proxy...
 *  Inside Gtk::Editable::on_insert_text...
 *  Inside Gtk::Editable::on_insert_text's call to parent function...
 *
 *  showing you that the correct sequence of functions were called and that Gtk::Editable,
 *  GtkEditableClass and Gtk::Entry are all hooked up with insert_text correctly.
 *
 *  Gtk::Editable is an abstract class that is essentially stand alone, but is inherited from
 *  Glib::TypeInstance so that it can have access to the GObject instance pointer it needs,
 *  which it gets through multiple inheritance with Gtk::Entry.
 *
 *  For simplicity, the class_init() and get_type() functions are only in a separate
 *  class for Gtk::Editable and Gtk::EditableClass, which is the main purpose of
 *  this example. In all the other classes, including GtkEntry, I put them in the one class.
 *
 *  Disclaimer :-) I needed to get this stuff working for my own project. This was the actual test
 *  code I worked with a few months ago, I just cleaned it up a bit before passing it on. This example
 *  is so far my only test of the code but it seems to work OK. If some of the other code in the example
 *  doesn't seem connected to anything thats because it's not complete, its only there to get the example
 *  up and running. The focus is intended to be on the Glib::TypeInstance, Glib::TypeInterface,
 *  Glib::Object, Gtk::Editable, Gtk::EditableClass and Gtk::Entry classes only.
 *
 *  I hope this is of some help,
 *  Jeff Franks.
 */

#include <gtk/gtk.h>
#include <glib-object.h>

/////////////////////////////////////////////////////////////////////////////////////////////////////
//                                        Header File Section                                      //
/////////////////////////////////////////////////////////////////////////////////////////////////////

namespace Glib {

/*  Glib::TypeInstance
 */

class TypeInstance
{
protected:
	GTypeInstance *instance;

	TypeInstance();
 	virtual ~TypeInstance() = 0;
};

/* Glib::TypeInterface
 */

class TypeInterface : protected virtual TypeInstance
{
public:
	virtual ~TypeInterface() = 0;
};

/*  Glib::Object
 */

class Object : public virtual TypeInstance
{
protected:
	explicit Object(GObject *object);

public:
	virtual ~Object();

	static void class_init(GObjectClass *g_class);

	static GType get_type();

	GObject* g_object() const { return (GObject*)instance; }
};

} // namespace Glib

namespace Gtk {

/*  Gtk::Object
 */

class Object : public Glib::Object
{
protected:
	explicit Object(GtkObject *object);

public:
	virtual ~Object();

	static void class_init(GtkObjectClass *g_class);

	static GType get_type();

	GtkObject* gtk_object() const { return (GtkObject*)instance; }
};

/*  Gtk::Widget
 */

class Widget : public Object
{
protected:
	explicit Widget(GtkWidget *widget);

public:
	virtual ~Widget();

	static void class_init(GtkWidgetClass *g_class);

	static GType get_type();

	GtkWidget* gtk_widget() const { return (GtkWidget*)instance; }

	void show();
};

////////////////////////////////////////// This starts the part of main interest to you //////////////////////////////////////////////

/*  Gtk::EditableClass
 */

class EditableClass
{
public:
	static void init(GtkEditableClass *g_iface);

	static void insert_text_proxy(GtkEditable *editable, const gchar *text, gint length, gint *position);
};

/*  Gtk::Editable
 */

class Editable : public Glib::TypeInterface
{
	friend class EditableClass;

protected:
	virtual void on_insert_text(const gchar *text, gint length, gint *position);

public:
	GtkEditable* gtk_editable() const { return (GtkEditable*)instance; }

	GtkEditableClass* gtk_editable_class() const { return GTK_EDITABLE_GET_CLASS(gtk_editable()); }
};

/*  Gtk::Entry
 */

class Entry : public Widget, public Editable
{
protected:
	explicit Entry(GtkEntry *entry);

public:
	Entry();

	virtual ~Entry();

	static void class_init(GtkEntryClass *g_class);

	static GType get_type();

	GtkEntry* gtk_entry() const { return (GtkEntry*)instance; }
};

////////////////////////////////////////// This ends the part of main interest to you ///////////////////////////////////////////////

/*  Gtk::Container
 */

class Container : public Widget
{
protected:
	explicit Container(GtkContainer *container);

public:
	virtual ~Container();

	static void class_init(GtkContainerClass *g_class);

	static GType get_type();

	GtkContainer* gtk_container() const { return (GtkContainer*)instance; }

	void add(Widget& widget);
};

/*  Gtk::Bin
 */

class Bin : public Container
{
protected:
	explicit Bin(GtkBin *bin);

public:
	virtual ~Bin();

	static void class_init(GtkBinClass *g_class);

	static GType get_type();
};

/*  GTk::Window
 */

class Window : public Bin
{
protected:
	explicit Window(GtkWindow *window);

public:
	Window();

	virtual ~Window();

	static void class_init(GtkWindowClass *g_class);

	static GType get_type();

	GtkWindow* gtk_window() const { return (GtkWindow*)instance; }
};

} // namespace Gtk

/////////////////////////////////////////////////////////////////////////////////////////////////////
//                                        Source File Section                                      //
/////////////////////////////////////////////////////////////////////////////////////////////////////

/*  Glib::TypeInstance
 */

Glib::TypeInstance::TypeInstance()
: instance(0)
{
}

Glib::TypeInstance::~TypeInstance()
{
}

/* Glib::TypeInterface
 */

Glib::TypeInterface::~TypeInterface()
{
}

/*  Glib::Object
 */

void destroy_notifier(gpointer data)
{
	Glib::Object *object = (Glib::Object*)data;
	if (object)
	{
		delete object;
		object = 0;
	}
}

Glib::Object::Object(GObject *object)
{
	if (!g_object_get_data(object, "test_pointer"))
		g_object_set_data_full(object, "test_pointer", this, destroy_notifier);

	instance = (GTypeInstance*)object;
}

Glib::Object::~Object()
{
	if (instance)
	{
		g_object_steal_data(g_object(), "test_pointer");
		instance = 0;
	}
}

void
Glib::Object::class_init(GObjectClass *g_class)
{
}

GType
Glib::Object::get_type()
{
	static GType type = 0;
	if (!type)
	{
		static const GTypeInfo info =
		{
			sizeof(GObjectClass),
			(GBaseInitFunc)0,
			(GBaseFinalizeFunc)0,
			(GClassInitFunc)class_init,
			0, 0,
			sizeof(GObject),
				0, /* preallocs */
			(GInstanceInitFunc)0
		};
		type = g_type_register_static(G_TYPE_OBJECT, "Test_G_Object", &info, GTypeFlags(0));
	}
	return type;
}

/*  Gtk::Object
 */

Gtk::Object::Object(GtkObject *object)
: Glib::Object((GObject*)object)
{
	gtk_object_ref(object);
	gtk_object_sink(object);
}

Gtk::Object::~Object()
{
}

void
Gtk::Object::class_init(GtkObjectClass *g_class)
{
	Glib::Object::class_init((GObjectClass*)g_class);
}

GType
Gtk::Object::get_type()
{
	static GType type = 0;
	if (!type)
	{
		static const GTypeInfo info =
		{
			sizeof(GtkObjectClass),
			(GBaseInitFunc)0,
			(GBaseFinalizeFunc)0,
			(GClassInitFunc)class_init,
			0, 0,
			sizeof(GtkObject),
			0, /* preallocs */
			(GInstanceInitFunc)0
		};
		type = g_type_register_static(GTK_TYPE_OBJECT, "Test_Gtk_Object", &info, GTypeFlags(0));
	}
	return type;
}

/*  Gtk::Widget
 */

Gtk::Widget::Widget(GtkWidget *widget) : Object((GtkObject*)widget)
{
}

Gtk::Widget::~Widget()
{
}

void
Gtk::Widget::class_init(GtkWidgetClass *g_class)
{
	Gtk::Object::class_init((GtkObjectClass*)g_class);
}

GType
Gtk::Widget::get_type()
{
	static GType type = 0;
	if (!type)
	{
		static const GTypeInfo info =
		{
			sizeof(GtkWidgetClass),
			(GBaseInitFunc)0,
			(GBaseFinalizeFunc)0,
			(GClassInitFunc)class_init,
			0, 0,
			sizeof(GtkWidget),
			0, /* preallocs */
			(GInstanceInitFunc)0
		};
		type = g_type_register_static(GTK_TYPE_WIDGET, "Test_Gtk_Widget", &info, GTypeFlags(0));
	}
	return type;
}

void
Gtk::Widget::show()
{
	gtk_widget_show(gtk_widget());
}

/*  Gtk::Editable
 */

void
Gtk::Editable::on_insert_text(const gchar *text, gint length, gint *position)
{
	g_print("Inside Gtk::Editable::on_insert_text...\n");
	GtkEditableClass *g_iface = (GtkEditableClass*)g_type_interface_peek_parent(gtk_editable_class());
	if (g_iface->insert_text)
	{
			g_print("Inside Gtk::Editable::on_insert_text's call to parent function...\n");
			g_iface->insert_text(gtk_editable(), text, length, position);
	}
}

/*  Gtk::EditableClass
 */

void
Gtk::EditableClass::init(GtkEditableClass *g_iface)
{
	g_print("Inside Gtk::EditableClass::init...\n");
	g_iface->insert_text = insert_text_proxy;
}

void
Gtk::EditableClass::insert_text_proxy(GtkEditable *editable, const gchar *text, gint length, gint *position)
{
	g_print("Inside Gtk::EditableClass::insert_text_proxy...\n");
	Glib::Object *object = static_cast<Glib::Object*>(g_object_get_data(G_OBJECT(editable), "test_pointer"));
	Gtk::Editable *tmp_editable = dynamic_cast<Gtk::Editable*>(object); /* needed for multiple inheritance */
	if (tmp_editable)
		tmp_editable->on_insert_text(text, length, position);
	else
	{
		GtkEditableClass *iface = static_cast<GtkEditableClass*>(g_type_interface_peek_parent(GTK_EDITABLE_GET_CLASS(editable)));
		if (iface->insert_text)
		{
			g_print("Inside Gtk::EditableClass::insert_text_proxy's call to parent function...\n");
			iface->insert_text(editable, text, length, position);
		}
	}
}

/*  Gtk::Entry
 */

Gtk::Entry::Entry(GtkEntry *entry)
: Widget((GtkWidget*)entry)
{
}

Gtk::Entry::Entry()
: Widget((GtkWidget*)g_object_new(get_type(), 0))
{
}

Gtk::Entry::~Entry()
{
}

void
Gtk::Entry::class_init(GtkEntryClass *g_class)
{
	Gtk::Widget::class_init((GtkWidgetClass*)g_class);
}

GType
Gtk::Entry::get_type()
{
	static GType type = 0;
	if (!type)
	{
		static const GTypeInfo entry_info =
		{
			sizeof(GtkEntryClass),
			(GBaseInitFunc)0,
			(GBaseFinalizeFunc)0,
			(GClassInitFunc)class_init,
			0, 0,
			sizeof(GtkEntry),
			0, /* preallocs */
			(GInstanceInitFunc)0
		};
		type = g_type_register_static(GTK_TYPE_ENTRY, "Test_Gtk_Entry", &entry_info, GTypeFlags(0));

		static const GInterfaceInfo editable_info =
		{
		 	(GInterfaceInitFunc)EditableClass::init,
			(GInterfaceFinalizeFunc)0,
			0
   		};
		g_type_add_interface_static(type, GTK_TYPE_EDITABLE, &editable_info);

		/* This is Gtk::CellEditable's bit but I didn't add it to the example. It and all the other interface
		   classes can been enacpsulated just like Gtk::Editable, and added to the appropriate widget's get_type().

		static const GInterfaceInfo cell_editable_info =
		{
		 	(GInterfaceInitFunc)CellEditableClass::init,
			(GInterfaceFinalizeFunc)0,
			0
		};
		g_type_add_interface_static(type, GTK_TYPE_CELL_EDITABLE, &cell_editable_info);

		*/
	}
	return type;
}

/*  Gtk::Container
 */

Gtk::Container::Container(GtkContainer *container)
: Widget((GtkWidget*)container)
{
}

Gtk::Container::~Container()
{
}

void
Gtk::Container::class_init(GtkContainerClass *g_class)
{
	Gtk::Widget::class_init((GtkWidgetClass*)g_class);
}

GType
Gtk::Container::get_type()
{
	static GType type = 0;
	if (!type)
	{
		static const GTypeInfo info =
		{
			sizeof(GtkContainerClass),
			(GBaseInitFunc)0,
			(GBaseFinalizeFunc)0,
			(GClassInitFunc)class_init,
			0, 0,
			sizeof(GtkContainer),
			0, /* preallocs */
			(GInstanceInitFunc)0
		};
		type = g_type_register_static(GTK_TYPE_CONTAINER, "Test_Gtk_Container", &info, GTypeFlags(0));
	}
	return type;
}

void
Gtk::Container::add(Widget& widget)
{
	gtk_container_add(gtk_container(), widget.gtk_widget());
}

/*  Gtk::Bin
 */

Gtk::Bin::Bin(GtkBin *bin)
: Container((GtkContainer*)bin)
{
}

Gtk::Bin::~Bin()
{
}

void
Gtk::Bin::class_init(GtkBinClass *g_class)
{
	Gtk::Container::class_init((GtkContainerClass*)g_class);
}

GType
Gtk::Bin::get_type()
{
	static GType type = 0;
	if (!type)
	{
		static const GTypeInfo info =
		{
			sizeof(GtkBinClass),
			(GBaseInitFunc)0,
			(GBaseFinalizeFunc)0,
			(GClassInitFunc)class_init,
			0, 0,
			sizeof(GtkBin),
			0, /* preallocs */
			(GInstanceInitFunc)0
		};
		type = g_type_register_static(GTK_TYPE_BIN, "Test_Gtk_Bin", &info, GTypeFlags(0));
	}
	return type;
}

/*  Gtk::Window
 */

Gtk::Window::Window(GtkWindow *window)
: Bin((GtkBin*)window)
{
	g_object_unref(g_object());
}

Gtk::Window::Window()
: Bin((GtkBin*)g_object_new(get_type(), 0))
{
	gtk_window()->type = GTK_WINDOW_TOPLEVEL;
	g_object_unref(g_object());
}

Gtk::Window::~Window()
{
}

void
Gtk::Window::class_init(GtkWindowClass *g_class)
{
	Gtk::Bin::class_init((GtkBinClass*)g_class);
}

GType
Gtk::Window::get_type()
{
	static GType type = 0;
	if (!type)
	{
		static const GTypeInfo info =
		{
			sizeof(GtkWindowClass),
			(GBaseInitFunc)0,
			(GBaseFinalizeFunc)0,
			(GClassInitFunc)class_init,
			0, 0,
			sizeof(GtkWindow),
			0, /* preallocs */
			(GInstanceInitFunc)0
		};
		type = g_type_register_static(GTK_TYPE_WINDOW, "Test_Gtk_Window", &info, GTypeFlags(0));
	}
	return type;
}

/////////////////////////////////////////////////////////////////////////////////////////////////////
//                                               Example                                           //
/////////////////////////////////////////////////////////////////////////////////////////////////////

class TestWindow : public Gtk::Window
{
	Gtk::Entry entry;

public:
	TestWindow()
	{
		gtk_signal_connect(GTK_OBJECT(g_object()), "destroy", &gtk_main_quit, 0);
		add(entry);
		entry.show();
		gtk_window_set_title(gtk_window(), "Interface Test");
		gtk_window_resize(gtk_window(), 250, 28);
		gtk_window_set_position(gtk_window(), GTK_WIN_POS_CENTER);
	}
};

int main(int argc, char *argv[])
{
	gtk_init(&argc, &argv);

	TestWindow *window = new TestWindow;
	window->show();

	gtk_main();
	return 0;
}





[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]