.hg和.ccg文件

.hg和.ccg源文件与C++的.h和.cc源文件非常相似,但是它们包含额外的宏,例如_CLASS_GOBJECT()_WRAP_METHOD()gmmproc会使用这些宏在头文件中的相同位置生成合适的C++源代码。而所有其他的C++源代码会按原样复制到相应的.h或.cc文件中。

.hg文件通常包含了一些头文件和声明一个类,并使用一些宏向类中添加API或行为。例如,gtkmmbutton.hg文件看起来大概这样:

#include <gtkmm/widget.h>
#include <gtkmm/actionable.h>
_DEFS(gtkmm,gtk)
_PINCLUDE(gtkmm/private/widget_p.h)

namespace Gtk
{

class Button
  : public Widget,
    public Actionable
{
  _CLASS_GTKOBJECT(Button, GtkButton, GTK_BUTTON, Gtk::Widget, GtkWidget)
  _IMPLEMENTS_INTERFACE(Actionable)
public:

  _CTOR_DEFAULT
  explicit Button(const Glib::ustring& label, bool mnemonic = false);

  _WRAP_METHOD(void set_label(const Glib::ustring& label), gtk_button_set_label)

  ...

  _WRAP_SIGNAL(void clicked(), "clicked")

  ...

  _WRAP_PROPERTY("label", Glib::ustring)
};

} // namespace Gtk

本示例中的宏执行下述操作:

_DEFS()

指定所生成源代码的目标目录,以及gmmproc应该解析的主要.defs文件的名称。

_PINCLUDE()

告诉gmmproc在生成的文件中包含private/button_p.h头文件。

_CLASS_GTKOBJECT()

告诉gmmproc在封装部件类时在类中添加一些类型定义(typedef)、构造函数、标准方法。

_IMPLEMENTS_INTERFACE()

告诉gmmproc为接口添加初始化代码。

_CTOR_DEFAULT

添加默认构造函数。

_WRAP_METHOD()_WRAP_SIGNAL()_WRAP_PROPERTY()

添加用于封装C API的一部分的方法。

通过这样使用gmmproc处理.hg和.ccg文件,可以从.hg和.ccg文件生成.h和.cc文件,尽管这在使用上述的构建结构时会自动进行:

$ cd gtk/src
$ /usr/lib/glibmm-2.68/proc/gmmproc -I ../../tools/m4 --defs . button . ./../gtkmm

请注意我们为gmmproc提供了.m4转换文件、.defs文件路径、.hg文件名、源代码目录、目标目录。

你应该避免在C++头文件中包含C头文件,以免污染全局名称空间,以免导出不必要的公共API。但是你需要在你的.ccg文件中包含必要的C头文件。

以下各节将对宏进行详细说明。

G.3.1. m4转换

在.hg和.ccg文件中使用的宏通常需要知道如何将C++类型转换为C类型,反之亦然。gmmproc会从你的tools/m4/codegen/m4/目录的.m4文件读取此信息。这样它就可以在C++方法的实现中调用C函数,并将适当的参数传递给该C函数。例如:这告诉gmmproc如何将GtkTreeView指针转换为Gtk::TreeView指针:

_CONVERSION(`GtkTreeView*',`TreeView*',`Glib::wrap($3)')

当此转换被gmmproc使用时,$3将会被参数名称替换。

使用一些额外的宏可以使其变得更简单和一致。例如gtkmm中的.m4文件:

