new
my $blob = Clownfish::Blob->new($byte_string);
Create a Blob containing the passed-in bytes. END_CONSTRUCTOR $pod_spec->set_synopsis($synopsis); $pod_spec->add_constructor( pod => $constructor );
my $xs_code = <<'END_XS_CODE';
MODULE = Clownfish PACKAGE = Clownfish::Blob
SV* new(either_sv, sv) SV *either_sv; SV *sv; CODE: { STRLEN size; char *ptr = SvPV(sv, size); cfish_Blob *self = (cfish_Blob*)XSBind_new_blank_obj(aTHX_ either_sv); cfish_Blob_init(self, ptr, size); RETVAL = CFISH_OBJ_TO_SV_NOINC(self); } OUTPUT: RETVAL END_XS_CODE
my $binding = Clownfish::CFC::Binding::Perl::Class->new(
parcel => "Clownfish",
class_name => "Clownfish::Blob",
);
$binding->set_pod_spec($pod_spec);
$binding->append_xs($xs_code);
$binding->exclude_constructor;
Clownfish::CFC::Binding::Perl::Class->register($binding);
}
sub bind_boolean { my $pod_spec = Clownfish::CFC::Binding::Perl::Pod->new; my $synopsis = <<'END_SYNOPSIS'; use Clownfish::Boolean qw( $true_singleton $false_singleton );
my $bool = Clownfish::Boolean->singleton($truth_value);
my $truth_value = $bool->get_value;
if ($bool->equals($true_singleton)) {
print "true\n";
}
END_SYNOPSIS
my $description = <<'END_DESCRIPTION';
There are only two singleton instances of this class: C<$true_singleton> and
C<$false_singleton> which are exported on demand.
END_DESCRIPTION
my $constructor = <<'END_CONSTRUCTOR';
=head2 singleton
my $bool = Clownfish::Boolean->singleton($truth_value);
Return either $true_singleton
or $false_singleton
depending on the supplied value. END_CONSTRUCTOR $pod_spec->set_synopsis($synopsis); $pod_spec->set_description($description); $pod_spec->add_constructor( pod => $constructor );
my $xs_code = <<'END_XS_CODE';
MODULE = Clownfish PACKAGE = Clownfish::Boolean
SV* singleton(either_sv, value) SV *either_sv; bool value; CODE: { RETVAL = CFISH_OBJ_TO_SV_INC(cfish_Bool_singleton(value)); } OUTPUT: RETVAL END_XS_CODE
my $binding = Clownfish::CFC::Binding::Perl::Class->new(
parcel => "Clownfish",
class_name => "Clownfish::Boolean",
);
$binding->set_pod_spec($pod_spec);
$binding->append_xs($xs_code);
Clownfish::CFC::Binding::Perl::Class->register($binding);
}
sub bind_bytebuf { my $pod_spec = Clownfish::CFC::Binding::Perl::Pod->new; my $synopsis = <<'END_SYNOPSIS'; my $buf = Clownfish::ByteBuf->new($byte_string); my $byte_string = $buf->to_perl; END_SYNOPSIS my $constructor = <<'END_CONSTRUCTOR'; =head2 new
my $buf = Clownfish::ByteBuf->new($byte_string);
Create a ByteBuf containing the passed-in bytes. END_CONSTRUCTOR $pod_spec->set_synopsis($synopsis); $pod_spec->add_constructor( pod => $constructor );
my $xs_code = <<'END_XS_CODE';
MODULE = Clownfish PACKAGE = Clownfish::ByteBuf
SV* new(either_sv, sv) SV *either_sv; SV *sv; CODE: { STRLEN size; char *ptr = SvPV(sv, size); cfish_ByteBuf *self = (cfish_ByteBuf*)XSBind_new_blank_obj(aTHX_ either_sv); cfish_BB_init_bytes(self, ptr, size); RETVAL = CFISH_OBJ_TO_SV_NOINC(self); } OUTPUT: RETVAL END_XS_CODE
my $binding = Clownfish::CFC::Binding::Perl::Class->new(
parcel => "Clownfish",
class_name => "Clownfish::ByteBuf",
);
$binding->set_pod_spec($pod_spec);
$binding->append_xs($xs_code);
$binding->exclude_constructor;
Clownfish::CFC::Binding::Perl::Class->register($binding);
}
sub bind_charbuf { my $pod_spec = Clownfish::CFC::Binding::Perl::Pod->new; my $synopsis = <<'END_SYNOPSIS'; my $buf = Clownfish::CharBuf->new; $buf->cat('abc'); $buf->cat_char(ord("\n")); print $buf->to_string; END_SYNOPSIS my $constructor = <<'END_CONSTRUCTOR'; my $buf = Clownfish::CharBuf->new( capacity => 256 ); END_CONSTRUCTOR $pod_spec->set_synopsis($synopsis); $pod_spec->add_constructor( sample => $constructor );
my $binding = Clownfish::CFC::Binding::Perl::Class->new(
parcel => "Clownfish",
class_name => "Clownfish::CharBuf",
);
$binding->set_pod_spec($pod_spec);
Clownfish::CFC::Binding::Perl::Class->register($binding);
}
sub bind_string { my $pod_spec = Clownfish::CFC::Binding::Perl::Pod->new; my $synopsis = <<'END_SYNOPSIS'; my $string = Clownfish::String->new('abc'); print $string->to_perl, "\n"; END_SYNOPSIS my $constructor = <<'END_CONSTRUCTOR'; =head2 new
my $string = Clownfish::String->new($perl_string);
Return a String containing the passed-in Perl string. END_CONSTRUCTOR $pod_spec->set_synopsis($synopsis); $pod_spec->add_constructor( pod => $constructor );
my $xs_code = <<'END_XS_CODE';
MODULE = Clownfish PACKAGE = Clownfish::String
SV* new(either_sv, sv) SV *either_sv; SV *sv; CODE: { STRLEN size; char *ptr = SvPVutf8(sv, size); cfish_String *self = (cfish_String*)XSBind_new_blank_obj(aTHX_ either_sv); cfish_Str_init_from_trusted_utf8(self, ptr, size); RETVAL = CFISH_OBJ_TO_SV_NOINC(self); } OUTPUT: RETVAL END_XS_CODE
my $binding = Clownfish::CFC::Binding::Perl::Class->new(
parcel => "Clownfish",
class_name => "Clownfish::String",
);
$binding->set_pod_spec($pod_spec);
$binding->append_xs($xs_code);
$binding->exclude_constructor;
Clownfish::CFC::Binding::Perl::Class->register($binding);
}
sub bind_stringiterator { my @hand_rolled = qw( Next Prev );
my $pod_spec = Clownfish::CFC::Binding::Perl::Pod->new;
my $synopsis = <<'END_SYNOPSIS';
my $iter = $string->top;
while (my $code_point = $iter->next) {
...
}
END_SYNOPSIS
my $next_pod = <<'END_POD';
=head2 next
my $code_point = $iter->next;
Return the code point after the current position and advance the iterator. Returns undef at the end of the string. Returns zero but true for U+0000. END_POD my $prev_pod = <<'END_POD'; =head2 prev
my $code_point = $iter->prev;
Return the code point before the current position and go one step back. Returns undef at the start of the string. Returns zero but true for U+0000. END_POD $pod_spec->set_synopsis($synopsis); $pod_spec->add_method( method => 'Next', alias => 'next', pod => $next_pod, ); $pod_spec->add_method( method => 'Prev', alias => 'prev', pod => $prev_pod, );
my $xs_code = <<'END_XS_CODE';
MODULE = Clownfish PACKAGE = Clownfish::StringIterator
SV* next(self) cfish_StringIterator *self; CODE: { int32_t cp = CFISH_StrIter_Next(self);
if (cp == CFISH_STR_OOB) {
RETVAL = &PL_sv_undef;
}
else if (cp == 0) {
/* Zero but true. */
RETVAL = newSVpvn("0e0", 3);
}
else {
RETVAL = newSViv(cp);
}
}
OUTPUT: RETVAL
SV* prev(self) cfish_StringIterator *self; CODE: { int32_t cp = CFISH_StrIter_Prev(self);
if (cp == CFISH_STR_OOB) {
RETVAL = &PL_sv_undef;
}
else if (cp == 0) {
/* Zero but true. */
RETVAL = newSVpvn("0e0", 3);
}
else {
RETVAL = newSViv(cp);
}
}
OUTPUT: RETVAL
END_XS_CODE
my $binding = Clownfish::CFC::Binding::Perl::Class->new(
parcel => "Clownfish",
class_name => "Clownfish::StringIterator",
);
$binding->set_pod_spec($pod_spec);
$binding->exclude_method($_) for @hand_rolled;
$binding->append_xs($xs_code);
Clownfish::CFC::Binding::Perl::Class->register($binding);
}
sub bind_err { my $pod_spec = Clownfish::CFC::Binding::Perl::Pod->new; my $synopsis = <<'END_SYNOPSIS'; package MyErr; use base qw( Clownfish::Err );
...
package main;
use Scalar::Util qw( blessed );
while (1) {
eval {
do_stuff() or MyErr->throw("retry");
};
if ( blessed($@) and $@->isa("MyErr") ) {
warn "Retrying...\n";
}
else {
# Re-throw.
die "do_stuff() died: $@";
}
}
END_SYNOPSIS
$pod_spec->set_synopsis($synopsis);
my $xs_code = <<'END_XS_CODE';
MODULE = Clownfish PACKAGE = Clownfish::Err
SV* trap(routine_sv, context_sv) SV *routine_sv; SV *context_sv; CODE: cfish_Err *error = XSBind_trap(routine_sv, context_sv); RETVAL = CFISH_OBJ_TO_SV_NOINC(error); OUTPUT: RETVAL END_XS_CODE
my $binding = Clownfish::CFC::Binding::Perl::Class->new(
parcel => "Clownfish",
class_name => "Clownfish::Err",
);
$binding->bind_constructor( alias => '_new' );
$binding->set_pod_spec($pod_spec);
$binding->append_xs($xs_code);
Clownfish::CFC::Binding::Perl::Class->register($binding);
}
sub bind_hash { my @hand_rolled = qw( Store );
my $pod_spec = Clownfish::CFC::Binding::Perl::Pod->new;
my $synopsis = <<'END_SYNOPSIS';
my $hash = Clownfish::Hash->new;
$hash->store($key, $value);
my $value = $hash->fetch($key);
END_SYNOPSIS
my $constructor = <<'END_CONSTRUCTOR';
my $hash = Clownfish::Hash->new( capacity => 256 );
END_CONSTRUCTOR
my $store_pod = <<'END_POD';
=head2 store
$hash->store($key, $value);
Store a key-value pair. END_POD $pod_spec->set_synopsis($synopsis); $pod_spec->add_constructor( sample => $constructor ); $pod_spec->add_method( method => 'Store', alias => 'store', pod => $store_pod, );
my $xs_code = <<'END_XS_CODE';
MODULE = Clownfish PACKAGE = Clownfish::Hash
SV*
fetch_raw(self, key)
cfish_Hash *self;
cfish_String *key;
CODE:
RETVAL = CFISH_OBJ_TO_SV_INC(CFISH_Hash_Fetch_IMP(self, key));
OUTPUT: RETVAL
void store(self, key, value_sv); cfish_Hash *self; cfish_String *key; SV *value_sv; PPCODE: { cfish_Obj *value = (cfish_Obj*)XSBind_perl_to_cfish_nullable(aTHX_ value_sv, CFISH_OBJ); CFISH_Hash_Store_IMP(self, key, value); } END_XS_CODE
my $binding = Clownfish::CFC::Binding::Perl::Class->new(
parcel => "Clownfish",
class_name => "Clownfish::Hash",
);
$binding->set_pod_spec($pod_spec);
$binding->exclude_method($_) for @hand_rolled;
$binding->append_xs($xs_code);
Clownfish::CFC::Binding::Perl::Class->register($binding);
}
sub bind_hashiterator { my $pod_spec = Clownfish::CFC::Binding::Perl::Pod->new; my $synopsis = <<'END_SYNOPSIS'; my $iter = Clownfish::HashIterator->new($hash); while ($iter->next) { my $key = $iter->get_key; my $value = $iter->get_value; } END_SYNOPSIS my $constructor = <<'END_CONSTRUCTOR'; my $iter = Clownfish::HashIterator->new($hash); END_CONSTRUCTOR $pod_spec->set_synopsis($synopsis); $pod_spec->add_constructor( sample => $constructor );
my $xs_code = <<'END_XS_CODE';
MODULE = Clownfish PACKAGE = Clownfish::HashIterator
SV* new(either_sv, hash) SV *either_sv; cfish_Hash *hash; CODE: { cfish_HashIterator *self = (cfish_HashIterator*)XSBind_new_blank_obj(aTHX_ either_sv); cfish_HashIter_init(self, hash); RETVAL = CFISH_OBJ_TO_SV_NOINC(self); } OUTPUT: RETVAL END_XS_CODE
my $binding = Clownfish::CFC::Binding::Perl::Class->new(
parcel => "Clownfish",
class_name => "Clownfish::HashIterator",
);
$binding->set_pod_spec($pod_spec);
$binding->append_xs($xs_code);
$binding->exclude_constructor;
Clownfish::CFC::Binding::Perl::Class->register($binding);
}
sub bind_float { my $pod_spec = Clownfish::CFC::Binding::Perl::Pod->new; my $synopsis = <<'END_SYNOPSIS'; my $float = Clownfish::Float->new(2.5); my $value = $float->get_value; END_SYNOPSIS my $constructor = <<'END_CONSTRUCTOR'; my $float = Clownfish::Float->new($value); END_CONSTRUCTOR $pod_spec->set_synopsis($synopsis); $pod_spec->add_constructor( sample => $constructor );
my $xs_code = <<'END_XS_CODE';
MODULE = Clownfish PACKAGE = Clownfish::Float
SV* new(either_sv, value) SV *either_sv; double value; CODE: { cfish_Float *self = (cfish_Float*)XSBind_new_blank_obj(aTHX_ either_sv); cfish_Float_init(self, value); RETVAL = CFISH_OBJ_TO_SV_NOINC(self); } OUTPUT: RETVAL END_XS_CODE
my $binding = Clownfish::CFC::Binding::Perl::Class->new(
parcel => "Clownfish",
class_name => "Clownfish::Float",
);
$binding->set_pod_spec($pod_spec);
$binding->append_xs($xs_code);
$binding->exclude_constructor;
Clownfish::CFC::Binding::Perl::Class->register($binding);
}
sub bind_integer { my $pod_spec = Clownfish::CFC::Binding::Perl::Pod->new; my $synopsis = <<'END_SYNOPSIS'; my $integer = Clownfish::Integer->new(7); my $value = $integer->get_value; END_SYNOPSIS my $constructor = <<'END_CONSTRUCTOR'; my $integer = Clownfish::Integer->new($value); END_CONSTRUCTOR $pod_spec->set_synopsis($synopsis); $pod_spec->add_constructor( sample => $constructor );
my $xs_code = <<'END_XS_CODE';
MODULE = Clownfish PACKAGE = Clownfish::Integer
SV* new(either_sv, value) SV *either_sv; int64_t value; CODE: { cfish_Integer *self = (cfish_Integer*)XSBind_new_blank_obj(aTHX_ either_sv); cfish_Int_init(self, value); RETVAL = CFISH_OBJ_TO_SV_NOINC(self); } OUTPUT: RETVAL END_XS_CODE
my $binding = Clownfish::CFC::Binding::Perl::Class->new(
parcel => "Clownfish",
class_name => "Clownfish::Integer",
);
$binding->set_pod_spec($pod_spec);
$binding->append_xs($xs_code);
$binding->exclude_constructor;
Clownfish::CFC::Binding::Perl::Class->register($binding);
}
sub bind_obj { my $pod_spec = Clownfish::CFC::Binding::Perl::Pod->new; my $synopsis = <<'END_SYNOPSIS'; package MyObj; use base qw( Clownfish::Obj );
# Inside-out member var.
my %foo;
sub new {
my ( $class, %args ) = @_;
my $foo = delete $args{foo};
my $self = $class->SUPER::new(%args);
$foo{$$self} = $foo;
return $self;
}
sub get_foo {
my $self = shift;
return $foo{$$self};
}
sub DESTROY {
my $self = shift;
delete $foo{$$self};
$self->SUPER::DESTROY;
}
END_SYNOPSIS
my $description = <<'END_DESCRIPTION';
Clownfish::Obj is the base class of the Clownfish object hierarchy.
From the standpoint of a Perl programmer, all classes are implemented as blessed scalar references, with the scalar storing a pointer to a C struct.
Subclassing
The recommended way to subclass Clownfish::Obj and its descendants is to use the inside-out design pattern. (See Class::InsideOut for an introduction to inside-out techniques.)
Since the blessed scalar stores a C pointer value which is unique per-object, $$self
can be used as an inside-out ID.
# Accessor for 'foo' member variable.
sub get_foo {
my $self = shift;
return $foo{$$self};
}
Caveats:
Inside-out aficionados will have noted that the "cached scalar id" stratagem recommended above isn't compatible with ithreads.
Overridden methods must not return undef unless the API specifies that returning undef is permissible. (Failure to adhere to this rule currently results in a segfault rather than an exception.)
CONSTRUCTOR
new
my $self = $class->SUPER::new;
Abstract constructor -- must be invoked via a subclass. Attempting to instantiate objects of class "Clownfish::Obj" directly causes an error.
Takes no arguments; if any are supplied, an error will be reported. END_DESCRIPTION my $to_perl_pod = <<'END_POD'; =head2 to_perl
my $native = $obj->to_perl;
Tries to convert the object to its native Perl representation. END_POD my $destroy_pod = <<'END_POD'; =head2 DESTROY
All Clownfish classes implement a DESTROY method; if you override it in a subclass, you must call $self->SUPER::DESTROY
to avoid leaking memory. END_POD $pod_spec->set_synopsis($synopsis); $pod_spec->set_description($description); $pod_spec->add_method( method => 'To_Host', alias => 'to_perl', pod => $to_perl_pod, ); $pod_spec->add_method( method => 'Destroy', alias => 'DESTROY', pod => $destroy_pod, );
my $xs_code = <<'END_XS_CODE';
MODULE = Clownfish PACKAGE = Clownfish::Obj
SV* get_class(self) cfish_Obj *self CODE: cfish_Class *klass = cfish_Obj_get_class(self); RETVAL = (SV*)CFISH_Class_To_Host(klass); OUTPUT: RETVAL
SV* get_class_name(self) cfish_Obj *self CODE: cfish_String *class_name = cfish_Obj_get_class_name(self); RETVAL = (SV*)CFISH_Str_To_Host(class_name); OUTPUT: RETVAL
bool is_a(self, class_name) cfish_Obj *self; cfish_String *class_name; CODE: { cfish_Class *target = cfish_Class_fetch_class(class_name); RETVAL = cfish_Obj_is_a(self, target); } OUTPUT: RETVAL
SV* clone_raw(self) cfish_Obj *self; CODE: RETVAL = CFISH_OBJ_TO_SV_NOINC(CFISH_Obj_Clone(self)); OUTPUT: RETVAL
SV* to_perl(self) cfish_Obj *self; CODE: RETVAL = (SV*)CFISH_Obj_To_Host(self); OUTPUT: RETVAL END_XS_CODE
my $binding = Clownfish::CFC::Binding::Perl::Class->new(
parcel => "Clownfish",
class_name => "Clownfish::Obj",
);
$binding->bind_method(
alias => 'DESTROY',
method => 'Destroy',
);
$binding->append_xs($xs_code);
$binding->set_pod_spec($pod_spec);
Clownfish::CFC::Binding::Perl::Class->register($binding);
}
sub bind_vector { my @hand_rolled = qw( Store );
my $pod_spec = Clownfish::CFC::Binding::Perl::Pod->new;
my $synopsis = <<'END_SYNOPSIS';
my $vector = Clownfish::Vector->new;
$vector->store($tick, $value);
my $value = $vector->fetch($tick);
END_SYNOPSIS
my $constructor = <<'END_CONSTRUCTOR';
my $vector = Clownfish::Vector->new( capacity => 256 );
END_CONSTRUCTOR
my $store_pod = <<'END_POD';
=head2 store
$vector->store($tick, $elem)
Store an element at index tick
, possibly displacing an existing element. END_POD $pod_spec->set_synopsis($synopsis); $pod_spec->add_constructor( sample => $constructor ); $pod_spec->add_method( method => 'Store', alias => 'store', pod => $store_pod, );
my $xs_code = <<'END_XS_CODE';
MODULE = Clownfish PACKAGE = Clownfish::Vector
SV* pop_raw(self) cfish_Vector *self; CODE: RETVAL = CFISH_OBJ_TO_SV_NOINC(CFISH_Vec_Pop(self)); OUTPUT: RETVAL
SV* delete_raw(self, tick) cfish_Vector *self; uint32_t tick; CODE: RETVAL = CFISH_OBJ_TO_SV_NOINC(CFISH_Vec_Delete(self, tick)); OUTPUT: RETVAL
void store(self, tick, value); cfish_Vector *self; uint32_t tick; cfish_Obj *value; PPCODE: { if (value) { CFISH_INCREF(value); } CFISH_Vec_Store_IMP(self, tick, value); }
SV* fetch_raw(self, tick) cfish_Vector *self; uint32_t tick; CODE: RETVAL = CFISH_OBJ_TO_SV_INC(CFISH_Vec_Fetch(self, tick)); OUTPUT: RETVAL END_XS_CODE
my $binding = Clownfish::CFC::Binding::Perl::Class->new(
parcel => "Clownfish",
class_name => "Clownfish::Vector",
);
$binding->set_pod_spec($pod_spec);
$binding->exclude_method($_) for @hand_rolled;
$binding->append_xs($xs_code);
Clownfish::CFC::Binding::Perl::Class->register($binding);
}
sub bind_class { my $pod_spec = Clownfish::CFC::Binding::Perl::Pod->new; my $synopsis = <<'END_SYNOPSIS'; my $class = Clownfish::Class->fetch_class('Foo::Bar'); my $subclass = Clownfish::Class->singleton('Foo::Bar::Jr', $class); END_SYNOPSIS my $fetch_class_sample = <<'END_CONSTRUCTOR'; my $class = Clownfish::Class->fetch_class($class_name); END_CONSTRUCTOR my $singleton_sample = <<'END_CONSTRUCTOR'; my $class = Clownfish::Class->singleton( class_name => $class_name, parent => $parent, ); END_CONSTRUCTOR $pod_spec->set_synopsis($synopsis); $pod_spec->add_constructor( alias => 'fetch_class', sample => $fetch_class_sample, ); $pod_spec->add_constructor( alias => 'singleton', sample => $singleton_sample, );
my $xs_code = <<'END_XS_CODE';
MODULE = Clownfish PACKAGE = Clownfish::Class
SV* fetch_class(unused_sv, class_name) SV *unused_sv; cfish_String *class_name; CODE: { cfish_Class *klass = cfish_Class_fetch_class(class_name); CFISH_UNUSED_VAR(unused_sv); RETVAL = klass ? (SV*)CFISH_Class_To_Host(klass) : &PL_sv_undef; } OUTPUT: RETVAL
SV* singleton(unused_sv, ...) SV *unused_sv; CODE: { static const XSBind_ParamSpec param_specs[2] = { XSBIND_PARAM("class_name", true), XSBIND_PARAM("parent", false), }; int32_t locations[2]; cfish_String *class_name = NULL; cfish_Class *parent = NULL; cfish_Class *singleton = NULL; CFISH_UNUSED_VAR(unused_sv); XSBind_locate_args(aTHX_ &(ST(0)), 1, items, param_specs, locations, 2); class_name = (cfish_String*)XSBind_arg_to_cfish( aTHX_ ST(locations[0]), "class_name", CFISH_STRING, CFISH_ALLOCA_OBJ(CFISH_STRING)); if (locations[1] < items) { parent = (cfish_Class*)XSBind_arg_to_cfish_nullable( aTHX_ ST(locations[1]), "parent", CFISH_CLASS, NULL); } singleton = cfish_Class_singleton(class_name, parent); RETVAL = (SV*)CFISH_Class_To_Host(singleton); } OUTPUT: RETVAL END_XS_CODE
my $binding = Clownfish::CFC::Binding::Perl::Class->new(
parcel => "Clownfish",
class_name => "Clownfish::Class",
);
$binding->set_pod_spec($pod_spec);
$binding->append_xs($xs_code);
Clownfish::CFC::Binding::Perl::Class->register($binding);
}
sub bind_stringhelper { my $xs_code = <<'END_XS_CODE'; MODULE = Clownfish PACKAGE = Clownfish::Util::StringHelper
Turn an SV's UTF8 flag on. Equivalent to Encode::_utf8_on, but we don't have to load Encode.
Turn an SV's UTF8 flag off.
Upgrade a SV to UTF8, converting Latin1 if necessary. Equivalent to utf::upgrade().
Concatenate one scalar onto the end of the other, ignoring UTF-8 status of the second scalar. This is necessary because $not_utf8 . $utf8 results in a scalar which has been infected by the UTF-8 flag of the second argument.