Wrapping interfaces that are implemented by unknown types.



I have already applied this patch to glibmm svn trunk, and I will soon
apply it to the 2.14 branch too. I thought I should post it here for
review.

It makes this code work, for instance:

  GtkTreeModel* cmodel = libsomethingspecial_tree_model_new()
  Glib::RefPtr<Gtk::TreeModel> model = Glib::wrap(cmodel);

This is helpful in libgdamm, which instantiates some objects for which
it does not even install headers.

-- 
murrayc murrayc com
www.murrayc.com
www.openismus.com
Index: ChangeLog
===================================================================
--- ChangeLog	(revision 445)
+++ ChangeLog	(working copy)
@@ -1,3 +1,16 @@
+2007-09-25  Murray Cumming  <murrayc murrayc com>
+
+	* glib/glibmm/wrap.cc:
+	* glib/glibmm/wrap.h: Added wrap_auto_interface<>(), which should 
+	be used by wrap() specializations for interfaces, so we create 
+	instances of the interface even if the derived C type is unknown to 
+	us.
+	* glib/glibmm/signalproxy_connectionnode.h: Do not wrap.h from here 
+	unnecessarily, to allow us to include objectbase.h in wrap.h, 
+	needed by the new templated method.
+	* tools/m4/class_interface.m4: Use wrap_auto_interface<>() 
+	instead of wrap_auto() for interfaces.
+
 2.14.0:
 
 2007-09-06  Daniel Elstner  <danielk openismus com>