_CONVERSION(`PrintSettings&',`GtkPrintSettings*',__FR2P)
_CONVERSION(`const PrintSettings&',`GtkPrintSettings*',__FCR2P)
_CONVERSION(`const Glib::RefPtr<Printer>&',`GtkPrinter*',__CONVERT_REFPTR_TO_P($3))

G.3.2. 安装m4

在封装方法时,通常最好将C函数的返回值储存在被称为输出参数的参数中。在本例中,C++方法返回void但是有一个用于储存C函数值的输出参数。gmmproc支持此功能,但是必须使用适当的初始化宏来告诉gmmproc如何从C函数的返回值初始化C++参数。

例如,如果一个C函数返回了一个GtkWidget*,并且由于某种原因,不想让C++方法返回此部件,而是希望C++方法将此部件放入指定的输出参数中,则如下所示的初始化宏是必须的:

_INITIALIZATION(`Gtk::Widget&',`GtkWidget*',`$3 = Glib::wrap($4)')

$3将被C++方法的输出参数名替换,$4将在此初始化被gmmproc使用时被C函数的返回值替代。为了方便起见,$1也将被不带&符的C++类型替换,而$2将被替换为C类型。

G.3.3. 类宏

类宏声明了类本身与其底层C类型的关系。它会生成一些内部构造函数、gobject_成员、类型定义(typedef)、gobj()访问器、Glib::wrap()成员函数、并进行类型注册等。

其他像_WRAP_METHOD()_WRAP_SIGNAL()这样的宏只能在调用_CLASS_*宏之后使用。

G.3.3.1. _CLASS_GOBJECT

这个宏为从GObject派生的类型声明了一个类型包装器,但是此包装器并不会派生自Gtk::Object

_CLASS_GOBJECT( C++类, C类, C转换宏, C++基类, C基类 )

示例来自于entry.hg

_CLASS_GOBJECT(Adjustment, GtkAdjustment, GTK_ADJUSTMENT, Glib::Object, GObject)

G.3.3.2. _CLASS_GTKOBJECT

这个宏为类型声明了一个类型派生包装器,此包装器将派生自Gtk::Object,例如部件(Widget)和对话框(Dialog)。

_CLASS_GTKOBJECT( C++类, C类, C转换宏, C++基类, C基类 )

来自button.hg文件中的例子:

_CLASS_GTKOBJECT(Button, GtkButton, GTK_BUTTON, Gtk::Widget, GtkWidget)

当类已从Gtk::Object派生时,你通常会使用此宏。例如:你将在封装GTK部件时使用它,因为Gtk::Widget派生自Gtk::Object

你还可以从Gtk::Object派生非部件类,这样你就可以在不通过Glib::RefPtr直接使用它们。例如:它们将可以使用Gtk::make_managed()实例化也可以作为成员变量在栈上实例化。这样你可以自行决定是否对它们使用引用计数。我们认为对部件使用引用计数很有用。

G.3.3.3. _CLASS_BOXEDTYPE

这个宏声明了一个非GObject结构体类型的包装器,并使用g_boxed_type_register_static()对该类型进行注册。

_CLASS_BOXEDTYPE( C++类, C类, new函数, copy函数, free函数 )

示例来自Gdk::RGBA

_CLASS_BOXEDTYPE(RGBA, GdkRGBA, NONE, gdk_rgba_copy, gdk_rgba_free)

G.3.3.4. _CLASS_BOXEDTYPE_STATIC

这个宏为类似于GdkRectangle这样的简单可赋值结构体声明了一个包装器。它和_CLASS_BOXEDTYPE类似,不过其包装的C结构体不是动态分配的。

_CLASS_BOXEDTYPE_STATIC( C++类, C类 )

示例来自Gdk::Rectangle

_CLASS_BOXEDTYPE_STATIC(Rectangle, GdkRectangle)

G.3.3.5. _CLASS_OPAQUE_COPYABLE

这个宏为具有复制和释放函数的不透明结构体声明了一个包装器。新建(new)、复制(copy)、释放(free)函数将用于实例化默认构造函数、复制构造函数、析构函数。

_CLASS_OPAQUE_COPYABLE( C++类, C类, new函数, copy函数, free函数 )

示例来自Glib::Checksum

_CLASS_OPAQUE_COPYABLE(VariantType, GVariantType, NONE, g_variant_type_copy, g_variant_type_free)

G.3.3.6. _CLASS_OPAQUE_REFCOUNTED

这个宏为使用引用计数的不透明结构体声明了一个包装器。C++包装器将不能直接实例化,需要与Glib::RefPtr一起使用。

_CLASS_OPAQUE_REFCOUNTED( C++函数, C函数, new函数, ref函数, unref函数 )

示例来自Gdk::Rectangle

_CLASS_OPAQUE_REFCOUNTED(CssSection, GtkCssSection, NONE, gtk_css_section_ref, gtk_css_section_unref)

G.3.3.7. _CLASS_GENERIC

这个宏用于包装任何不属于专用包装宏的结构体。

_CLASS_GENERIC( C++类, C类 )

示例来自Gdk::Rectangle

_CLASS_GENERIC(TimeCoord, GdkTimeCoord)

G.3.3.8. _CLASS_INTERFACE

这个宏为派生自GTypeInterface的类型声明了一个包装器。

_CLASS_INTERFACE( C++类, C类, C转换宏, C接口结构体, C++基类(可选的), C基类(可选的) )

示例来自与celleditable.hg

_CLASS_INTERFACE(CellEditable, GtkCellEditable, GTK_CELL_EDITABLE, GtkCellEditableIface)

对于接口是从其他接口派生的情况,两个附加参数是可选的,GInterface有另一个GInterface作为先决条件时属于此情况。示例来自于loadableicon.hg

_CLASS_INTERFACE(LoadableIcon, GLoadableIcon, G_LOADABLE_ICON, GLoadableIconIface, Icon, GIcon)

G.3.4. 构造函数宏

_CTOR_DEFAULT()_WRAP_CTOR()宏封装指定的*_new() C函数并添加构造函数。这些宏假定C对象具有与函数参数名一样的属性名,这样它就可以直接向g_object_new()调用提供参数。这些构造函数实际上从不会调用*_new() C函数,因为gtkmm必须实例化派生的GType,而*_new() C函数只是用于给C程序员提供便利。

在使用_CLASS_GOBJECT()时,其构造函数应该是保护(protected)的而不是公有(public)。并且每个公有(public)的构造函数都应该有_WRAP_CREATE()。这样可以防止在不使用RefPtr的情况下直接实例化这个类。例如:

class TextMark : public Glib::Object
{
  _CLASS_GOBJECT(TextMark, GtkTextMark, GTK_TEXT_MARK, Glib::Object, GObject)

protected:
  _WRAP_CTOR(TextMark(const Glib::ustring& name, bool left_gravity = true), gtk_text_mark_new)

public:
  _WRAP_CREATE(const Glib::ustring& name, bool left_gravity = true)

G.3.4.1. _CTOR_DEFAULT

这个宏创建一个不接受参数的默认构造函数

G.3.4.2. _WRAP_CTOR

这个宏创建一个接受参数的构造函数,与*_new() C函数等效。但是他不会调用*_new()函数,只会创建有相同参数类型的构造函数。它需要一个C++构造函数签名和一个C函数名。

他还有需要额外的可选参数:

errthrow

这告诉gmmproc*_new()的最后一个 GError**是否应该被忽略。

G.3.4.3. 手工编码构造函数

有时因为*_new() C函数的参数不与对象属性直接对应,或是*_new() C函数不只是简单的调用g_object_new(),你将必须手写一部分构造函数。在此时你可以在》ccg文件中使用_CONSTRUCT()宏来保存一些工作。_CONSTRUCT宏接受一系列属性名与值作为参数。示例来自button.ccg

Button::Button(const Glib::ustring& label, bool mnemonic)
:
  _CONSTRUCT("label", label.c_str(), "use_underline", gboolean(mnemonic))
{}

G.3.5. 抑制某些代码生成的宏

在某些宏在_CLASS_*宏之后使用时,它们会抑制某些代码的生成。一些抑制在.cc文件中生成定义,另一些抑制在.h文件中生成的声明和.cc文件中生成的定义。

G.3.5.1. _CUSTOM_DEFAULT_CTOR

抑制_CLASS_BOXEDTYPE_CLASS_BOXEDTYPE_STATIC_CLASS_OPAQUE_COPYABLE生成默认构造函数的声明和定义。

G.3.5.2. _CUSTOM_CTOR_CAST

抑制_CLASS_BOXEDTYPE_CLASS_BOXEDTYPE_STATIC生成接受指向其所包装的C类型的指针的构造函数的声明和定义。

抑制_CLASS_INTERFACE_CLASS_OPAQUE_COPYABLE生成接受指向其所包装的C类型的指针的构造函数的定义。

抑制_CLASS_GOBJECT_CLASS_GTKOBJECT生成接受指向其所包装的C类型的指针且接受其他构造参数的构造函数的定义。

G.3.5.3. _CUSTOM_DTOR

抑制_CLASS_GOBJECT_CLASS_GTKOBJECT生成析构函数的定义

G.3.5.4. _CUSTOM_MOVE_OPERATIONS

抑制_CLASS_GOBJECT_CLASS_GTKOBJECT生成移动构造函数和移动赋值运算符的声明和定义。

例如:

class Derived : public Glib::Object
{
  _CLASS_GOBJECT(Derived, GDerived, G_DERIVED, Glib::Object, GObject)

  _CUSTOM_MOVE_OPERATIONS

public:
  Derived(Derived&& src) noexcept;
  Derived& operator=(Derived&& src) noexcept;
  // ...
};

G.3.5.5. _CUSTOM_WRAP_NEW

抑制_CLASS_GOBJECT生成Glib::wrap_new()的定义。

G.3.5.6. _CUSTOM_WRAP_FUNCTION

抑制_CLASS_GOBJECT_CLASS_GTKOBJECT生成Glib::wrap()函数的定义。

G.3.5.7. _NO_WRAP_FUNCTION

抑制_CLASS_GOBJECT_CLASS_BOXEDTYPE_CLASS_BOXEDTYPE_STATIC_CLASS_OPAQUE_COPYABLE_CLASS_INTERFACE_CLASS_GTKOBJECT生成Glib::wrap()函数的声明和定义。

G.3.6. 方法宏

G.3.6.1. _WRAP_METHOD

这个宏用于生成封装C函数的C++方法。

_WRAP_METHOD( C++方法签名, C函数名)

示例来自于entry.hg

_WRAP_METHOD(void set_text(const Glib::ustring& text), gtk_entry_set_text)

在.defs文件中有对C函数更全面的描述,convert*.m4文件中包含了从C++参数类型到C参数类型的必要转换。这个宏还基于*_docs.xml*_docs_override.xml文件生成doxygen文档注释。

有一些可选的额外参数:

refreturn

在C函数不提供引用时,对其返回值额外调用一次reference()

errthrow ["<exceptions>"]

使用C函数的最后一个GError**参数抛出异常,可选的"<exceptions>"是一个以逗号分隔的可抛出异常列表。它决定了哪些@throws Doxygen命令将被加入文档。默认抛出的是Glib::Error异常。如果你需要在异常描述中使用逗号,请使用反斜杠对其进行转义。例如:errthrow "Glib::OptionError Hello\, world, Glib::ConvertError"

deprecated ["<text>"]

将生成的代码放入#ifdef块中。text是可选参数,用于提供与弃用有关的文本信息。

constversion

直接调用同一个函数的非const版本,而不是生成几乎相同的代码。

newin "<version>"

在文档中添加@newin Doxygen命令,或替换从C文档中生成的@newin命令。

ifdef <identifier>

将生成的代码放入ifdef <identifier>块中。

slot_name <parameter_name>

如果方法存在一个槽参数,指定该槽参数的名称。这使得gmmproc可以生成用于复制槽并将其复制传递给C函数的gpointer user_data参数的代码。slot_callback参数需要使用此选项指定的名称。

slot_callback <function_name>

slot_name选项一起使用。用slot_name所指定的胶水回调函数处理提取槽然后调用它。这个回调函数的函数指针也将被传递给封装此C函数的方法。

no_slot_copy

告知gmmproc如果此方法有槽,不要将槽的副本传递给C函数。而是直接传递槽本身。槽的参数名和胶水回调函数必须分别使用slot_nameslot_callback选项指定。

封装C API时,选择对应的C++类型也很重要。尽管多数时候应该在C++方法中使用什么C++类型是很明显的,但我们还是想给你一些提示:

  • 通过RefPtr使用的对象:传递RefPtr的const引用。例如:const Glib::RefPtr<Gtk::FileFilter>& filter
  • 通过RefPtr使用的const对象:如果函数不应该修改此对象,即使对象已经是const的,也应该确保对象的RefPtr是const的。例如:const Glib::RefPtr<const Gtk::FileFilter>& filter
  • 包装GList*GSList*参数:首先你需要阅读C函数的文档,确定列表中每项的数据字段包含了那些对象。然后确定如何用std::vector进行包装。例如包装为std::vector<Glib::RefPtr<Gdk::Pixbuf>>。你可能需要定义一个Traits类型来指示C类型和C++类型如何相互转换。
  • 包装GList*GSList*返回值:你需要在此阅读C函数文档,确定调用方是否需要负责释放列表和列表中的项目。然后根据这些信息,选择m4转换规则的所有权(无(none)、浅(shallow)、深(deep)),你可能应该将其直接放在.hg文件中,因为所有权取决于函数本身而不是其类型。例如:
    #m4 _CONVERSION(`GSList*',`std::vector<Widget*>',`Glib::SListHandler<Widget*>::slist_to_vector($3, Glib::OWNERSHIP_SHALLOW)')

G.3.6.2. _WRAP_METHOD_DOCS_ONLY

这个宏类似于_WRAP_METHOD(),但是它只生成封装C函数的C++方法的文档。当你需要手动编写该方法又不想手动编写文档时,请使用这个宏。

_WRAP_METHOD_DOCS_ONLY(C function name)

示例来自于container.hg

_WRAP_METHOD_DOCS_ONLY(gtk_recent_info_get_applications)

有一些可选的额外参数:

errthrow ["<exceptions>"]

不要在文档中包含C函数的最后一个GError**参数的文档。可选的 "<exceptions>"是一个以逗号分隔的可抛出异常列表。它决定了哪些@throws Doxygen命令将被加入文档。默认抛出的是Glib::Error异常。如果你需要在异常描述中使用逗号,请使用反斜杠对其进行转义。例如:errthrow "Glib::OptionError Hello\, world, Glib::ConvertError"

newin "<version>"

在文档中添加@newin Doxygen命令,或替换从C文档中生成的@newin命令。

voidreturn

不要再文档中包含@return Doxygen命令。如果被封装的C函数有返回而封装它的C++方法返回void,这将很有用。

G.3.6.3. _IGNORE, _IGNORE_SIGNAL, _IGNORE_PROPERTY

gmmproc会在stdout上警告你忘了封装相关的函数、信号、属性、子属性,这有助于确保API被完整的封装。但有时候你会不想对某些函数、信号、属性、子属性封装,或者你想要手动编写一些函数,那么你可以使用_IGNORE()、_IGNORE_SIGNAL()、_IGNORE_PROPERTY()宏使gmmproc停止发出部分警告。

_IGNORE(C属性名1, C属性名2, ...) _IGNORE_SIGNAL(C属性名1, C属性名2, ...) _IGNORE_PROPERTY(C属性名1, C属性名2, ...)

示例来自flowbox.hg

_IGNORE(gtk_flow_box_set_filter_func, gtk_flow_box_set_sort_func)
_IGNORE_SIGNAL(activate-cursor-child, toggle-cursor-child, move-cursor)

G.3.6.4. _WRAP_SIGNAL

这个宏生成用来封装C GObject信号的C++ libsigc++风格的信号。实际上它会生成一个公有访问器方法,例如signal_clicked(),它返回一个代理对象。gmmproc使用.defs文件寻找C参数类型并使用.m4转换文件查找如何将其转换为适当的类型。

_WRAP_SIGNAL( C++信号处理函数签名, C信号名)

来自button.hg文件中的例子:

_WRAP_SIGNAL(void clicked(),"clicked")

信号通常在GTK结构体中具有函数指针,在.c文件中有对应的枚举值和g_signal_new()函数。

有一些可选的额外参数:

no_default_handler

不要生成on_something()虚成员函数以允许简单的覆写默认信号处理函数。可以在添加一个具有默认信号处理函数的信号会使虚函数表尺寸变大以至于破坏ABI的时候,或是在需要添加一个非公共C默认处理函数的时候,使用此可选参数。

custom_default_handler

.h文件中生成on_something()虚成员函数的声明,但不在.cc文件中生成该函数的定义。请在你需要手动编写函数定义的时候使用此可选参数。

custom_c_callback

不要为信号生成C回调函数。请在你需要手动编写回调函数的时候使用此可选参数。

refreturn

当C函数没有提供引用的时候,在on_something()虚成员函数的返回值上额外调用一次reference()

deprecated ["<text>"]

将生成的代码放入#ifdef块中。text是可选参数,用于提供与弃用有关的文本信息。

newin "<version>"

在文档中添加@newin Doxygen命令,或替换从C文档中生成的@newin命令。

ifdef <identifier>

将生成的代码放入ifdef <identifier>块中。

exception_handler <method_name>

允许使用自定义异常处理函数而不是使用默认异常处理函数。如果异常被用户自定义的处理函数程序抛出,则该异常将被默认异常处理函数捕获。

detail_name <parameter_name>

signal_something()成员函数添加一个const Glib::ustring&参数。如果信号接受详细信号名,也就是说底层C代码使用了G_SIGNAL_DETAILED标志注册这个信号,则使用该参数作为详细信号名。

two_signal_methods

需与detail_name选项一起使用。生成两个signal_something()成员函数,其中一个不带任何参数,而另一个接受一个参数但该参数没有默认值。如果只使用detail_name选项,则只生成一个成员函数,此成员函数将接受一个参数且此参数有默认值。请再需要保持ABI不变的情况下使用此选项。

G.3.6.5. _WRAP_PROPERTY

这个宏生成用于包装C GObject属性的C++方法。你需要为该属性指定属性名以及其值所需的C++类型。gmmproc使用.defs文件寻找C参数类型并使用.m4转换文件查找如何将其转换为适当的类型。

_WRAP_PROPERTY(C属性名, C++类型)

来自button.hg文件中的例子:

_WRAP_PROPERTY("label", Glib::ustring)

有一些可选的额外参数:

deprecated ["<text>"]

将生成的代码放入#ifdef块中。text是可选参数,用于提供与弃用有关的文本信息。

newin "<version>"

在文档中添加@newin Doxygen命令,或替换从C文档中生成的@newin命令。

G.3.6.6. _WRAP_VFUNC

这个宏生成用于封装C虚函数的C++成员函数。

_WRAP_VFUNC( C++成员函数签名, C函数名)

示例来自于widget.hg

_WRAP_VFUNC(SizeRequestMode get_request_mode() const, get_request_mode)

*_vfuncs.defs文件中有对C函数更全面的描述,convert*.m4文件中包含了从C++参数类型到C参数类型的必要转换。转换也可以写入.hg文件。虚函数通常需要额外的转换,这些转换最好保存在使用这些虚函数的.hg文件文件中。

有一些可选的额外参数:

refreturn

当C虚函数没有提供引用的时候,在something_vfunc()函数的返回值上额外调用一次reference()

refreturn_ctype

当被调用的C函数期望其提供一个引用的时候,在C回调函数当中,对被覆写的something_vfunc()函数的返回值额外调用一次reference()

keep_return

当被调用的C函数不想要得到属于它的引用的时候,在C回调函数中保留一份返回值的副本。

errthrow

如果C虚函数的参数中的最后有一个GError**参数,则使用此参数抛出异常。

custom_vfunc

不在.cc文件中生成虚函数的定义。请在你需要手写虚函数的时候使用此可选参数。

custom_vfunc_callback

不为虚函数生成C回调函数。请在你需要手写回调函数时使用此可选参数。

ifdef <identifier>

将生成的代码放入ifdef <identifier>块中。

slot_name <parameter_name>

如果方法存在一个槽参数,指定该槽参数的名称。这使得gmmproc可以生成用于复制槽并将其复制传递给C函数的gpointer user_data参数的代码。slot_callback参数需要使用此选项指定的名称。

slot_callback <function_name>

slot_name选项一起使用。用slot_name所指定的胶水回调函数处理提取槽然后调用它。这个回调函数的函数指针也将被传递给封装此C函数的方法。

no_slot_copy

告知gmmproc如果此方法有槽,不要将槽的副本传递给C函数。而是直接传递槽本身。槽的参数名和胶水回调函数必须分别使用slot_nameslot_callback选项指定。

return_value <value>

定义非默认的返回值。

err_return_value <value>

定义一个只在C++ something_vfunc()函数抛出异常,且传播到了C回调函数中的时候才使用的非默认返回值。如果指定了return_value但没有指定err_return_value,则将err_return_value的值视作return_value的值。

exception_handler <method_name>

允许使用自定义异常处理函数而不是使用默认异常处理函数。如果异常被用户自定义的处理函数程序抛出,则该异常将被默认异常处理函数捕获。

一个可能有例外的规则:如果一个C虚函数返回了指向派生自GObject的对象的指针,也即一个引用计数对象的指针,那么C++虚函数应该返回Glib::RefPtr<>对象。且需要refreturnrefreturn_ctype额外参数。

G.3.7. 其他宏

G.3.7.1. _IMPLEMENTS_INTERFACE

这个宏为接口生成初始化代码

_IMPLEMENTS_INTERFACE(C++接口名)

示例来自于grid.hg

_IMPLEMENTS_INTERFACE(Orientable)

有一个可选的额外参数:

ifdef <identifier>

将生成的代码放入ifdef <identifier>块中。

G.3.7.2. _WRAP_ENUM

这个宏生成一个C++枚举以包装C枚举。你必须指定所需的C++名称和底层C枚举的名称。

示例来自于enums.hg

_WRAP_ENUM(Orientation, GtkOrientation)

有一些可选的额外参数:

NO_GTYPE

如果枚举不是一个GType,则需使用此选项。在这个情况下C枚举没有*_get_type()函数,你不需要为该函数额外包含头文件。但你应该此为对C API提交一个bug,因为所有的枚举都应该被注册为GTypes。

如果你指定了NO_GTYPE,不要再_WRAP_PROPERTY中使用此枚举类型。这会导致生成的property_*()成员函数被调用时出现运行时错误。

示例来自于icontheme.hg

_WRAP_ENUM(IconLookupFlags, GtkIconLookupFlags, NO_GTYPE)
      
gtype_func <function_name>

指定C枚举的*_get_type()函数名。如果gmmproc无法从C枚举类型的名称推断出正确的函数名,请使用此额外参数。

示例来自于glibmm的dbusproxy.hg

_WRAP_ENUM(ProxyFlags, GDBusProxyFlags, gtype_func g_dbus_proxy_flags_get_type)
      
CONV_TO_INT

在一个类中生成普通枚举而不是生成枚举类。这将允许该枚举类型的值可以直接转换为int类型的值,且这样的枚举还拥有与枚举类类似的作用域。

示例来自于dialog.hg

_WRAP_ENUM(ResponseType, GtkResponseType, CONV_TO_INT)
      
s#<from>#<to>#

替换一个或多个枚举常量名称的全部或一部分。你可以添加任意数量的替换。

示例来自于glibmm中的iochannel.hg

_WRAP_ENUM(SeekType, GSeekType, NO_GTYPE, s#^SEEK_#SEEK_TYPE_#)
      
deprecated ["<text>"]

将生成的代码放入#ifdef块中。text是可选参数,用于提供与弃用有关的文本信息。

newin "<version>"

在文档中添加@newin Doxygen命令,或替换从C文档中生成的@newin命令。

G.3.7.3. _WRAP_ENUM_DOCS_ONLY

这个宏只是为枚举生成一个Doxygen文档块。这对于因为过于复杂(这通常是因为使用了C宏)而无法使用_WRAP_ENUM()包装,且依然需要包含为枚举生成的文档的枚举类型很有用。它与_WRAP_ENUM()使用同一语法,也处理一样的参数(尽管有些额外参数因为没有作用会被忽略)

G.3.7.4. _WRAP_GERROR

这个宏会生成一个C++异常类,生成的类派生自Glib::Error,并具有一个Code枚举成员变量和一个code()成员函数。你必须指定所需的C++名称、以及与其对应的C枚举名、C枚举值前缀。

然后可以由带有errthrow选项的_WRAP_METHOD()生成的成员函数抛出此异常。

示例来自于pixbuf.hg

_WRAP_GERROR(PixbufError, GdkPixbufError, GDK_PIXBUF_ERROR)

_WRAP_GERROR()接受与_WRAP_ENUM()一样的可选参数(尽管CONV_TO_INT参数因为没有作用而被忽略)。

G.3.7.5. _MEMBER_GET / _MEMBER_SET

如果要包装可以被直接访问其数据成员的简单结构体或装箱类型,请使用这些宏。这些宏会为其数据成员创建访问器成员函数(getter和setter)。

_MEMBER_GET(C++名, C名, C++类型, C类型)

_MEMBER_SET(C++名, C名, C++类型, C类型)

示例来自于rectangle.hg

_MEMBER_GET(x, x, int, int)

G.3.7.6. _MEMBER_GET_PTR / _MEMBER_SET_PTR

使用这些宏可以自动为指针类型的数据成员提供访问器(getter和setter)。对于getter函数,将创建两个版本,一个const版本一个非const版本。

_MEMBER_GET_PTR(C++名, C名, C++类型, C类型)

_MEMBER_GET_PTR(C++名, C名, C++类型, C类型)

示例来自于item.hg文件中的Pango::Analysis

// _MEMBER_GET_PTR(engine_lang, lang_engine, EngineLang*, PangoEngineLang*)
// It's just a comment. It's difficult to find a real-world example.

G.3.7.7. _MEMBER_GET_GOBJECT / _MEMBER_SET_GOBJECT

使用这些宏可以为返回之前必须被引用的GObject类型数据成员提供访问器(getters和setters)。

_MEMBER_GET_GOBJECT(C++名, C名, C++类型, C类型)

_MEMBER_SET_GOBJECT(C++名, C名, C++类型, C类型)

示例来自于Pangomm中的layoutline.hg

_MEMBER_GET_GOBJECT(layout, layout, Pango::Layout, PangoLayout*)

G.3.8. gmmproc参数处理

gmmproc允许以各种方式为处理成员函数签名的宏(如_WRAP_METHOD()_WRAP_CTOR()_WRAP_CREATE())处理成员函数签名中的参数:

G.3.8.1. 对参数重新排序

对于所有处理成员函数签名的宏,可以为C++参数指定一个与C函数、虚函数、信号中现有顺序不同的顺序。例如,假设如下所示的C函数将被封装为Gtk::Widget类的C++成员函数:

void gtk_widget_set_device_events(GtkWidget* widget, GdkDevice* device,
  GdkEventMask events);
但是必须改变C++成员函数中两个参数的顺序。如下所示的代码会将函数封装为C++成员函数但两个参数的顺序会有所不同:
_WRAP_METHOD(void set_device_events(Gdk::EventMask events{events},
  const Glib::RefPtr<const Gdk::Device>& device{device}),
  gtk_widget_set_device_events)
成员函数参数后的{c_param_name}告知gmmproc将C++参数映射到{}内的C参数。由于C++参数名与C参数名相对应,所以上述代码可以被重写为:
_WRAP_METHOD(void set_device_events(Gdk::EventMask events{.},
  const Glib::RefPtr<const Gdk::Device>& device{.}),
  gtk_widget_set_device_events)

请注意,当为使用_WRAP_SIGNAL()的成员函数签名重排参数时,C参数名总是p0p1等。因为这些参数名由generate_extra_defs实用工具使用,而该实用工具不在乎C API中的参数名是什么。

G.3.8.2. 可选参数处理

对于除了_WRAP_SIGNAL()_WRAP_VFUNC()以外的所有处理成员函数签名的宏,都可以讲参数设为可选的,这样就可以在没有指定可选参数的情况下额外生成C++成员函数。例如,假设以下所示的*_new()将被封装为Gtk::ToolButton类的构造函数:

GtkToolItem* gtk_tool_button_new(GtkWidget* icon_widget, const gchar* label);
另外,假设C API允许函数的label参数为NULL,这样该参数就是可选的。通过在参数名后附加一个{?}可以使gmmproc生成具有所有参数的原始构造函数和一个带有可选参数的构造函数。在如下所示的情况下:
_WRAP_CTOR(ToolButton(Widget& icon_widget, const Glib::ustring& label{?}),
  gtk_tool_button_new)
将生成两个构造函数:一个带可选参数,另一个不带可选参数。

G.3.8.3. 输出参数处理

使用_WRAP_METHOD()也可以将封装好的C函数的返回值放到C++成员函数的输出参数中,而不是让C++成员函数和C函数一样返回一个值。要做到这个,只需要在C++成员函数参数列表中的输出参数名后附加{OUT}即可。例如,如果gtk_widget_get_request_mode()的声明如下所示:

GtkSizeRequestMode gtk_widget_get_request_mode(GtkWidget* widget);
想要让C++成员函数使用输出参数而不是返回一个SizeRequestMode。可以用如下所示的方法:
_WRAP_METHOD(void get_request_mode(SizeRequestMode& mode{OUT}) const,
  gtk_widget_get_request_mode)
附加在名为mode的输出参数之后的{OUT},告知gmmproc将C函数的返回值放在输出参数中。但是在这种情况下还必须指定必要的初始化宏。如下所示:
_INITIALIZATION(`SizeRequestMode&',`GtkSizeRequestMode',`$3 = (SizeRequestMode)($4)')
也可以写成:
_INITIALIZATION(`SizeRequestMode&',`GtkSizeRequestMode',`$3 = ($1)($4)')

如果被封装的C函数有输出参数,_WRAP_METHOD()也支持将C输出参数设置为C++输出参数。例如,假设我们想封装以下C函数,该函数在C输出参数rect中返回一个值:

gboolean gtk_icon_view_get_cell_rect(GtkIconView* icon_view,
  GtkTreePath* path, GtkCellRenderer* cell, GdkRectangle* rect);
要让gmmproc将返回值放于C++ rect输出参数中,可以使用如下所示的_WRAP_METHOD()宏:
_WRAP_METHOD(bool get_cell_rect(const TreeModel::Path& path,
  const CellRenderer& cell, Gdk::Rectangle& rect{>>}) const,
  gtk_icon_view_get_cell_rect)
rect参数名后面的{>>}表示C++输出参数应该用C函数的C参数返回的值来设置。gmmproc将生成一个用于储存C输出参数的值的临时变量的声明,以及一条使用临时变量设置C++输出参数的语句。在这种情况下,可能需要使用_INITIALIZATION()描述如何从一个GdkRectangle*设置一个Gdk::Rectangle&。如下所示:
_INITIALIZATION(`Gdk::Rectangle&',`GdkRectangle',`$3 = Glib::wrap(&($4))')

G.3.8.4. 处理字符串参数

在C++成员函数中的字符串值的输入参数通常是const Glib::ustring&或者是const std::string&。而在C代码中,它是const gchar*。在将空字符串转换为const gchar*的时候,可以将其转换为nullptr或者是一个指向空字符串的指针(用c_str())。一些C函数的一些参数接受nullptr,并以特殊方式解释它。其他参数不能为nullptr

_WRAP_METHOD()和类似的宏的默认转换是:

  • 对于带有或不带有默认值的强制性参数:空字符串到空字符串
  • 对于附加了{?}的可选参数:空字符串到nullptr
如果默认转换不够好,可以将{NULL}附加到强制性参数之后,或是将{?!NULL}附加到可选参数之后(!NULL意为非NULL)。如果想同时添加C参数名和NULL,则用空格对它们进行分隔:{c_param_name NULL}

G.3.9. 基本类型

C API中使用的一些基本类型在C++中有更好的替代类型。例如,C++中有bool类型,所以不需要gboolean类型。以下列表显示了C API中的一些常用类型,以及你可以在C++封装库中将它们转换为哪些类型。

基本类型的等效项

C 类型: gboolean

C++类型: bool

C 类型: gint

C++类型: int

C 类型: guint

C++类型: guint

C 类型: gdouble

C++类型: double

C 类型: gunichar

C++类型: gunichar

C 类型: gchar*

C++类型: Glib::ustring(或std::string)