perlgobject-introspectionmoo

Why is my Moo object that inherits from a non-Moo class blessed into the parent's package for some modules?


I'm trying to create a Gtk3 application in Perl using GObject Introspection and Moo. There's a non-Moo class from Gtk, Gtk::ApplicationWindow, which I subclass through Moo using extends 'Gtk::ApplicationWindow'. The issue is that when an object of that subclass is created, it remains of the type of the parent class - i.e. Gtk::ApplicationWindow.

I tried the same thing by subclassing my own non-Moo based class instead, and the object created from this subclass is of the correct type. What could be the reason for this difference?

use v5.10;
use strict;
use warnings;

# Import the Gtk classes (non-Moo)
use Glib::Object::Introspection;
Glib::Object::Introspection->setup(basename => 'Gtk', version => '3.0', package => 'Gtk');
Glib::Object::Introspection->setup(basename => 'Gio', version => '2.0', package => 'Gio');

#################################################
{
    # A dummy non-Moo class
    package ClassNonMoo;
    sub new { bless {}, shift; }
}

{
    # Moo class extending the dummy class
    package ClassMoo;
    use Moo;

    extends 'ClassNonMoo';

    sub FOREIGNBUILDARGS {
        my ($class, %args) = @_;
        return ($args{app});
    }
}
#################################################

{
    # Moo class extending Gtk::ApplicationWindow
    package ClassMooGtkAppWin;
    use Moo;

    extends 'Gtk::ApplicationWindow';

    sub FOREIGNBUILDARGS {
        my ($class, %args) = @_;
        return ($args{app});
    }
}

#################################################

# Create objects of ClassMoo and ClassMooGtkAppWin
sub create_objects {
    my ($app) = @_;

    my $o1 = ClassMoo->new( app => $app );
    my $o2 = ClassMooGtkAppWin->new( app => $app );

    say "o1 = $o1\no2 = $o2";
    # Output:
    # o1 = ClassMoo=HASH(0x2f7bc50)
    # o2 = Gtk::ApplicationWindow=HASH(0x2f7bd40)
    #
    # Shouldn't o2 be of the type ClassMooGtkAppWin ?

    exit(0);
}

# We can create a GtkApplicationWindow only after creating a GtkApplication and
# running it. This code just ensures that create_object() is called once the
# application is 'active'.
my $app = Gtk::Application->new('org.test', 'flags-none');
$app->signal_connect(activate => sub { create_objects($app) });
$app->run();

Solution

  • "new" constructors need to be written in a way to observe the caller's actual class (which works with subclassing), but they can also hard-code which class they create objects with.

    Compare:

    package MyClass;
    
    # Considerate of subclassing
    sub new {
        my $class = shift;
        return bless {}, $class;
    }
    
    # Doesn't give a shit
    sub new {
        my $class = shift;
        return bless {};
    }
    

    Glib looks like it's an XS module wrapper around a C library, and it might hard code the class.

    You could probably just try to re-bless (call bless again on the object that was constructed) the object into your actual subclass. Not sure exactly how that would work with Moo, but probably in the BUILD method.

    You could also skip inheritance and use delegation instead. Create an attribute to hold the original Window object, and then delegate all methods to it, except your own in the subclass.

    In Moose (not Moo), you can do this with a regex: https://metacpan.org/pod/Moose#handles-ARRAY-HASH-REGEXP-ROLE-ROLETYPE-DUCKTYPE-CODE

    Not sure how to do that nicely with Moo.