Next: Inferiors In Python, Previous: Xmethod API, Up: Python API

##### 23.2.2.14 Writing an Xmethod

Implementing xmethods in Python will require implementing xmethod matchers and xmethod workers (see Xmethods In Python). Consider the following C`++` class:

         class MyClass
         {
         public:
           MyClass (int a) : a_(a) { }

           int geta (void) { return a_; }
           int operator+ (int b);

         private:
           int a_;
         };

         int
         MyClass::operator+ (int b)
         {
           return a_ + b;
         }

Let us define two xmethods for the class `MyClass`, one replacing the method `geta`, and another adding an overloaded flavor of `operator+` which takes a `MyClass` argument (the C`++` code above already has an overloaded `operator+` which takes an `int` argument). The xmethod matcher can be defined as follows:

         class MyClass_geta(gdb.xmethod.XMethod):
             def __init__(self):
                 gdb.xmethod.XMethod.__init__(self, 'geta')

             def get_worker(self, method_name):
                 if method_name == 'geta':
                     return MyClassWorker_geta()

         class MyClass_sum(gdb.xmethod.XMethod):
             def __init__(self):
                 gdb.xmethod.XMethod.__init__(self, 'sum')

             def get_worker(self, method_name):
                 if method_name == 'operator+':
                     return MyClassWorker_plus()

         class MyClassMatcher(gdb.xmethod.XMethodMatcher):
             def __init__(self):
                 gdb.xmethod.XMethodMatcher.__init__(self, 'MyClassMatcher')
                 # List of methods 'managed' by this matcher
                 self.methods = [MyClass_geta(), MyClass_sum()]

             def match(self, class_type, method_name):
                 if class_type.tag != 'MyClass':
                     return None
                 workers = []
                 for method in self.methods:
                     if method.enabled:
                         worker = method.get_worker(method_name)
                         if worker:
                             workers.append(worker)

                 return workers

Notice that the `match` method of `MyClassMatcher` returns a worker object of type `MyClassWorker_geta` for the `geta` method, and a worker object of type `MyClassWorker_plus` for the `operator+` method. This is done indirectly via helper classes derived from `gdb.xmethod.XMethod`. One does not need to use the `methods` attribute in a matcher as it is optional. However, if a matcher manages more than one xmethod, it is a good practice to list the xmethods in the `methods` attribute of the matcher. This will then facilitate enabling and disabling individual xmethods via the `enable/disable` commands. Notice also that a worker object is returned only if the corresponding entry in the `methods` attribute of the matcher is enabled.

The implementation of the worker classes returned by the matcher setup above is as follows:

         class MyClassWorker_geta(gdb.xmethod.XMethodWorker):
             def get_arg_types(self):
                 return None

             def __call__(self, obj):
                 return obj['a_']

         class MyClassWorker_plus(gdb.xmethod.XMethodWorker):
             def get_arg_types(self):
                 return gdb.lookup_type('MyClass')

             def __call__(self, obj, other):
                 return obj['a_'] + other['a_']

For gdb to actually lookup a xmethod, it has to be registered with it. The matcher defined above is registered with gdb globally as follows:

         gdb.xmethod.register_xmethod_matcher(None, MyClassMatcher())

If an object `obj` of type `MyClass` is initialized in C`++` code as follows:

         MyClass obj(5);

then, after loading the Python script defining the xmethod matchers and workers into `GDBN`, invoking the method `geta` or using the operator `+` on `obj` will invoke the xmethods defined above:

         (gdb) p obj.geta()
         $1 = 5

         (gdb) p obj + obj
         $2 = 10

Consider another example with a C++ template class:

         template <class T>
         class MyTemplate
         {
         public:
           MyTemplate () : dsize_(10), data_ (new T [10]) { }
           ~MyTemplate () { delete [] data_; }

           int footprint (void)
           {
             return sizeof (T) * dsize_ + sizeof (MyTemplate<T>);
           }

         private:
           int dsize_;
           T *data_;
         };

Let us implement an xmethod for the above class which serves as a replacement for the `footprint` method. The full code listing of the xmethod workers and xmethod matchers is as follows:

         class MyTemplateWorker_footprint(gdb.xmethod.XMethodWorker):
             def __init__(self, class_type):
                 self.class_type = class_type

             def get_arg_types(self):
                 return None

             def __call__(self, obj):
                 return (self.class_type.sizeof +
                         obj['dsize_'] *
                         self.class_type.template_argument(0).sizeof)

         class MyTemplateMatcher_footprint(gdb.xmethod.XMethodMatcher):
             def __init__(self):
                 gdb.xmethod.XMethodMatcher.__init__(self, 'MyTemplateMatcher')

             def match(self, class_type, method_name):
                 if (re.match('MyTemplate<[ \t\n]*[_a-zA-Z][ _a-zA-Z0-9]*>',
                              class_type.tag) and
                     method_name == 'footprint'):
                     return MyTemplateWorker_footprint(class_type)

Notice that, in this example, we have not used the `methods` attribute of the matcher as the matcher manages only one xmethod. The user can enable/disable this xmethod by enabling/disabling the matcher itself.