Index: tools/m4/class_interface.m4
===================================================================
--- tools/m4/class_interface.m4	(revision 445)
+++ tools/m4/class_interface.m4	(working copy)
@@ -162,7 +162,7 @@ namespace Glib
 
 Glib::RefPtr<__NAMESPACE__::__CPPNAME__> wrap(__CNAME__`'* object, bool take_copy)
 {
-  return Glib::RefPtr<__NAMESPACE__::__CPPNAME__>( dynamic_cast<__NAMESPACE__::__CPPNAME__*> (Glib::wrap_auto ((GObject*)(object), take_copy)) );
+  return Glib::RefPtr<__NAMESPACE__::__CPPNAME__>( dynamic_cast<__NAMESPACE__::__CPPNAME__*> (Glib::wrap_auto_interface<__NAMESPACE__::__CPPNAME__> ((GObject*)(object), take_copy)) );
   //We use dynamic_cast<> in case of multiple inheritance.
 }
 
@@ -237,8 +237,14 @@ private:
 
 protected:
   __CPPNAME__`'(); // you must derive from this class
+
+public:
+  // This is public so that C++ wrapper instances can be
+  // created for C instances of unwrapped types.
+  // For instance, if an unexpected C type implements the C interface. 
   explicit __CPPNAME__`'(__CNAME__* castitem);
 
+protected:
 #endif /* DOXYGEN_SHOULD_SKIP_THIS */
 
 public:
Index: glib/glibmm/signalproxy_connectionnode.h
===================================================================
--- glib/glibmm/signalproxy_connectionnode.h	(revision 445)
+++ glib/glibmm/signalproxy_connectionnode.h	(working copy)
@@ -24,13 +24,18 @@
  */
 
 #include <sigc++/sigc++.h>
-#include <glibmm/wrap.h>
+#include <glib/gtypes.h>
 
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
 typedef struct _GObject GObject;
+typedef struct _GClosure GClosure;
+#endif //DOXYGEN_SHOULD_SKIP_THIS
 
 namespace Glib
 {
 
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+
 /** SignalProxyConnectionNode is a connection node for use with SignalProxy.
   * It lives between the layer of Gtk+ and libsigc++.
   * It is very much an internal class.
@@ -63,6 +68,8 @@ protected:
   GObject* object_;
 };
 
+#endif //DOXYGEN_SHOULD_SKIP_THIS
+
 } /* namespace Glib */
 
 
Index: glib/glibmm/wrap.cc
===================================================================
--- glib/glibmm/wrap.cc	(revision 445)
+++ glib/glibmm/wrap.cc	(working copy)
@@ -45,36 +45,6 @@ typedef std::vector<Glib::WrapNewFunctio
 
 static WrapFuncTable* wrap_func_table = 0;
 
-
-static Glib::ObjectBase* create_new_wrapper(GObject* object)
-{
-  g_return_val_if_fail(wrap_func_table != 0, 0);
-
-  bool gtkmm_wrapper_already_deleted = (bool)g_object_get_qdata((GObject*)object, Glib::quark_cpp_wrapper_deleted_);
-  if(gtkmm_wrapper_already_deleted)
-  {
-    g_warning("Glib::create_new_wrapper: Attempted to create a 2nd C++ wrapper for a C instance whose C++ wrapper has been deleted.");
-    return 0;
-  }
-
-  // Traverse upwards through the inheritance hierarchy
-  // to find the most-specialized wrap_new() for this GType.
-  //
-  for(GType type = G_OBJECT_TYPE(object); type != 0; type = g_type_parent(type))
-  {
-    // Look up the wrap table index stored in the type's static data.
-    // If a wrap_new() has been registered for the type then call it.
-    //
-    if(const gpointer idx = g_type_get_qdata(type, Glib::quark_))
-    {
-      const Glib::WrapNewFunction func = (*wrap_func_table)[GPOINTER_TO_UINT(idx)];
-      return (*func)(object);
-    }
-  }
-
-  return 0;
-}
-
 } // anonymous namespace
 
 
@@ -120,6 +90,48 @@ void wrap_register(GType type, WrapNewFu
   g_type_set_qdata(type, Glib::quark_, GUINT_TO_POINTER(idx));
 }
 
+
+Glib::ObjectBase* wrap_create_new_wrapper(GObject* object, bool exact_type_only)
+{
+  g_return_val_if_fail(wrap_func_table != 0, 0);
+
+  const bool gtkmm_wrapper_already_deleted = (bool)g_object_get_qdata((GObject*)object, Glib::quark_cpp_wrapper_deleted_);
+  if(gtkmm_wrapper_already_deleted)
+  {
+    g_warning("Glib::wrap_create_new_wrapper: Attempted to create a 2nd C++ wrapper for a C instance whose C++ wrapper has been deleted.");
+    return 0;
+  }
+
+  if(exact_type_only)
+  {
+    GType type = G_OBJECT_TYPE(object);
+    if(const gpointer idx = g_type_get_qdata(type, Glib::quark_))
+    {
+      const Glib::WrapNewFunction func = (*wrap_func_table)[GPOINTER_TO_UINT(idx)];
+      return (*func)(object);
+    }
+  }
+  else
+  {
+    // Traverse upwards through the inheritance hierarchy
+    // to find the most-specialized wrap_new() for this GType.
+    //
+    for(GType type = G_OBJECT_TYPE(object); type != 0; type = g_type_parent(type))
+    {
+      // Look up the wrap table index stored in the type's static data.
+      // If a wrap_new() has been registered for the type then call it.
+      //
+      if(const gpointer idx = g_type_get_qdata(type, Glib::quark_))
+      {
+        const Glib::WrapNewFunction func = (*wrap_func_table)[GPOINTER_TO_UINT(idx)];
+        return (*func)(object);
+      }
+    }
+  }
+
+  return 0;
+}
+
 // This is a factory function that converts any type to
 // its C++ wrapper instance by looking up a wrap_new() function in a map.
 //
@@ -129,13 +141,12 @@ ObjectBase* wrap_auto(GObject* object, b
     return 0;
 
   // Look up current C++ wrapper instance:
-  ObjectBase* pCppObject =
-      static_cast<ObjectBase*>(g_object_get_qdata(object, Glib::quark_));
+  ObjectBase* pCppObject = ObjectBase::_get_current_wrapper(object);
 
   if(!pCppObject)
   {
     // There's not already a wrapper: generate a new C++ instance.
-    pCppObject = create_new_wrapper(object);
+    pCppObject = wrap_create_new_wrapper(object);
 
     if(!pCppObject)
     {
Index: glib/glibmm/wrap.h
===================================================================
--- glib/glibmm/wrap.h	(revision 445)
+++ glib/glibmm/wrap.h	(working copy)
@@ -23,11 +23,13 @@
 
 #include <glib-object.h>
 #include <glibmm/refptr.h>
-
+#include <glibmm/objectbase.h>
 
 namespace Glib
 {
 
+#ifndef DOXYGEN_SHOULD_SKIP_THIS
+
 class ObjectBase;
 class Object;
 
@@ -46,6 +48,50 @@ void wrap_register(GType type, WrapNewFu
 // or automatically generate a new wrapper if there's none.
 Glib::ObjectBase* wrap_auto(GObject* object, bool take_copy = false);
 
+/** Create a C++ instance of a known C++ type that is mostly closely associated with the GType of the C object.
+ * @param object The C object which should be placed in a new C++ instance.
+ * @param exact_type_only If this is true then only create a C++ object if we know of a C++ type that exactly matches the C object's GType.
+ */
+Glib::ObjectBase* wrap_create_new_wrapper(GObject* object, bool exact_type_only = false);
+
+// Return the current C++ wrapper instance of the GObject,
+// or automatically generate a new wrapper if there's none.
+template<class TInterface>
+TInterface* wrap_auto_interface(GObject* object, bool take_copy = false)
+{
+  if(!object)
+    return 0;
+
+  // Look up current C++ wrapper instance:
+  ObjectBase* pCppObject = ObjectBase::_get_current_wrapper(object);
+
+  if(!pCppObject)
+  {
+    // There's not already a wrapper: generate a new C++ instance.
+    // We use exact_type_only=true avoid creating Glib::Object for interfaces of unknown implementation,
+    // because we do not want a C++ object that does not dynamic_cast to the expected interface type.
+    pCppObject = wrap_create_new_wrapper(object, true /* exact_type_only */);
+  }
+
+  //If no exact wrapper was created, 
+  //create an instance of the interface, 
+  //so we at least get the expected type:
+  TInterface* result = 0;
+  if(pCppObject)
+     result = dynamic_cast<TInterface*>(pCppObject);
+  else
+     result = new TInterface((typename TInterface::BaseObjectType*)object);
+
+  // take_copy=true is used where the GTK+ function doesn't do
+  // an extra ref for us, and always for plain struct members.
+  if(take_copy && result)
+    result->reference();
+
+  return result;
+}
+
+#endif //DOXYGEN_SHOULD_SKIP_THIS
+
 // Get a C++ instance that wraps the C instance.
 // This always returns the same C++ instance for the same C instance.
 // Each wrapper has it's own override of Glib::wrap().


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