Re: (glibmm) Deeper level of inheritance in own type of objects - how-to or plans to implement?



Attached, a C++ program. Compile instruction at beginning. Comments about expected results/problems, in it as well.
First run - uncomment test_5() call in main(). 2nd run, comment it back.
I hope it makes my rant somewhat clearer.
 Cristi

On Wed, Nov 26, 2008 at 1:06 PM, Armin Burgmeier <armin arbur net> wrote:
On Wed, 2008-11-26 at 03:00 +0200, Cristi P wrote:
> I struggled some time ago with doing object inheritance at more than
> one level, like:
> Glib::Object -> MyObjectA (w/ some properties for example) ->
> MyObjectB (w/ additional properties and/or overriden [virtual]
> functions)
> While the gtkmm says about using "inheritance" I couldn't solve the
> problem the way I expect from a C++ library. So, unless I'm mistaken
> somewhere, it *almost* is impossible.
> At that time I did some hard stuff to overcome that problem (with
> implications on how you write your objects) but now I'm getting in
> even bigger problems, when
> instead of starting from Glib::Object I start from some other C++
> wrapper, like, for example,
> Clutter::Group -> MyObjectA -> MyObjectB
>
> This is really annoying since I want to use C++ w/ the various
> wrappers in more then a simplistic GUI wrapper and with one level of
> inheritance  :-(
>
> So far the problems that I think I saw:
> a) it seems that the glibmm::object* source files (or at least some
> ObjectBase constructors) are assuming derived objects are mostly C++
> wrappers or one level deeper
> b) (from memory) saw some strange things about declaring the wrapper's
> type as being the C actual type instead of the just created type ?
>    I think there was also a comment like "allow g_peek_class..." to
> work !?
> c) wrappers around methods want to "peek" at the original C object to
> call some functions for example, and are assuming current object has
> its parent type the C object, which, in case it runs on
> object MyObjectB, it is a wrong assumption.

I don't really understand what your problem is. Could you be more
specific on what the actual problem is when you have a Glib::Object -> A
-> B inheritance tree? And maybe provide a compilable test case?

Maybe you need to create an own GType for each of your derived classes.
This can be accomplished by calling the Glib::ObjectBase constructor
with the type name as a string. But this is only a guess.

>   Can't the wrapping code gkmmproc is building,  try, since it really
> knows (or could) the C type, work directly on that type (w/
> g_peek_class...) instead of relying on the
>   actual object instance type being derived directly from the C type?
> d) I also saw some comments about skiping some virtual function calls.
>       I wonder:
>      1) what's the optimization they're talking about? (haven't
> checked sources enough)

When a C++ wrapper is created, such as Gtk::Window, and a C virtual
function is called on the underlying GObject, such as the default signal
handler for the "show" signal, then we can skip calling the virtual
on_show() member function of the C++ object, because it cannot be
overriden anyway.

However, if the same (C) virtual function is called on an instance of a
derived class, such as ExampleWindow inheriting from Gtk::Window, then
ExampleWindow could have overriden the on_show() (C++) virtual function,
so we need to call it.

>      2) (would have to check) - can they also be a source of problems
> for the thing I want to have fixed?

I don't think so.

> Anyway, I might be mistaken, and if there is a method to accomplish
> what I said in the beginning that I want, I'd be glad to hear it!
>
> Thanks.

Armin


#include <glibmm.h>
#include <gtkmm.h>

// to compile:
// g++ -g `pkg-config --cflags glibmm-2.4 gtkmm-2.4` `pkg-config --libs glibmm-2.4 gtkmm-2.4` demo.cpp

// ====== Some utility functions
static void print_type(const GType ot)
{
	GType opt = g_type_parent(ot);
	printf("type=%d(%s) baseType=%d(%s)",
		(int)ot, g_type_name(ot), (int)opt, g_type_name(opt));
}


