unicode: Cleanup implementation
diff --git a/src/Makefile.am b/src/Makefile.am
index 2fbf278..70c51d1 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -24,8 +24,8 @@
 	hb-ot-head-private.hh \
 	hb-private.h \
 	hb-shape.cc \
-	hb-unicode.c \
-	hb-unicode-private.h \
+	hb-unicode.cc \
+	hb-unicode-private.hh \
 	$(NULL)
 HBHEADERS = \
 	hb.h \
@@ -65,7 +65,7 @@
 HBCFLAGS += $(GLIB_CFLAGS)
 HBLIBS   += $(GLIB_LIBS)
 HBSOURCES += \
-	hb-glib.c \
+	hb-glib.cc \
 	$(NULL)
 HBHEADERS += \
 	hb-glib.h \
@@ -76,7 +76,7 @@
 HBCFLAGS += $(ICU_CFLAGS)
 HBLIBS   += $(ICU_LIBS)
 HBSOURCES += \
-	hb-icu.c \
+	hb-icu.cc \
 	$(NULL)
 HBHEADERS += \
 	hb-icu.h \
diff --git a/src/hb-buffer-private.hh b/src/hb-buffer-private.hh
index 2b063b7..dfcc45d 100644
--- a/src/hb-buffer-private.hh
+++ b/src/hb-buffer-private.hh
@@ -30,7 +30,7 @@
 
 #include "hb-private.h"
 #include "hb-buffer.h"
-#include "hb-unicode-private.h"
+#include "hb-unicode-private.hh"
 
 HB_BEGIN_DECLS
 
diff --git a/src/hb-glib.c b/src/hb-glib.cc
similarity index 95%
rename from src/hb-glib.c
rename to src/hb-glib.cc
index 1905637..5bebf5e 100644
--- a/src/hb-glib.c
+++ b/src/hb-glib.cc
@@ -28,20 +28,28 @@
 
 #include "hb-glib.h"
 
-#include "hb-unicode-private.h"
+#include "hb-unicode-private.hh"
 
 #include <glib.h>
 
 HB_BEGIN_DECLS
 
 
-static hb_codepoint_t
-hb_glib_get_mirroring (hb_unicode_funcs_t *ufuncs,
-                       hb_codepoint_t      unicode,
-                       void               *user_data)
+static unsigned int
+hb_glib_get_combining_class (hb_unicode_funcs_t *ufuncs,
+                             hb_codepoint_t      unicode,
+                             void               *user_data)
+
 {
-  g_unichar_get_mirror_char (unicode, &unicode);
-  return unicode;
+  return g_unichar_combining_class (unicode);
+}
+
+static unsigned int
+hb_glib_get_eastasian_width (hb_unicode_funcs_t *ufuncs,
+                             hb_codepoint_t      unicode,
+                             void               *user_data)
+{
+  return g_unichar_iswide (unicode) ? 2 : 1;
 }
 
 static hb_unicode_general_category_t
@@ -50,7 +58,17 @@
                               void               *user_data)
 
 {
-  return g_unichar_type (unicode);
+  /* hb_unicode_general_category_t and GUnicodeType are identical */
+  return (hb_unicode_general_category_t) g_unichar_type (unicode);
+}
+
+static hb_codepoint_t
+hb_glib_get_mirroring (hb_unicode_funcs_t *ufuncs,
+                       hb_codepoint_t      unicode,
+                       void               *user_data)
+{
+  g_unichar_get_mirror_char (unicode, &unicode);
+  return unicode;
 }
 
 static hb_script_t
@@ -167,7 +185,7 @@
   MATCH_SCRIPT (LISU);                   /* Lisu */
   MATCH_SCRIPT (MEETEI_MAYEK);           /* Mtei */
   MATCH_SCRIPT (OLD_SOUTH_ARABIAN);      /* Sarb */
-#if GLIB_CHECK_VERSION(2,28,0)
+#if GLIB_CHECK_VERSION(2,27,92)
   MATCH_SCRIPT (OLD_TURKIC);             /* Orkh */
 #else
   MATCH_SCRIPT2(OLD_TURKISH, OLD_TURKIC);/* Orkh */
@@ -190,33 +208,16 @@
   return HB_SCRIPT_UNKNOWN;
 }
 
-static unsigned int
-hb_glib_get_combining_class (hb_unicode_funcs_t *ufuncs,
-                             hb_codepoint_t      unicode,
-                             void               *user_data)
-
-{
-  return g_unichar_combining_class (unicode);
-}
-
-static unsigned int
-hb_glib_get_eastasian_width (hb_unicode_funcs_t *ufuncs,
-                             hb_codepoint_t      unicode,
-                             void               *user_data)
-{
-  return g_unichar_iswide (unicode) ? 2 : 1;
-}
-
 static hb_unicode_funcs_t glib_ufuncs = {
   HB_REFERENCE_COUNT_INVALID, /* ref_count */
-  NULL,
+  NULL, /* parent */
   TRUE, /* immutable */
   {
-    hb_glib_get_general_category, NULL, NULL,
-    hb_glib_get_combining_class, NULL, NULL,
-    hb_glib_get_mirroring, NULL, NULL,
-    hb_glib_get_script, NULL, NULL,
-    hb_glib_get_eastasian_width, NULL, NULL
+    hb_glib_get_combining_class,
+    hb_glib_get_eastasian_width,
+    hb_glib_get_general_category,
+    hb_glib_get_mirroring,
+    hb_glib_get_script
   }
 };
 
