Search code examples
perlstructexporter

Can I use Exporter in Perl to export a Struct?


I have defined a struct in a module (using Class:Struct) and I am confused as to how it gets exported to another module.

package MyPackage;
use Class::Struct;
use Exporter 'import';
@EXPORT = qw( function1 function2 );
struct(MyStruct=>{'type'=>'$', 'other'=>'$'});

Now if I include MyPackage in a different module, I can directly create a MyStruct variable:

package OtherPackage;
use MyPackage qw(function1 function2);
use Data::Dumper;

sub my_function {
    print Dumper(MyStruct);
}

and if I call my_function, it prints out the empty struct as expected.

However, and this is my confusion, I don't know how to add it to the @EXPORT, or even if I should. Can you please help?


Solution

  • Exporter isn't appropriate here. You said it yourself: you can use MyPackage; and initialize MyStructs. The example you gave, if written without the convenience of Class::Struct, would look something like this:

    package MyPackage;
    use Exporter qw(import);
    our @EXPORT = qw(function1 function2);
    sub function1 { ... }
    sub function2 { ... }
    1;
    
    package MyStruct;
    sub new # Constructor
    { 
        my $self = shift; 
        my %args = @_;
        return bless(
            {
                'MyStruct::type'  => $args{type},
                'MyStruct::other' => $args{other},
            },
            $self,
        );
    }
    
    sub type # Setter/getter for type
    {
        my ($self, $set) = shift;
        return $set ? $self->{type} = $set
                    : $self->{type};
    }
    
    sub other # Setter/getter for other
    {
        ... # So on and so forth
    }
    1;
    

    The difference is that Class::Struct takes the "blueprint" and writes the MyStruct class for you. From perlobj: A class is simply a package. A class provides methods that expect to operate on objects.

    It's pretty confusing to put two packages together in a single file (which is what Class::Struct does implicitly) so instead you could separate them.

    MyPackage.pm:

    package MyPackage;
    use parent qw(Exporter);
    our @EXPORT_OK = qw(function1 function2);
    sub function1 { ... }
    sub function2 { ... }
    1;
    

    MyStruct.pm:

    package MyStruct;
    use Class::Struct;
    struct( 
        MyStruct => { 
            'type' => '$',
            'other' => '$'
        }
    );
    1;
    

    It wasn't clear from your question, but if MyPackage::function1() is meant to operate on the data contained in MyStruct objects, you should scrap MyPackage altogether and provide it as a method in MyStruct:

    package MyStruct;
    use Class::Struct;
    struct( 
        MyStruct => { 
            'type' => '$',
            'other' => '$'
        }
    );
    sub function1
    {
        my $self = shift;
        print "This is function1 in $self\n";
    }
    1;
    

    Finally, instead of exporting anything from MyStruct, just use it like an object:

    use MyStruct;
    my $instance = MyStruct->new( type => 'foo' ); # Instantiating
    $instance->other('bar'); # Setting
    print $instance->type;   # Getting