1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213
|
* Using gtkmmproc *
-------------------
Introduction
------------
The creators of Gtk-- built a program called gtkmmproc which helped them to build C++ wrappers of the Gtk+ C widgets. Realising how useful this was they included it in the Gtk-- install so that the rest of us could use it. By following the instructions in 'The project structure' you can easily create your own project that uses gtkmmproc.
gtkmmproc processes files with a .gen_h suffix. It generates a class header (*.h) file, a class implementation (*.cc) file, and a 'private' header (*_p.h) file. The 'private' header file contains the declaration for the class's Gtk-- type information object.
The .gen_h files contain a mixture of C++ code and gtkmmproc macros. These macros expand to form code in the .h, .cc and _p.h files. If you are lucky then most of your code can be generated by these macros. If you are unlucky then you will need to create some code manually.
There are various examples of these .gen_h files. The files in Gtk-- will tend to be more complicated, but the files in GtkExtra--, GtkGlArea--, and Panel-- are simpler. You should probably open a .gen_h file along side this documentation and refer to it as you read the text.
Running gtkmmproc
-----------------
gkmmproc should be called from the command line like so:
gtkmmproc [options] name srcdir destdir
name: The first part of the file names. e.g. button -> button.gen_h, button.h button.cc button_p.h
srcdir: Where the .gen_h file can be found.
desdir: Where the name.h, name.cc, and private/name_p.h files should be created.
srcdir and destdir can be the same directory, though ideally you should keep them separate.
Two useful options are --local and --m4. --local tells gtkmmproc that it will be creating wrappers which are not part of the main Gtk-- project. You will usually use --local, unless you are actually working on the Gtk-- project. --m4, followed by a directory path, tells gtkmmproc where to find additional type conversions. You will learn more about these conversions below.
If you modify the GtkExtra-- project, instead of creating your own, then you will not need to worry about calling gtkmmproc yourself, or even listing the .gen_h files.
Writing the .gen_h files
------------------------
The following sections describe the structure of a .gen_h file and the macros that should be used.
- Overall structure
The first part of the file is like a normal C++ class header, with some macros added. The second part, under PRIVATE_START includes extra code for the _p.h file. The third part, under IMPL_START includes extra code for the .cc class implementation file. The last two parts are optional.
- Conversions (#m4 or include)
gktmmproc already knows about most types which tend to be used by widget functions. However, you may occasionally need to give it a helping hand by adding an #m4 statement. This is one place where the m4 language creeps into our .gen_h file. But you should be able to add simple conversions just by looking at existing examples.
e.g. #m4 GTKMM_CONVERSION(`GdkDrawable*', `Gdk_Drawable&', `$3.gdkobj()', `$3')
You could also include several lines inside `#m4begin' and `#m4end', or you could put them in a separate .m4 file included from the .gen_h file:
e.g. include(local_convert.m4)
You should place single quotes around the parameters, but be careful to use the correct quote - use `($3)', not '($3)'.
- GTKMM_CONVERSION syntax:
These lines only really make complete sense when you look at the gtkmmproc source, and understand a little #m4.
$1, $2, and $3 represent the first, second, and third arguments to the internal __FWD_CONVERT and __REV_CONVERT functions.
e.g. With the above GTKMM_CONVERSION example:
__FWD_CONVERT is called like so:
__FWD_CONVERT(GdkDrawable*, Gdk_Drawable&, d)
This produces d.gdkobk() from $3.gtkobj()
_FP2PD and _FP2R are short cuts:
_FP2PD = Forward Pointer to Pointer Downcast
_FP2R = Forward Pointer to Reference
- Namespace
You should decide upon a namespace for your set of wrappers in order to keep your classes separate.
- CLASS_START
Declare your namespace before the class declarartion, with the CLASS_START macro.
e.g. CLASS_START(Gtk), and CLASS_START(GtkExtra).
You will also need to use this namespace when you add code under the IMPL_START macro.
- WRAP_CLASS()
This macro provides the information that gtkmmproc needs to calculate the names of the various functions and types which it needs to use to create a Gtk-- wrapper for the GTK+ code. Here is the official parameter listing:
WRAP_CLASS(GtkmmName, // Name of wrapping Gtk-- class (without namespace)
GTKName, // Name of wrapped GTK+ type
GTKCast, // GTK+ "type cast" macro
GTKCheck, // GTK+ "type check" macro
basename, // basename of Gtk-- methods
GtkmmParent, // Name of parent Gtk-- class
GTKParent, // Name of parent GTK+ type
GTKParentCast) // GTK+ "type cast" macro for parent
For instance:
WRAP_CLASS(Button,
GtkButton,
GTK_BUTTON,
GTK_IS_BUTTON,
button,
Gtk::Bin,
GtkBin,
GTK_BIN
);
- Constructors
You will probably need to manually code the constructors. Do so by declaring the method here, like a normal constructor declaration, and implementing it under the IMPL_START macro.
- CTOR_CAST_DEFAULT
This macro creates a default constructor, which uses gtk_object_new(), and then calls initialize_class(). This is enough for some wrappers, but unfortunately you must look into the underlying code for the gtk_something_new function. If the function does anything more, such as initializing member data, then you must manually create the default constructor and duplicate the initialisation code. If you are lucky, the underlying widget will have a gtk_something_construct() function which you can call instead of duplicating the code.
- CTOR_CAST
This macro defines the implementation of a constructor which takes a pointer to an instance of the underlying C widget. This constructor will be declared whether you use this macro or not.
- DTOR
This macro defines the implementation of a standard destructor. The destructor will be declared whether you use this macro or not.
- WRAP_METHOD
This macro takes two arguments - the declarartion of the underlying C function, and the declaration of the class method to be created. The first argument of the GTK+ function should always be a pointer to an instance of the GTK+ widget.
This is where you may need those #m4 conversions, to help gtkmmproc build code that converts between a C type and a suitable C++ class.
nstring:
You might need to use nstring instead of string in your wrapper methods. This should be used when wrapping gchar* parameters and return values which can be NULL. It will ensure that the NULL is passed through to GTK+. It also ensures that a std::string is not constructed from a NULL gchar*, which would lead to a segfault.
Users of your code will not need to use nstrings because they can use a std::string wherever a method uses an nstring.
e.g.
If your method's wrapping needs to be more complicated than a simple one-to-one argument conversion, then you will need to code it manually. For instance, the GTK+ function may accept a pointer to another widget, but your Gtk-- method should probably pass a refernce to a suitable Gtk-- wrapper.
- WRAP_MEMBER
Some GTK+ widgets expect you to access their member data directly. Naturally this is horrific to a C++ developer, so you should wrap those member variables to produce get_ and set_ member methods. You may decide to rename the member or convert it to a more suitable type.
The format is:
WRAP_MEMBER(
attribute, // value, inline, or ref - so far Gtk-- only uses value.
GtkmmName, // Gtk-- member name
GTKName, // GTK+ member name
GtkmmType, // Gtk-- type
GTKType // GTK+ type
);
e.g.
From notebook.gen_h:
WRAP_MEMBER(value, show_tabs, show_tabs, bool, gint);
- SIGNAL_SPEC
Here's an extract from Karl Nelson's email in the gtkmm-main list:
SIGNAL_SPEC("focus", //1
both, //2
gint focus(GtkDirectionType), //3
gtk_container_focus); //4
1. name of signal for gtkmm class
2. Set of attributes associated with the signal.
vfunc - Declare a function for emiting this signal
sig - Declare a signal with connection capality
impl - Declare a _impl method for the user to override
both - Both a signal and impl
emit - Modifies sig to be emitable (implied if arg 4!="")
noemit - Modified sig to be not emitable
marsh - Replace the gtk+ marshaller if rettype != void
translate - cause the arguments to be converted.
(implied if arg 4 != "" and the arguments don`t match)
Use this one explicitly only where you want a new proxy class
declared which you want to fill in with more info.
Example: switch_page signal doesn`t match the emitting function at all,
so we tell it we are going to write translation, then use
m4 to add more functions to the proxy.
fake - (implies translate)
The gtk+ function supplied doesn`t exist, write one for us.
Used in the same place if we want to have a default emit.
These attributes can be combined with a pipe character. e.g. 'vfunc | impl'.
3. function signature calling this signal, connecting
the signal and _impl methods.
4. (optional) gtk+ class function
- If not present, signal will not be user callable.
- If not present and signal is type emit, gtk-- will write
is own gtkmm_foo_method() function for you.
- If present and noemit not specified the signal will
be emittable and therefore callable by anyone.
- If the entire function is specified the signal will have
a full translation activated which will allow the gtk--
and gtk+ types to vary.
Translating signals are the most complex case and look like this
SIGNAL_SPEC("bar", both, void bar(Gtk_Widget*),
void gtk_foo_bar(GtkFoo*, GtkWidget*));
Which will make a signal which is emittable, overriddable
and connectable with the C++ profile of void bar(Gtk_Widget*),
but the internals will be GtkWidget* with conversions as
specified in convert.m4.
- Signals with return types:
If a signal has a return type, but no default implementation, then you will need to use the 'marsh' flag in SIGNAL_SPEC. If you are lucky then Gtk-- will already have defined a marshaller for the signal's particular combination of return type and arguments. Gtkmmproc generates the name of this marshaller, and the compiler will tell you if it is not defined.
If the underlying C code needed to define it's own marshaller, then you will also need to define a Gtk-- marshaller. Look at marshal.h and marshal.cc in the Gtk-- source code, and create your own marshaller by declaring the function at the top of your .gen_h file, and defining it under the START_IMPL macro.
sheet.gen_h in GtkExtra-- contains an example of this technique.
Note that marshalling is only required for real signals, where gtk_signal_new() has been used. Sometimes the GTK+ author just writes a function pointer. Such functions should always be 'vfunc | impl' or just 'impl'. They don`t need a marshaller because there is no signal.
- PRIVATE_START
You will need to add an #include for the private header of your class's base class:
e.g. #include <gtk--/private/button_p.h>
- IMPL_START
You will probably need to manually code your constructor implementations here. Remember to enclose the code with a 'using namespace GtkSomething{}' block. You will also need a 'using namespace Gtk;' statement.
Additional Reading
------------------
It is always a good idea to search the Gtk-- mailing list archive for keywords such as gtkmmproc, WRAP_CLASS, WRAP_METHOD, SIGNAL_SPEC, etc.
Murray Cumming <murrayc@usa.net>
With major input from Karl Nelson <kenelson@ece.ucdavis.edu>
|