diff --git a/src/hb-icu.c b/src/hb-icu.cc
similarity index 97%
rename from src/hb-icu.c
rename to src/hb-icu.cc
index 6bc3339..4978bf5 100644
--- a/src/hb-icu.c
+++ b/src/hb-icu.cc
@@ -29,7 +29,7 @@
 
 #include "hb-icu.h"
 
-#include "hb-unicode-private.h"
+#include "hb-unicode-private.hh"
 
 #include <unicode/uversion.h>
 #include <unicode/uchar.h>
@@ -38,14 +38,6 @@
 HB_BEGIN_DECLS
 
 
-static hb_codepoint_t
-hb_icu_get_mirroring (hb_unicode_funcs_t *ufuncs,
-		      hb_codepoint_t      unicode,
-		      void               *user_data)
-{
-  return u_charMirror(unicode);
-}
-
 static unsigned int
 hb_icu_get_combining_class (hb_unicode_funcs_t *ufuncs,
 			    hb_codepoint_t      unicode,
@@ -125,6 +117,14 @@
   return HB_UNICODE_GENERAL_CATEGORY_UNASSIGNED;
 }
 
+static hb_codepoint_t
+hb_icu_get_mirroring (hb_unicode_funcs_t *ufuncs,
+		      hb_codepoint_t      unicode,
+		      void               *user_data)
+{
+  return u_charMirror(unicode);
+}
+
 static hb_script_t
 hb_icu_get_script (hb_unicode_funcs_t *ufuncs,
 		   hb_codepoint_t      unicode,
@@ -264,14 +264,14 @@
 
 static hb_unicode_funcs_t icu_ufuncs = {
   HB_REFERENCE_COUNT_INVALID, /* ref_count */
-  NULL,
+  NULL, /* parent */
   TRUE, /* immutable */
   {
-    hb_icu_get_general_category, NULL, NULL,
-    hb_icu_get_combining_class, NULL, NULL,
-    hb_icu_get_mirroring, NULL, NULL,
-    hb_icu_get_script, NULL, NULL,
-    hb_icu_get_eastasian_width, NULL, NULL
+    hb_icu_get_combining_class,
+    hb_icu_get_eastasian_width,
+    hb_icu_get_general_category,
+    hb_icu_get_mirroring,
+    hb_icu_get_script
   }
 };
 
diff --git a/src/hb-ot-shape.cc b/src/hb-ot-shape.cc
index 2ffb076..589dee3 100644
--- a/src/hb-ot-shape.cc
+++ b/src/hb-ot-shape.cc
@@ -143,16 +143,13 @@
 static void
 hb_set_unicode_props (hb_ot_shape_context_t *c)
 {
-  hb_unicode_get_general_category_func_t get_general_category = c->buffer->unicode->v.get_general_category;
-  hb_unicode_get_combining_class_func_t get_combining_class = c->buffer->unicode->v.get_combining_class;
+  hb_unicode_funcs_t *unicode = c->buffer->unicode;
   hb_glyph_info_t *info = c->buffer->info;
 
   unsigned int count = c->buffer->len;
   for (unsigned int i = 1; i < count; i++) {
-    info[i].general_category() = get_general_category (c->buffer->unicode, info[i].codepoint,
-                                                       c->buffer->unicode->v.get_general_category_data);
-    info[i].combining_class() = get_combining_class (c->buffer->unicode, info[i].codepoint,
-                                                     c->buffer->unicode->v.get_combining_class_data);
+    info[i].general_category() = unicode->get_general_category (info[i].codepoint);
+    info[i].combining_class() = unicode->get_combining_class (info[i].codepoint);
   }
 }
 
@@ -193,7 +190,7 @@
 static void
 hb_mirror_chars (hb_ot_shape_context_t *c)
 {
-  hb_unicode_get_mirroring_func_t get_mirroring = c->buffer->unicode->v.get_mirroring;
+  hb_unicode_funcs_t *unicode = c->buffer->unicode;
 
   if (HB_DIRECTION_IS_FORWARD (c->target_direction))
     return;
@@ -202,8 +199,7 @@
 
   unsigned int count = c->buffer->len;
   for (unsigned int i = 0; i < count; i++) {
-    hb_codepoint_t codepoint = get_mirroring (c->buffer->unicode, c->buffer->info[i].codepoint,
-                                              c->buffer->unicode->v.get_mirroring_data);
+    hb_codepoint_t codepoint = unicode->get_mirroring (c->buffer->info[i].codepoint);
     if (likely (codepoint == c->buffer->info[i].codepoint))
       c->buffer->info[i].mask |= rtlm_mask; /* XXX this should be moved to before setting user-feature masks */
     else
diff --git a/src/hb-shape.cc b/src/hb-shape.cc
index 9d9bf25..2b536fa 100644
--- a/src/hb-shape.cc
+++ b/src/hb-shape.cc
@@ -74,10 +74,10 @@
 
   /* If script is set to INVALID, guess from buffer contents */
   if (buffer->props.script == HB_SCRIPT_INVALID) {
-    hb_unicode_get_script_func_t get_script = buffer->unicode->v.get_script;
+    hb_unicode_funcs_t *unicode = buffer->unicode;
     unsigned int count = buffer->len;
     for (unsigned int i = 0; i < count; i++) {
-      hb_script_t script = get_script (buffer->unicode, buffer->info[i].codepoint, buffer->unicode->v.get_script_data);
+      hb_script_t script = unicode->get_script (buffer->info[i].codepoint);
       if (likely (script != HB_SCRIPT_COMMON &&
 		  script != HB_SCRIPT_INHERITED &&
 		  script != HB_SCRIPT_UNKNOWN)) {
diff --git a/src/hb-unicode-private.h b/src/hb-unicode-private.h
deleted file mode 100644
index 456d8de..0000000
--- a/src/hb-unicode-private.h
+++ /dev/null
@@ -1,77 +0,0 @@
-/*
- * Copyright (C) 2009  Red Hat, Inc.
- * Copyright © 2011 Codethink Limited
- *
- *  This is part of HarfBuzz, a text shaping library.
- *
- * Permission is hereby granted, without written agreement and without
- * license or royalty fees, to use, copy, modify, and distribute this
- * software and its documentation for any purpose, provided that the
- * above copyright notice and the following two paragraphs appear in
- * all copies of this software.
- *
- * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
- * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
- * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
- * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
- * DAMAGE.
- *
- * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
- * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
- * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
- * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
- *
- * Red Hat Author(s): Behdad Esfahbod
- * Codethink Author(s): Ryan Lortie
- */
-
-#ifndef HB_UNICODE_PRIVATE_H
-#define HB_UNICODE_PRIVATE_H
-
-#include "hb-private.h"
-
-#include "hb-unicode.h"
-
-HB_BEGIN_DECLS
-
-
-/*
- * hb_unicode_funcs_t
- */
-
-struct _hb_unicode_funcs_t {
-  hb_reference_count_t ref_count;
-  hb_unicode_funcs_t *parent;
-
-  hb_bool_t immutable;
-
-  struct {
-    hb_unicode_get_general_category_func_t	get_general_category;
-    void                                       *get_general_category_data;
-    hb_destroy_func_t                           get_general_category_destroy;
-
-    hb_unicode_get_combining_class_func_t	get_combining_class;
-    void                                       *get_combining_class_data;
-    hb_destroy_func_t                           get_combining_class_destroy;
-
-    hb_unicode_get_mirroring_func_t		get_mirroring;
-    void                                       *get_mirroring_data;
-    hb_destroy_func_t                           get_mirroring_destroy;
-
-    hb_unicode_get_script_func_t		get_script;
-    void                                       *get_script_data;
-    hb_destroy_func_t                           get_script_destroy;
-
-    hb_unicode_get_eastasian_width_func_t	get_eastasian_width;
-    void                                       *get_eastasian_width_data;
-    hb_destroy_func_t                           get_eastasian_width_destroy;
-  } v;
-};
-
-extern HB_INTERNAL hb_unicode_funcs_t _hb_unicode_funcs_nil;
-
-
-HB_END_DECLS
-
-#endif /* HB_UNICODE_PRIVATE_H */
diff --git a/src/hb-unicode-private.hh b/src/hb-unicode-private.hh
new file mode 100644
index 0000000..67a60f5d
--- /dev/null
+++ b/src/hb-unicode-private.hh
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2009  Red Hat, Inc.
+ * Copyright © 2011 Codethink Limited
+ * Copyright (C) 2010  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ * Codethink Author(s): Ryan Lortie
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_UNICODE_PRIVATE_HH
+#define HB_UNICODE_PRIVATE_HH
+
+#include "hb-private.h"
+
+#include "hb-unicode.h"
+
+HB_BEGIN_DECLS
+
+
+/*
+ * hb_unicode_funcs_t
+ */
+
+struct _hb_unicode_funcs_t {
+  hb_reference_count_t ref_count;
+  hb_unicode_funcs_t *parent;
+
+  hb_bool_t immutable;
+
+#define IMPLEMENT(return_type, name) \
+  inline return_type \
+  get_##name (hb_codepoint_t unicode) \
+  { return this->get.name (this, unicode, this->user_data.name); }
+
+  IMPLEMENT (unsigned int, combining_class)
+  IMPLEMENT (unsigned int, eastasian_width)
+  IMPLEMENT (hb_unicode_general_category_t, general_category)
+  IMPLEMENT (hb_codepoint_t, mirroring)
+  IMPLEMENT (hb_script_t, script)
+
+#undef IMPLEMENT
+
+  /* Don't access these directly.  Call get_*() instead. */
+
+  struct {
+    hb_unicode_get_combining_class_func_t	combining_class;
+    hb_unicode_get_eastasian_width_func_t	eastasian_width;
+    hb_unicode_get_general_category_func_t	general_category;
+    hb_unicode_get_mirroring_func_t		mirroring;
+    hb_unicode_get_script_func_t		script;
+  } get;
+
+  struct {
+    void 					*combining_class;
+    void 					*eastasian_width;
+    void 					*general_category;
+    void 					*mirroring;
+    void 					*script;
+  } user_data;
+
+  struct {
+    hb_destroy_func_t				combining_class;
+    hb_destroy_func_t				eastasian_width;
+    hb_destroy_func_t				general_category;
+    hb_destroy_func_t				mirroring;
+    hb_destroy_func_t				script;
+  } destroy;
+};
+
+extern HB_INTERNAL hb_unicode_funcs_t _hb_unicode_funcs_nil;
+
+
+HB_END_DECLS
+
+#endif /* HB_UNICODE_PRIVATE_HH */
diff --git a/src/hb-unicode.c b/src/hb-unicode.c
deleted file mode 100644
index e047ef7..0000000
--- a/src/hb-unicode.c
+++ /dev/null
@@ -1,256 +0,0 @@
-/*
- * Copyright (C) 2009  Red Hat, Inc.
- * Copyright © 2011 Codethink Limited
- *
- *  This is part of HarfBuzz, a text shaping library.
- *
- * Permission is hereby granted, without written agreement and without
- * license or royalty fees, to use, copy, modify, and distribute this
- * software and its documentation for any purpose, provided that the
- * above copyright notice and the following two paragraphs appear in
- * all copies of this software.
- *
- * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
- * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
- * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
- * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
- * DAMAGE.
- *
- * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
- * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
- * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
- * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
- *
- * Red Hat Author(s): Behdad Esfahbod
- * Codethink Author(s): Ryan Lortie
- */
-
-#include "hb-private.h"
-
-#include "hb-unicode-private.h"
-
-HB_BEGIN_DECLS
-
-
-/*
- * hb_unicode_funcs_t
- */
-
-static hb_codepoint_t
-hb_unicode_get_mirroring_nil (hb_unicode_funcs_t *ufuncs    HB_UNUSED,
-                              hb_codepoint_t      unicode   HB_UNUSED,
-                              void               *user_data HB_UNUSED)
-{
-  return unicode;
-}
-
-static hb_unicode_general_category_t
-hb_unicode_get_general_category_nil (hb_unicode_funcs_t *ufuncs    HB_UNUSED,
-                                     hb_codepoint_t      unicode   HB_UNUSED,
-                                     void               *user_data HB_UNUSED)
-{
-  return HB_UNICODE_GENERAL_CATEGORY_OTHER_LETTER;
-}
-
-static hb_script_t
-hb_unicode_get_script_nil (hb_unicode_funcs_t *ufuncs    HB_UNUSED,
-                           hb_codepoint_t      unicode   HB_UNUSED,
-                           void               *user_data HB_UNUSED)
-{
-  return HB_SCRIPT_UNKNOWN;
-}
-
-static unsigned int
-hb_unicode_get_combining_class_nil (hb_unicode_funcs_t *ufuncs    HB_UNUSED,
-                                    hb_codepoint_t      unicode   HB_UNUSED,
-                                    void               *user_data HB_UNUSED)
-{
-  return 0;
-}
-
-static unsigned int
-hb_unicode_get_eastasian_width_nil (hb_unicode_funcs_t *ufuncs    HB_UNUSED,
-                                    hb_codepoint_t      unicode   HB_UNUSED,
-                                    void               *user_data HB_UNUSED)
-{
-  return 1;
-}
-
-hb_unicode_funcs_t _hb_unicode_funcs_nil = {
-  HB_REFERENCE_COUNT_INVALID, /* ref_count */
-  NULL, /* parent */
-  TRUE, /* immutable */
-  {
-    hb_unicode_get_general_category_nil, NULL, NULL,
-    hb_unicode_get_combining_class_nil, NULL, NULL,
-    hb_unicode_get_mirroring_nil, NULL, NULL,
-    hb_unicode_get_script_nil, NULL, NULL,
-    hb_unicode_get_eastasian_width_nil, NULL, NULL
-  }
-};
-
-hb_unicode_funcs_t *
-hb_unicode_funcs_create (hb_unicode_funcs_t *parent)
-{
-  hb_unicode_funcs_t *ufuncs;
-
-  if (!HB_OBJECT_DO_CREATE (hb_unicode_funcs_t, ufuncs))
-    return &_hb_unicode_funcs_nil;
-
-  if (parent != NULL) {
-    ufuncs->parent = hb_unicode_funcs_reference (parent);
-    hb_unicode_funcs_make_immutable (parent);
-    ufuncs->v = parent->v;
-
-    /* Clear out the destroy notifies from our parent.
-     *
-     * We don't want to destroy the user_data twice and since we hold a
-     * reference on our parent then we know that the user_data will
-     * survive for at least as long as we do anyway.
-     */
-    ufuncs->v.get_general_category_destroy = NULL;
-    ufuncs->v.get_combining_class_destroy = NULL;
-    ufuncs->v.get_mirroring_destroy = NULL;
-    ufuncs->v.get_script_destroy = NULL;
-    ufuncs->v.get_eastasian_width_destroy = NULL;
-  } else {
-    ufuncs->v = _hb_unicode_funcs_nil.v;
-  }
-
-  return ufuncs;
-}
-
-hb_unicode_funcs_t *
-hb_unicode_funcs_reference (hb_unicode_funcs_t *ufuncs)
-{
-  HB_OBJECT_DO_REFERENCE (ufuncs);
-}
-
-unsigned int
-hb_unicode_funcs_get_reference_count (hb_unicode_funcs_t *ufuncs)
-{
-  HB_OBJECT_DO_GET_REFERENCE_COUNT (ufuncs);
-}
-
-void
-hb_unicode_funcs_destroy (hb_unicode_funcs_t *ufuncs)
-{
-  HB_OBJECT_DO_DESTROY (ufuncs);
-
-  if (ufuncs->parent != NULL)
-    hb_unicode_funcs_destroy (ufuncs->parent);
-
-  if (ufuncs->v.get_general_category_destroy != NULL)
-    ufuncs->v.get_general_category_destroy (ufuncs->v.get_general_category_data);
-
-  if (ufuncs->v.get_combining_class_destroy != NULL)
-    ufuncs->v.get_combining_class_destroy (ufuncs->v.get_combining_class_data);
-
-  if (ufuncs->v.get_mirroring_destroy != NULL)
-    ufuncs->v.get_mirroring_destroy (ufuncs->v.get_mirroring_data);
-
-  if (ufuncs->v.get_script_destroy != NULL)
-    ufuncs->v.get_script_destroy (ufuncs->v.get_script_data);
-
-  if (ufuncs->v.get_eastasian_width_destroy != NULL)
-    ufuncs->v.get_eastasian_width_destroy (ufuncs->v.get_eastasian_width_data);
-
-  free (ufuncs);
-}
-
-hb_unicode_funcs_t *
-hb_unicode_funcs_get_parent (hb_unicode_funcs_t *ufuncs)
-{
-  return ufuncs->parent;
-}
-
-void
-hb_unicode_funcs_make_immutable (hb_unicode_funcs_t *ufuncs)
-{
-  if (HB_OBJECT_IS_INERT (ufuncs))
-    return;
-
-  ufuncs->immutable = TRUE;
-}
-
-hb_bool_t
-hb_unicode_funcs_is_immutable (hb_unicode_funcs_t *ufuncs)
-{
-  return ufuncs->immutable;
-}
-
-
-#define SETTER(name) \
-void                                                                           \
-hb_unicode_funcs_set_##name##_func (hb_unicode_funcs_t             *ufuncs,    \
-                                    hb_unicode_get_##name##_func_t  func,      \
-                                    void                           *user_data, \
-                                    hb_destroy_func_t               destroy)   \
-{                                                                              \
-  if (ufuncs->immutable)                                                       \
-    return;                                                                    \
-                                                                               \
-  if (func != NULL) {                                                          \
-    ufuncs->v.get_##name = func;                                               \
-    ufuncs->v.get_##name##_data = user_data;                                   \
-    ufuncs->v.get_##name##_destroy = destroy;                                  \
-  } else if (ufuncs->parent != NULL) {                                         \
-    ufuncs->v.get_##name = ufuncs->parent->v.get_##name;                       \
-    ufuncs->v.get_##name##_data = ufuncs->parent->v.get_##name##_data;;        \
-    ufuncs->v.get_##name##_destroy = NULL;                                     \
-  } else {                                                                     \
-    ufuncs->v.get_##name = hb_unicode_get_##name##_nil;                        \
-    ufuncs->v.get_##name##_data = NULL;                                        \
-    ufuncs->v.get_##name##_destroy = NULL;                                     \
-  }                                                                            \
-}
-
-SETTER(mirroring)
-SETTER(general_category)
-SETTER(script)
-SETTER(combining_class)
-SETTER(eastasian_width)
-
-hb_codepoint_t
-hb_unicode_get_mirroring (hb_unicode_funcs_t *ufuncs,
-			  hb_codepoint_t unicode)
-{
-  return ufuncs->v.get_mirroring (ufuncs, unicode,
-				  ufuncs->v.get_mirroring_data);
-}
-
-hb_unicode_general_category_t
-hb_unicode_get_general_category (hb_unicode_funcs_t *ufuncs,
-				 hb_codepoint_t unicode)
-{
-  return ufuncs->v.get_general_category (ufuncs, unicode,
-					 ufuncs->v.get_general_category_data);
-}
-
-hb_script_t
-hb_unicode_get_script (hb_unicode_funcs_t *ufuncs,
-		       hb_codepoint_t unicode)
-{
-  return ufuncs->v.get_script (ufuncs, unicode,
-			       ufuncs->v.get_script_data);
-}
-
-unsigned int
-hb_unicode_get_combining_class (hb_unicode_funcs_t *ufuncs,
-				hb_codepoint_t unicode)
-{
-  return ufuncs->v.get_combining_class (ufuncs, unicode,
-					ufuncs->v.get_combining_class_data);
-}
-
-unsigned int
-hb_unicode_get_eastasian_width (hb_unicode_funcs_t *ufuncs,
-				hb_codepoint_t unicode)
-{
-  return ufuncs->v.get_eastasian_width (ufuncs, unicode,
-					ufuncs->v.get_eastasian_width_data);
-}
-
-
-HB_END_DECLS
diff --git a/src/hb-unicode.cc b/src/hb-unicode.cc
new file mode 100644
index 0000000..ddad884
--- /dev/null
+++ b/src/hb-unicode.cc
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2009  Red Hat, Inc.
+ * Copyright © 2011 Codethink Limited
+ * Copyright (C) 2010  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ * Codethink Author(s): Ryan Lortie
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#include "hb-private.h"
+
+#include "hb-unicode-private.hh"
+
+HB_BEGIN_DECLS
+
+
+/*
+ * hb_unicode_funcs_t
+ */
+
+static unsigned int
+hb_unicode_get_combining_class_nil (hb_unicode_funcs_t *ufuncs    HB_UNUSED,
+				    hb_codepoint_t      unicode   HB_UNUSED,
+				    void               *user_data HB_UNUSED)
+{
+  return 0;
+}
+
+static unsigned int
+hb_unicode_get_eastasian_width_nil (hb_unicode_funcs_t *ufuncs    HB_UNUSED,
+				    hb_codepoint_t      unicode   HB_UNUSED,
+				    void               *user_data HB_UNUSED)
+{
+  return 1;
+}
+
+static hb_unicode_general_category_t
+hb_unicode_get_general_category_nil (hb_unicode_funcs_t *ufuncs    HB_UNUSED,
+				     hb_codepoint_t      unicode   HB_UNUSED,
+				     void               *user_data HB_UNUSED)
+{
+  return HB_UNICODE_GENERAL_CATEGORY_OTHER_LETTER;
+}
+
+static hb_codepoint_t
+hb_unicode_get_mirroring_nil (hb_unicode_funcs_t *ufuncs    HB_UNUSED,
+			      hb_codepoint_t      unicode   HB_UNUSED,
+			      void               *user_data HB_UNUSED)
+{
+  return unicode;
+}
+
+static hb_script_t
+hb_unicode_get_script_nil (hb_unicode_funcs_t *ufuncs    HB_UNUSED,
+			   hb_codepoint_t      unicode   HB_UNUSED,
+			   void               *user_data HB_UNUSED)
+{
+  return HB_SCRIPT_UNKNOWN;
+}
+
+
+hb_unicode_funcs_t _hb_unicode_funcs_nil = {
+  HB_REFERENCE_COUNT_INVALID, /* ref_count */
+  NULL, /* parent */
+  TRUE, /* immutable */
+  {
+    hb_unicode_get_combining_class_nil,
+    hb_unicode_get_eastasian_width_nil,
+    hb_unicode_get_general_category_nil,
+    hb_unicode_get_mirroring_nil,
+    hb_unicode_get_script_nil,
+  }
+};
+
+
+hb_unicode_funcs_t *
+hb_unicode_funcs_create (hb_unicode_funcs_t *parent)
+{
+  hb_unicode_funcs_t *ufuncs;
+
+  if (!HB_OBJECT_DO_CREATE (hb_unicode_funcs_t, ufuncs))
+    return &_hb_unicode_funcs_nil;
+
+  if (parent != NULL)
+  {
+    hb_unicode_funcs_make_immutable (parent);
+    ufuncs->parent = hb_unicode_funcs_reference (parent);
+
+    ufuncs->get = parent->get;
+
+    /* We can safely copy user_data from parent since we hold a reference
+     * onto it and it's immutable.  We should not copy the destroy notifiers
+     * though. */
+    ufuncs->user_data = parent->user_data;
+  }
+  else
+  {
+    ufuncs->get = _hb_unicode_funcs_nil.get;
+  }
+
+  return ufuncs;
+}
+
+hb_unicode_funcs_t *
+hb_unicode_funcs_reference (hb_unicode_funcs_t *ufuncs)
+{
+  HB_OBJECT_DO_REFERENCE (ufuncs);
+}
+
+unsigned int
+hb_unicode_funcs_get_reference_count (hb_unicode_funcs_t *ufuncs)
+{
+  HB_OBJECT_DO_GET_REFERENCE_COUNT (ufuncs);
+}
+
+void
+hb_unicode_funcs_destroy (hb_unicode_funcs_t *ufuncs)
+{
+  HB_OBJECT_DO_DESTROY (ufuncs);
+
+#define DESTROY(name) if (ufuncs->destroy.name) ufuncs->destroy.name (ufuncs->user_data.name)
+  DESTROY (combining_class);
+  DESTROY (eastasian_width);
+  DESTROY (general_category);
+  DESTROY (mirroring);
+  DESTROY (script);
+#undef DESTROY
+
+  if (ufuncs->parent != NULL)
+    hb_unicode_funcs_destroy (ufuncs->parent);
+
+  free (ufuncs);
+}
+
+void
+hb_unicode_funcs_make_immutable (hb_unicode_funcs_t *ufuncs)
+{
+  if (HB_OBJECT_IS_INERT (ufuncs))
+    return;
+
+  ufuncs->immutable = TRUE;
+}
+
+hb_bool_t
+hb_unicode_funcs_is_immutable (hb_unicode_funcs_t *ufuncs)
+{
+  return ufuncs->immutable;
+}
+
+hb_unicode_funcs_t *
+hb_unicode_funcs_get_parent (hb_unicode_funcs_t *ufuncs)
+{
+  return ufuncs->parent;
+}
+
+
+#define IMPLEMENT(return_type, name)                                           \
+                                                                               \
+void                                                                           \
+hb_unicode_funcs_set_##name##_func (hb_unicode_funcs_t             *ufuncs,    \
+                                    hb_unicode_get_##name##_func_t  func,      \
+                                    void                           *user_data, \
+                                    hb_destroy_func_t               destroy)   \
+{                                                                              \
+  if (ufuncs->immutable)                                                       \
+    return;                                                                    \
+                                                                               \
+  if (ufuncs->destroy.name)                                                    \
+    ufuncs->destroy.name (ufuncs->user_data.name);                             \
+                                                                               \
+  if (func) {                                                                  \
+    ufuncs->get.name = func;                                                   \
+    ufuncs->user_data.name = user_data;                                        \
+    ufuncs->destroy.name = destroy;                                            \
+  } else if (ufuncs->parent != NULL) {                                         \
+    ufuncs->get.name = ufuncs->parent->get.name;                               \
+    ufuncs->user_data.name = ufuncs->parent->user_data.name;                   \
+    ufuncs->destroy.name = NULL;                                               \
+  } else {                                                                     \
+    ufuncs->get.name = hb_unicode_get_##name##_nil;                            \
+    ufuncs->user_data.name = NULL;                                             \
+    ufuncs->destroy.name = NULL;                                               \
+  }                                                                            \
+}                                                                              \
+                                                                               \
+return_type                                                                    \
+hb_unicode_get_##name (hb_unicode_funcs_t *ufuncs,                             \
+		       hb_codepoint_t      unicode)                            \
+{                                                                              \
+  return ufuncs->get.name (ufuncs, unicode, ufuncs->user_data.name);           \
+}
+
+IMPLEMENT (unsigned int, combining_class)
+IMPLEMENT (unsigned int, eastasian_width)
+IMPLEMENT (hb_unicode_general_category_t, general_category)
+IMPLEMENT (hb_codepoint_t, mirroring)
+IMPLEMENT (hb_script_t, script)
+
+#undef IMPLEMENT
+
+
+HB_END_DECLS
diff --git a/src/hb-unicode.h b/src/hb-unicode.h
index 9785f3e..0b94aa3 100644
--- a/src/hb-unicode.h
+++ b/src/hb-unicode.h
@@ -68,40 +68,25 @@
 
 /* typedefs */
 
-typedef hb_codepoint_t                (*hb_unicode_get_mirroring_func_t)        (hb_unicode_funcs_t *ufuncs,
-                                                                                 hb_codepoint_t      unicode,
-                                                                                 void               *user_data);
-typedef hb_unicode_general_category_t (*hb_unicode_get_general_category_func_t) (hb_unicode_funcs_t *ufuncs,
-                                                                                 hb_codepoint_t      unicode,
-                                                                                 void               *user_data);
-typedef hb_script_t                   (*hb_unicode_get_script_func_t)           (hb_unicode_funcs_t *ufuncs,
-                                                                                 hb_codepoint_t      unicode,
-                                                                                 void               *user_data);
 typedef unsigned int                  (*hb_unicode_get_combining_class_func_t)  (hb_unicode_funcs_t *ufuncs,
                                                                                  hb_codepoint_t      unicode,
                                                                                  void               *user_data);
 typedef unsigned int                  (*hb_unicode_get_eastasian_width_func_t)  (hb_unicode_funcs_t *ufuncs,
                                                                                  hb_codepoint_t      unicode,
                                                                                  void               *user_data);
+typedef hb_unicode_general_category_t (*hb_unicode_get_general_category_func_t) (hb_unicode_funcs_t *ufuncs,
+                                                                                 hb_codepoint_t      unicode,
+                                                                                 void               *user_data);
+typedef hb_codepoint_t                (*hb_unicode_get_mirroring_func_t)        (hb_unicode_funcs_t *ufuncs,
+                                                                                 hb_codepoint_t      unicode,
+                                                                                 void               *user_data);
+typedef hb_script_t                   (*hb_unicode_get_script_func_t)           (hb_unicode_funcs_t *ufuncs,
+                                                                                 hb_codepoint_t      unicode,
+                                                                                 void               *user_data);
 
 /* setters */
 
 void
-hb_unicode_funcs_set_mirroring_func (hb_unicode_funcs_t *ufuncs,
-				     hb_unicode_get_mirroring_func_t mirroring_func,
-                                     void *user_data, hb_destroy_func_t destroy);
-
-void
-hb_unicode_funcs_set_general_category_func (hb_unicode_funcs_t *ufuncs,
-					    hb_unicode_get_general_category_func_t general_category_func,
-                                            void *user_data, hb_destroy_func_t destroy);
-
-void
-hb_unicode_funcs_set_script_func (hb_unicode_funcs_t *ufuncs,
-				  hb_unicode_get_script_func_t script_func,
-                                  void *user_data, hb_destroy_func_t destroy);
-
-void
 hb_unicode_funcs_set_combining_class_func (hb_unicode_funcs_t *ufuncs,
 					   hb_unicode_get_combining_class_func_t combining_class_func,
                                            void *user_data, hb_destroy_func_t destroy);
@@ -111,21 +96,25 @@
 					   hb_unicode_get_eastasian_width_func_t eastasian_width_func,
                                            void *user_data, hb_destroy_func_t destroy);
 
+void
+hb_unicode_funcs_set_general_category_func (hb_unicode_funcs_t *ufuncs,
+					    hb_unicode_get_general_category_func_t general_category_func,
+                                            void *user_data, hb_destroy_func_t destroy);
+
+void
+hb_unicode_funcs_set_mirroring_func (hb_unicode_funcs_t *ufuncs,
+				     hb_unicode_get_mirroring_func_t mirroring_func,
+                                     void *user_data, hb_destroy_func_t destroy);
+
+void
+hb_unicode_funcs_set_script_func (hb_unicode_funcs_t *ufuncs,
+				  hb_unicode_get_script_func_t script_func,
+                                  void *user_data, hb_destroy_func_t destroy);
+
+
 
 /* accessors */
 
-hb_codepoint_t
-hb_unicode_get_mirroring (hb_unicode_funcs_t *ufuncs,
-			  hb_codepoint_t unicode);
-
-hb_unicode_general_category_t
-hb_unicode_get_general_category (hb_unicode_funcs_t *ufuncs,
-				 hb_codepoint_t unicode);
-
-hb_script_t
-hb_unicode_get_script (hb_unicode_funcs_t *ufuncs,
-		       hb_codepoint_t unicode);
-
 unsigned int
 hb_unicode_get_combining_class (hb_unicode_funcs_t *ufuncs,
 				hb_codepoint_t unicode);
@@ -134,6 +123,18 @@
 hb_unicode_get_eastasian_width (hb_unicode_funcs_t *ufuncs,
 				hb_codepoint_t unicode);
 
+hb_unicode_general_category_t
+hb_unicode_get_general_category (hb_unicode_funcs_t *ufuncs,
+				 hb_codepoint_t unicode);
+
+hb_codepoint_t
+hb_unicode_get_mirroring (hb_unicode_funcs_t *ufuncs,
+			  hb_codepoint_t unicode);
+
+hb_script_t
+hb_unicode_get_script (hb_unicode_funcs_t *ufuncs,
+		       hb_codepoint_t unicode);
+
 
 HB_END_DECLS
 
diff --git a/test/test-unicode.c b/test/test-unicode.c
index f610c1d..c210d54 100644
--- a/test/test-unicode.c
+++ b/test/test-unicode.c
@@ -120,10 +120,14 @@
 static void
 test_subclassing_nil (void)
 {
-  hb_unicode_funcs_t *uf = hb_unicode_funcs_create (NULL);
+  hb_unicode_funcs_t *uf, *aa;
+
+  uf = hb_unicode_funcs_create (NULL);
   g_assert_cmpint (hb_unicode_funcs_get_reference_count (uf), ==, 1);
-  hb_unicode_funcs_t *aa = hb_unicode_funcs_create (uf);
+
+  aa = hb_unicode_funcs_create (uf);
   g_assert_cmpint (hb_unicode_funcs_get_reference_count (uf), ==, 2);
+
   hb_unicode_funcs_destroy (uf);
   g_assert_cmpint (hb_unicode_funcs_get_reference_count (uf), ==, 1);
 
@@ -133,7 +137,6 @@
   g_assert_cmpint (hb_unicode_get_script (aa, 'a'), ==, HB_SCRIPT_ARABIC);
   g_assert_cmpint (hb_unicode_get_script (aa, 'b'), ==, HB_SCRIPT_UNKNOWN);
 
-
   g_assert_cmpint (hb_unicode_funcs_get_reference_count (aa), ==, 1);
   g_assert_cmpint (hb_unicode_funcs_get_reference_count (uf), ==, 1);
   g_assert (!freed0 && !freed1);
@@ -145,10 +148,14 @@
 static void
 test_subclassing_glib (void)
 {
-  hb_unicode_funcs_t *uf = hb_glib_get_unicode_funcs ();
+  hb_unicode_funcs_t *uf, *aa;
+
+  uf = hb_glib_get_unicode_funcs ();
   g_assert_cmpint (hb_unicode_funcs_get_reference_count (uf), ==, 0);
-  hb_unicode_funcs_t *aa = hb_unicode_funcs_create (uf);
+
+  aa = hb_unicode_funcs_create (uf);
   g_assert_cmpint (hb_unicode_funcs_get_reference_count (uf), ==, 0);
+
   hb_unicode_funcs_destroy (uf);
   g_assert_cmpint (hb_unicode_funcs_get_reference_count (uf), ==, 0);
 
@@ -168,13 +175,15 @@
 static void
 test_subclassing_deep (void)
 {
-  hb_unicode_funcs_t *uf = hb_unicode_funcs_create (NULL);
+  hb_unicode_funcs_t *uf, *aa;
+
+  uf = hb_unicode_funcs_create (NULL);
   g_assert_cmpint (hb_unicode_funcs_get_reference_count (uf), ==, 1);
 
   hb_unicode_funcs_set_script_func (uf, simple_get_script,
                                     unique_pointer0, free_up);
 
-  hb_unicode_funcs_t *aa = hb_unicode_funcs_create (uf);
+  aa = hb_unicode_funcs_create (uf);
   g_assert_cmpint (hb_unicode_funcs_get_reference_count (aa), ==, 1);
   g_assert_cmpint (hb_unicode_funcs_get_reference_count (uf), ==, 2);