NAME Class::XSConstructor - a super-fast (but limited) constructor in XS SYNOPSIS package Person { use Class::XSConstructor qw( name! age email phone ); use Class::XSAccessor { accessors => [qw( name age email phone )], exists_predicates => [qw( age email phone )], }; } DESCRIPTION Class::XSAccessor is able to provide you with a constructor for your class, but it's fairly limited. It basically just does: sub new { my $class = shift; bless { @_ }, ref($class)||$class; } Class::XSConstructor goes a little further towards Moose-like constructors, adding the following features: * Supports initialization from a hashref as well as a list of key-value pairs. * Only initializes the attributes you specified. Given the example in the synposis: my $obj = Person->new(name => "Alice", height => "170 cm"); The height will be ignored because it's not a defined attribute for the class. (In strict mode, an error would be thrown because of the unrecognized parameter instead of it simply being ignored.) * Supports required attributes using an exclamation mark. The name attribute in the synopsis is required. When multiple required attributes are missing, the constructor will only report the first one it encountered, based on the order the attributes were declared in. This is for compatibility with Moo and Moose error messages, which also only report the first missing required attribute. * Supports defaults and builders. For example: use Class::XSAccessor name => { default => '__ANON__' }; Or: use Class::XSAccessor name => { default => sub { return '__ANON__' } }; Or: use Class::XSAccessor name => { builder => '__fallback_name' }; sub __fallback_name { return '__ANON__'; } You can alternatively provide a string of Perl code that will be evaluated to generate the default: use Class::XSAccessor name => { default => \'sprintf("__%s__", uc "anon")' }; If you expect subclasses to need to override defaults, use a builder. Subclasses can simply provide a method of the same name. The XS code has special faster code paths for the following defaults which are all very common values to choose as defaults: default => undef, default => 0, default => 1, default => !!0, default => !!1, default => "", default => \'[]', default => \'{}', If an attribute has a default or builder, its "required" status is ignored. Builders and coderef defaults are likely to siginificantly slow down your constructor. * Provides support for type constraints. use Types::Standard qw(Str Int); use Class::XSConstructor ( "name!" => Str, "age" => { isa => Int, default => 0 }, "email" => Str, "phone" => Str, ); Type constraints can also be provided as coderefs returning a boolean: use Types::Standard qw(Str Int); use Class::XSConstructor ( "name!" => Str, "age" => { isa => Int, default => 0 }, "email" => sub { !ref($_[0]) and $_[0] =~ /\@/ }, "phone" => Str, ); Type constraints are likely to siginificantly slow down your constructor. When multiple attributes fail their type check, the constructor will only report the first one it encountered, based on the order the attributes were declared in. This is for compatibility with Moo and Moose error messages, which also only report the first failed check. Note that Class::XSConstructor is only building your constructor for you. For read-write attributes, *checking the type constraint in the accessor is your responsibility*. * Type coercions. If your type constraint is a Type::Tiny object which provides a coercion: coercion => 1 Otherwise: foo => { default => sub { ... }, isa => sub { ... }, coerce => sub { my $oldval = $_[0]}; ...; return $newval }, } Type coercions are likely to siginificantly slow down your constructor. * Supports Moose/Moo/Class::Tiny-style `BUILD` methods. Including `__no_BUILD__`. * Optionally supports strict-style constructors a la MooX::StrictConstructor and MooseX::StrictConstructor. To opt in, pass "!!" as part of the import line. Although it doesn't really matter where in the list you include it, I recommend putting it at the end for readability. use Class::XSConstructor qw( name! age email phone !! ); Or: use Class::XSConstructor ( "name!" => Str, "age" => Int, "email" => sub { !ref($_[0]) and $_[0] =~ /\@/ }, "phone" => Str, "!!", ); Error messages when violating the strict constructor will list all the unexpected arguments, but the order in which they are listed will be unpredictable. The strict constructor check happens *after* `BUILD` methods have been called. Because `BUILD` methods are passed a reference to the init args hashref, they can alter it, removing certain keys if they need to. For example: use v5.36; package Person { use Class::XSConstructor qw( fullname !! ); use Class::XSAccessor { accessors => [ 'fullname' ] }; sub BUILD ( $self, $args ) { if ( exists $args->{given_name} and exists $args->{surname} ) { $self->fullname( join q{ } => ( delete $args->{given_name}, delete $args->{surname}, ) ); } } } my $bob = Person->new( given_name => 'Bob', surname => 'Dobalina' ); say $bob->fullname; * Constructor names other than `__PACKAGE__->new`: use Class::XSConstructor [ 'Person', 'create' ] => qw( name! age email phone ); my $bob = Person->create( name => 'Bob Dobalina' ); It is NOT possible to create two different constructors for the same class with different attributes for each: use Class::XSConstructor [ 'Person', 'new_by_phone' ] => qw( name! phone ); use Class::XSConstructor [ 'Person', 'new_by_email' ] => qw( name! email ); However, you can create multiple contructors that all use the same defined list of attributes. use Class::XSConstructor [ 'Person', 'new' ] => qw( name! phone email ); Class::XSConstructor::install_constructor( 'Person::new_by_phone' ); Class::XSConstructor::install_constructor( 'Person::new_by_email' ); API This section documents the API of Class::XSConstructor for other modules that wish to wrap its functionality (to perhaps provide additional features like accessors). If you are just using Class::XSConstructor to install a constructor into your class, you can skip this section of the documentation. Functions and Methods None of the following functions are exported. `Class::XSConstructor->import(@optlist)` Does all the setup for a class to install the constructor. Will determine which class to install the constructor into based on `caller` and call the method `new`. You can override this by passing an arrayref of the package name to do the setup for, followed by the method name for the constructor: Class::XSConstructor->import( [ $packagename, $methodname ], @optlist ); For historical reasons, it is also possible to override the package name using: local $Class::XSConstructor::SETUP_FOR = 'Some::Package'; Class::XSConstructor->import( @optlist ); ... Though this does not allow you to provide a method name. Returns nothing. Class::XSConstructor::install_constructor($subname) Just installs the XS constructor without doing some of the necessary setup. $subname is a fully qualified sub name, like "Foo::new". This is automatically done as part of `import`, so if you're using `import`, you don't need to do this. Returns nothing. Use of Package Variables Class::XSConstructor stores its configuration for class Foo in a bunch of package variables with the prefix `Foo::__XSCON_`. The only supported use of these variables that you may need to be aware of is: $Foo::__XSCON_BUILD If set to "0", indicates that XSConstructor should not try to call `BUILD` methods for the class (probably because there are none, so it would be a waste of time scanning through the inheritance tree looking for them). If set to an arrayref of coderefs, these will be the methods which the constructor calls. If set to undef, the constructor will populate this method with either the value "0" or an arrayref of coderefs next time it is called. Any other value is invalid. $Foo::__XSCON_STRICT If set to true, indicates that XSConstructor should use a "strict" constructor, which complains about the presence of any unrecognized keys in the init args hashref. CAVEATS Inheritance will automatically work if you are inheriting from another Class::XSConstructor class, but you need to set @ISA *before* importing from Class::XSConstructor (which will happen at compile time!) An easy way to do this is to use parent before using Class::XSConstructor. package Employee { use parent "Person"; use Class::XSConstructor qw( employee_id! ); use Class::XSAccessor { getters => [qw(employee_id)] }; } BUGS Please report any bugs to . SEE ALSO Class::Tiny, Class::XSAccessor. AUTHOR Toby Inkster . THANKS To everybody in *#xs* on irc.perl.org. COPYRIGHT AND LICENCE This software is copyright (c) 2018-2019 by Toby Inkster. This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself. DISCLAIMER OF WARRANTIES THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.