[RFC][PATCH] Small enhancement for Glib::Object::Subclass, take 3



Hello All,

I have implemented the interface suggested by Muppet, though in perl
(it is quick, dirty, but works).

The interface is now that instead of just Glib::ParamSpec, you can pass
a hash, that has the paramspec under pspec key and may (but need not)
have keys getter and/or setter pointing to coderefs. The order of
arguments for setter is reversed again (since most often the pspec
argument won't be needed and it is easier to ignore last arg).

It works by magicaly wrapping the GET/SET_PROPERTY function. The import
method first maps the hashes in properties to the paramspecs, saving the
getters and setters. Then it sets GET/SET_PROPERTY of the caller to
closures, that first look in the (enclosed) hash of getters/setters, and
if not found, call the (enclosed) original GET/SET_PROPERTY (either from
caller, or default).

-------------------------------------------------------------------------------
                                                 Jan 'Bulb' Hudec <bulb ucw cz>

--- orig/Subclass.pm    2004-04-12 01:03:07.000000000 +0200
+++ mod/Subclass.pm     2004-05-12 00:48:09.000000000 +0200
@@ -23,6 +23,7 @@
 our $VERSION = '0.02';
 
 use Glib;
+use Carp;
 
 =head1 NAME
 
@@ -49,6 +50,17 @@
            'default value',
            [qw/readable writable/]
         ),
+       {
+          pspec => Glib::ParamSpec->string (
+             'other_string',
+             'Other String Property',
+             'This property is another example with getter and setter',
+             'default value',
+             [qw/readable writable/]
+           ),
+          getter => \&get_other_string,
+          setter => \&set_other_string
+        },
      ];
 
 =head1 DESCRIPTION
@@ -162,6 +174,11 @@
 default get and set methods store property data in the object as hash
 values named for the parameter name.
 
+Alternatively, you can use the getter/setter specification for properties, as
+described below in L</PROPERTIES>. Both mechanisms can coexist, gettets/setters
+taking precedence over C<GET/SET_PROPERTY>. Note, that the setter gets
+arguments in different order - pspec is last.
+
 The default C<SET_PROPERTY> looks like this:
 
    my ($self, $pspec, $newval) = @_;
@@ -198,12 +215,12 @@
 
 *new = \&Glib::Object::new;
 
-sub GET_PROPERTY {
+sub _get_hash {
    my ($self, $pspec) = @_;
    $self->{$pspec->get_name};
 }
 
-sub SET_PROPERTY {
+sub _set_hash {
    my ($self, $pspec, $newval) = @_;
    $self->{$pspec->get_name} = $newval;
 }
@@ -212,13 +229,50 @@
    my ($self, $superclass, %arg) = @_;
    my $class = caller;
 
+   my %setters;
+   my %getters;
+   @{$arg{properties}} = map {
+      if(ref($_) eq 'HASH') {
+        carp "Invalid property specification" unless $_->{pspec};
+        my $name = $_->{pspec}->get_name();
+        $setters{$name} = $_->{setter} if $_->{setter};
+        $getters{$name} = $_->{getter} if $_->{getter};
+        $_->{pspec};
+      } else {
+        $_;
+      };        
+   } @{$arg{properties}};
    # the CHECK callback will be executed after the module is compiled
    my $check = sub {
       # "optionally" supply defaults
-      for (qw(new GET_PROPERTY SET_PROPERTY)) {
-         defined &{"$class\::$_"}
-            or *{"$class\::$_"} = \&$_;
-      }
+      # default for new...
+      *{"$class\::new"} = \&new unless(defined &{"$class\::new"});
+
+      # default for GET_PROPERTY ...
+      my $getter = \&_get_hash;
+      $getter = \&{"$class\::GET_PROPERTY"} if(defined &{"$class\::GET_PROPERTY"});
+      *{"$class\::GET_PROPERTY"} = sub { # (0:self, 1:pspec)
+        my ($self, $pspec) = @_;
+        my $fn = $getters{$pspec->get_name};
+        if($fn) {
+           $fn->(@_);
+        } else {
+           $getter->(@_);
+        }
+      };
+
+      # default for SET_PROPERTY  ...
+      my $setter = \&_set_hash;
+      $setter = \&{"$class\::SET_PROPERTY"} if(defined &{"$class\::SET_PROPERTY"});
+      *{"$class\::SET_PROPERTY"} = sub { # (0:self, 1:pspec, 2:newval)
+        my ($self, $pspec, $newval) = @_;
+        my $fn = $setters{$pspec->get_name};
+        if($fn) {
+           $fn->($self, $newval, $pspec);
+        } else {
+           $setter->(@_);
+        }
+      };
    };
    eval "package $class; CHECK { &\$check }";
 
@@ -237,6 +291,13 @@
 constructors, documented in the C API reference's Parameters and Values page,
 as well as L<Glib::ParamSpec>.
 
+You can also specify getter and/or setter with the property. Instead of
+Glib::ParamSpec pass a hash, that contains the Glib::ParamSpec under key
+C<pspec> and either or both keys C<getter> and C<setter> with coderefs as
+values. These will be called to get/set that property instead of
+C<GET/SET_PROPERTY>. The setter gets arguments in different order, ($self,
+$newval, $pspec), so $pspec can be easily ignored.
+
 =head1 SIGNALS
 
 Creating new signals for your new object is easy.  Just provide a hash

Attachment: signature.asc
Description: Digital signature



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]