File: Using_gtkmmproc.txt

package info (click to toggle)
gtkextramm 0.9.2-6
  • links: PTS
  • area: main
  • in suites: woody
  • size: 1,936 kB
  • ctags: 457
  • sloc: sh: 9,429; cpp: 3,644; perl: 1,077; makefile: 217; ansic: 8
file content (213 lines) | stat: -rw-r--r-- 12,396 bytes parent folder | download | duplicates (3)
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>