static void print_object(const Glib::Object& o, const char *ending = "\n")
{
	GType ot = G_OBJECT_TYPE(o.gobj());
	printf("object: %p, ", o.gobj());
	print_type(ot);
	printf("%s", ending);
}


static void print_prop(const Glib::Object& o, const char* n)
{
  	GParamSpec* param_spec = g_object_class_find_property(G_OBJECT_GET_CLASS(o.gobj()), n);

	print_object(o, "");
	printf(" property: %s found=%s\n", n, (param_spec? "yes":"false"));
	
	// SEEME: unref the param_spec?
}


// ==============

struct Object1: public Glib::Object
{
	Object1()
	{
	}
};

struct Object2: public Glib::Object
{
	Object2()
	{
	}
};

void test_1()
{
	printf("=== test 1\n");
	Object1 o1;
	Object2 o2;
	print_object(o1);
	print_object(o2);
/*
	Somewhat expected results were to have the types of these 2 be different (automatically registered) and parent type to be GOBject.
	But let's say it is ok and continue...
	Well, almost, I don't understand why it is not ok to have them at least declared as gtkmm_Object
*/
}


// =================================
struct Object2_1: public Glib::Object
{
	Object2_1(): 
		ObjectBase("Object2_1") //as per docs, this seems the way to create a new type.
	{
	}
};

struct Object2_2: public Glib::Object
{
	Object2_2(): 
		ObjectBase("Object2_2")
	{
	}
};

void test_2()
{
	printf("=== test 2\n");
	Object2_1 o1;
	print_object(o1);
	Object2_2 o2;
	print_object(o2);
/*
	Expected results, it is ok, let's continue/.
	Well, almost, I don't understand why it is not ok to have base_type be gtkmm_Object and code insist on getting over that.
*/
}

// ===============================
struct Object3_1: public Glib::Object
{
	Object3_1(): 
		ObjectBase("Object3_1") //as per docs, this seems the way to create a new type.
		, prop(*this, "prop1")
	{
	}
	Glib::Property<gint32> prop;
};

struct Object3_2: public Glib::Object
{
	Object3_2(): 
		ObjectBase("Object3_2")
		, prop(*this, "prop2")
	{
	}
	Glib::Property<gint32> prop;
};

void test_3()
{
	printf("=== test 3\n");
	Object3_1 o1;
	print_prop(o1, "prop1");
	print_prop(o1, "prop2");
	Object3_2 o2;
	print_prop(o2, "prop1");
	print_prop(o2, "prop2");
/*
	Expected results (besides comments from previous test), let's continue/
*/
}

// ================
struct Object4: public Object3_1
{
	Object4()
		: ObjectBase("Object4")
	{
	}

};

