开发者

Perl framework for datatype checking

So I'd like to bring a bit of Mouse speed to my other subroutines and do a bit of type checking à la

sub mysub {
    my ($self, $int) = @_;
    assert_int($int, 'int[1]');
}

The subroutine arguments is the data to assert & an optional name for this data. This is so we get a more useful error message e.g.

Assertion error! 'int[1]' is not an intege开发者_运维百科r; got 'HASH(0xXXXXXXXXX)'

Writing it in pure Perl was not hard. When you compare it to the speed in Mouse & gfx's other module Data::Util, ~4x faster than pure Perl, it's obvious that pure XS solutions have the upper-hand but they lack this optional parameter name. Writing a small wrapper around Data::Util's check functions are still way slower than using XS directly.

Does anyone know of a suite of fast data type assertion utilities which allow for this field name to be passed into the assertion?

EDIT after @ikegami answer on how to use Mouse's type system it seems that the performance is still lacking in comparison to just using XS libraries. So the question morphs more into Is there an XS based framework which can provide customisable error messages or at least lets you say what the attribute is called.

EDIT this is the code I'm currently comparing the performance against. This has been massively based on gfx's work on Data::Util and Mouse's XS mode. This gives us 5x the speed on a pure Perl solution. It is somewhat unfair to compare C to Perl which is why I was wondering if a framework already exists which can give this performance but with the right kind of error message. I don't want to be writing this stuff if someone else has already done it :)

use Inline C => <<'END_C';

static int S_nv_is_integer(pTHX_ NV const nv) {
  if(nv == (NV)(IV)nv){
    return TRUE;
  }
  else {
    char buf[64];  /* Must fit sprintf/Gconvert of longest NV */
    const char* p;
    (void)Gconvert(nv, NV_DIG, 0, buf);
    p = &buf[0];

    /* -?[0-9]+ */
    if(*p == '-') p++;

    while(*p){
        if(!isDIGIT(*p)){
            return FALSE;
        }
        p++;
    }
    return TRUE;
  }
}

int assert_int(SV* sv, char* attributeName) {
  assert(sv);
  int result;
  if(SvPOKp(sv)){
    int const num_type = grok_number(SvPVX(sv), SvCUR(sv), NULL);
    result = num_type && !(num_type & IS_NUMBER_NOT_INT);
  }
  else if(SvIOKp(sv)){
    result = TRUE;
  }
  else if(SvNOKp(sv)) {
    result = S_nv_is_integer(aTHX_ SvNVX(sv));
  }
  else {
    result = FALSE;
  }

  if(! result) {
    croak("'%s' was not an integer; got '%s'", attributeName, SvPV(sv, PL_na));
  }
  return result;
}

END_C


If you want what Mouse uses, nothing's stopping you.

my $int_constraint = Mouse::Util::TypeConstraints::find_type_constraint('Int');

sub mysub {
    my ($self, $int) = @_;
    $int_constraint->assert_valid($int);
    ...
}

If you use check or die instead of assert_valid if you want to provide your own message.

I've used this with Moose, but it looks like it should work with Mouse too.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