void test_4()
{
	printf("=== test 4\n");
	Object3_1 o1;
	print_prop(o1, "prop1");
	print_prop(o1, "prop2");

	Object4 o4;
	print_prop(o4, "prop1");
	print_prop(o4, "prop2");
/*
	BAD... Object4 is still declared as deriving from Object (it is a similar class to Object3_1, to C and C++  glib :-( )
	Checking the code, I think the following areas are having some problems:
	- register_derived_type - since it takes the type name as the base type name prefixed by "Gtkmm_", it
		seems that the correct name should be: "register_cpp_wrapper_type" ?
		- and in object*.* files it is called w/ base type always being G_OBJECT anyway?
	- Object() constructor - you can override the type to not be G_OBJECT_TYPE only if you setup a custom type name, which is ok,
		but, then, why call a "clone_custom_type" to get this object's type? When it is supposed to be already created and
		given by its own ObjectClass data member?
		- and, bad, we clone from the G_OBJECT_TYPE, due to the ObjectClass::init() call first
		- I can guess this constructor should be only for C++ objects derived only from G_OBJECT?
			- checking into glibmm and gtkmm - couldn't find a call to it
		
	- clone_custom_type: 
		- see above: "why *clone*"?
			- Q: if your program said you want a specific type_name, then WHY we *clone* that type?
		- it does a very bad assumption: that you're deriving your object directly from a wrapper, and as such
		  it takes as your parent type one type above the level you'd want.
			While it might be ok for objects derived immediately from a wrapper, it will deny an inheritance
			tree bigger than that (it won't correctly declare your real parent class, but take one from a level above)
		- how to fix: 2 sugestions:
			-a) Don't care about getting rid of the C++ wrapper types from the chain. so... use directly gtype_
			-b) make somehow the OBjectClass know this *current* object and only this one (no derived ones) know about it being a c++ wrapper
			(I think that all C++ direct wrappers are instantiated w/ custom_type_name = 0, maybe you could use that, else, buildup
			some other ObjectBase constructor w/ an additional "is_wrapper" parameter?)
				Note that knowing if the object is a C++ wrapper could have more benefits!

	Other comments:
	- look how hard also the inheritance problem is handled in the wrappers - lot of fidling with *both* the ObjectClass and the various
		constructors and the get_type/get_base_type overloaded functions :-(
	- in order for virtual functions of your derived objects to work, it looks like you have to have setup a custom_name to your class so that
		the is_derived_() returns true (see, for example, why, by looking at virtual Widget::on_button_pressed_event(...)
		- but in the same time, if you give it a real custom_name that is not the anonymous name, then you're going to actually
		 have created a *clone* of your actual object type - see Object::Object() and OBject::Object(ConstructParams)

	Probably, by looking at code and current gtk wrappers, to allow you to have inheritance down in the C glib 
	way for your own class tree, you'd have to define:
		- your own OBjectClass
		- your own get_type and get_base_type static functions
		- both simple constructor and Constructor(Glib::ConstructorParams)
*/
}

// ================
struct MyLabel: public Gtk::Label
{
	MyLabel()
		: Gtk::Label(),
		property_automatic_space_strip(*this, "automatic-space-strip")
	{
	}

	Glib::Property<gint32> property_automatic_space_strip;	
};


void test_5()
{
	printf("=== test 5\n");
	MyLabel l;
	print_object(l);
	l.set_text("blah");
/*
	You're going to get:
		(process:24006): GLib-GObject-CRITICAL **: g_object_class_install_property: assertion `class->set_property != NULL' failed

	I guess you have to do something else to obtain the desired effect of having a new GType for this class and be able to add properties)
*/
}

// ==================
struct MyLabel2: public Gtk::Label
{
	MyLabel2()
		: 
		Glib::ObjectBase("MyLabel2"),
		Gtk::Label(),
		property_automatic_space_strip(*this, "automatic-space-strip")
	{
		property_automatic_space_strip.set_value(1);
	}

	Glib::Property<gint32> property_automatic_space_strip;	
};

struct MyLabel3: public MyLabel2
{
	MyLabel3()
		: 
		Glib::ObjectBase("MyLabel3"),
		MyLabel2(),
		property_2(*this, "prop2")
	{
		property_2.set_value(1);
	}

	Glib::Property<gint32> property_2;	
};


void test_6()
{
	printf("=== test 6\n");
	MyLabel2 l2;
	print_object(l2);
	l2.set_text("blah");
	print_prop(l2, "prop2");

	MyLabel3 l3;
	print_object(l3);
	l3.set_text("blah");
	l3.property_automatic_space_strip.set_value(10); // works ok
	print_prop(l3, "automatic-space-strip");
/*
Type of MyLabel2 - ok (except for not wanting to use gtkmm_Label.
type of MyLabel3 - WRONG :-(
And because of that, you'd somehow expect, since the types are identical, to not have l3.prop_2 present (unless you 
	consider these 2 new properties to be "dynamic"	properties - which they aren't)
*/
}

// ================
int main()
{
	Glib::init();

	test_1();
	test_2();
	test_3();
	test_4();
	//test_5(); // UNCOMMENT before checking test_6()
	test_6();
	return 0;
}



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