-
Notifications
You must be signed in to change notification settings - Fork 3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Moo like #4
base: master
Are you sure you want to change the base?
Moo like #4
Changes from 7 commits
2a3d1f0
81c96a6
8a669f1
508b637
070dd71
57c0663
4a89cb1
c88643b
667f82d
54a5046
7140352
b2e218f
e5dae46
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
#include "xs/meta.h" | ||
#include "xs/common.h" | ||
#include <new> | ||
|
||
namespace caixs { namespace meta { | ||
|
||
typedef AV* PackageMeta; | ||
|
||
static MGVTBL package_marker; | ||
|
||
static int field_meta_free(pTHX_ SV*, MAGIC* mg); | ||
|
||
struct FieldMeta { | ||
SV* name; | ||
SV* required; | ||
SV* default_value; | ||
}; | ||
|
||
|
||
#define FIELDS_PREALLOCADED 5 | ||
#define FIELD_SV_COUNT 3 | ||
|
||
static PackageMeta find_package(HV* stash) { | ||
MAGIC* mg = CAIXS_mg_findext((SV*)stash, PERL_MAGIC_ext, &package_marker); | ||
return (PackageMeta)(mg ? mg->mg_obj : NULL); | ||
} | ||
|
||
static PackageMeta create_package(HV* stash) { | ||
AV* meta = newAV(); | ||
av_extend(meta, FIELDS_PREALLOCADED * FIELD_SV_COUNT); | ||
|
||
sv_magicext((SV*)stash, (SV*)meta, PERL_MAGIC_ext, &package_marker, NULL, 0); | ||
SvREFCNT_dec_NN((SV*)meta); | ||
SvRMAGICAL_off((SV*)stash); | ||
|
||
return meta; | ||
} | ||
|
||
inline size_t size(PackageMeta meta) { return (AvFILLp(meta) + 1) / FIELD_SV_COUNT;} | ||
|
||
void record(PackageMeta meta, SV* hash_key, SV* required, SV* default_value) { | ||
FieldMeta* fields = (FieldMeta*)AvARRAY(meta); | ||
|
||
STRLEN name_len; | ||
char* name = SvPV(hash_key, name_len); | ||
|
||
/* check that there might be already field meta is defined*/ | ||
size_t fields_sz = size(meta); | ||
for(size_t i = 0; i < fields_sz; ++i) { | ||
STRLEN field_len; | ||
char* field_name = SvPV(fields[i].name, field_len); | ||
if (field_len != name_len) continue; | ||
|
||
if (strcmp(name, field_name) == 0) { | ||
croak("object key '%' is already defined", name); | ||
} | ||
} | ||
|
||
if (SvOK(default_value) && (!SvROK(default_value) || SvTYPE(SvRV(default_value)) != SVt_PVCV)) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
croak("'default' should be a code reference"); | ||
|
||
size_t new_sz = AvFILLp(meta) + FIELD_SV_COUNT; | ||
av_fill(meta, new_sz); | ||
|
||
SvREFCNT_inc_simple_NN(hash_key); | ||
SvREFCNT_inc_simple(default_value); | ||
FieldMeta& field = fields[fields_sz]; | ||
|
||
field.name = hash_key; | ||
field.required = SvTRUE(required) ? &PL_sv_yes : NULL; | ||
field.default_value = default_value; | ||
} | ||
|
||
void activate(PackageMeta meta, SV *sv) { | ||
HV* hv = (HV*)SvRV(sv); | ||
|
||
FieldMeta* fields = (FieldMeta*)AvARRAY(meta); | ||
size_t fields_sz = size(meta); | ||
for(size_t i = 0; i < fields_sz; ++i) { | ||
FieldMeta& field = fields[i]; | ||
|
||
STRLEN field_len; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Аналогично комменту про utf8 выше - зачем использовать строковый апи без предпосчитанного хэша? |
||
char* field_name = SvPV(field.name, field_len); | ||
|
||
if (SvOK(field.default_value)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. SvOK упадет на NULL, а при этом ты используешь SvREFCNT_inc_simple для работы в default_value. Так оно NULL или не-NULL? |
||
SV** value = hv_fetch(hv, field_name, field_len, 0); | ||
if (!value) { | ||
dSP; | ||
PUSHMARK(SP); | ||
int count = call_sv(fields->default_value, G_SCALAR | G_NOARGS); | ||
SPAGAIN; | ||
|
||
if (count != 1) croak("unexpected return from 'default': %d, expected: 1", count); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Я бы либо вообще убрал эту проверку (и брал просто 1й элемент со стека), либо сделал текст ошибки более понятным. |
||
|
||
SV* new_val = POPs; | ||
SvREFCNT_inc(new_val); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. _simple_NN |
||
SV** ok = hv_store(hv, field_name, field_len, new_val, 0); | ||
if (!ok) SvREFCNT_dec(new_val); | ||
PUTBACK; | ||
} | ||
} else if (field.required == &PL_sv_yes) { | ||
SV** value = hv_fetch(hv, field_name, field_len, 0); | ||
if (!value) croak("key '%s' is required", field_name); | ||
return; | ||
} | ||
} | ||
} | ||
|
||
// API-helpers | ||
|
||
void install (CV *cv, SV* hash_key, SV* required, SV *default_value) { | ||
GV* gv = CvGV(cv); | ||
if (!gv) croak("cant get CV's glob"); | ||
|
||
HV* stash = GvSTASH(gv); | ||
if (!stash) croak("can't get stash"); | ||
|
||
PackageMeta meta = find_package(stash); | ||
if (!meta) meta = create_package(stash); | ||
if (!meta) return; | ||
|
||
record(meta, hash_key, required, default_value); | ||
} | ||
|
||
void activate(HV* stash, SV* object) { | ||
PackageMeta meta = find_package(stash); | ||
if (!meta) return; | ||
|
||
activate(meta, object); | ||
} | ||
|
||
|
||
|
||
}} | ||
|
||
|
||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
use strict; | ||
use Test::More; | ||
use Class::Accessor::Inherited::XS; | ||
use Class::Accessor::Inherited::XS::Constants; | ||
|
||
sub install { | ||
my ($package, $key, $required, $default) = @_; | ||
Class::Accessor::Inherited::XS::install_constructor("${package}::new"); | ||
Class::Accessor::Inherited::XS::install_object_accessor("${package}::${key}", $key, None); | ||
Class::Accessor::Inherited::XS::test_install_meta("${package}::${key}", $key, $required, $default); | ||
}; | ||
|
||
subtest "check required" => sub { | ||
subtest "required = 1" => sub { | ||
my $package = 't::P' . __LINE__; | ||
install($package, 'foo', 1, undef); | ||
ok (eval { $package->new(foo => 'v'); 1 }, "when required key is supplied, all ok"); | ||
is (eval { $package->new(k => 'v'); 1 }, undef, "when required key is missing, die"); | ||
like $@, qr/key 'foo' is required/; | ||
}; | ||
|
||
subtest "required = 0" => sub { | ||
my $package = 't::P' . __LINE__; | ||
install($package, 'foo', 0, undef); | ||
ok (eval { $package->new(foo => 'v'); 1 }, "when required key is supplied, all ok"); | ||
ok (eval { $package->new(foo => 'v'); 1 }, "when required key is missing, all ok"); | ||
}; | ||
}; | ||
|
||
subtest "check default" => sub { | ||
my $default = \"default-value"; | ||
my $sub = sub { return $$default } ; | ||
my $package = 't::P' . __LINE__; | ||
install($package, 'foo', 0, $sub); | ||
is $package->new(foo => 'v')->{foo}, 'v'; | ||
is $package->new->{foo}, 'default-value'; | ||
|
||
$default = \undef; | ||
is $package->new->{foo}, undef; | ||
}; | ||
|
||
|
||
done_testing; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
#ifndef __INHERITED_XS_COMMON_H_ | ||
#define __INHERITED_XS_COMMON_H_ | ||
|
||
extern "C" { | ||
#include "EXTERN.h" | ||
#include "perl.h" | ||
#include "XSUB.h" | ||
} | ||
|
||
inline MAGIC* | ||
CAIXS_mg_findext(SV* sv, int type, MGVTBL* vtbl) { | ||
MAGIC* mg; | ||
|
||
if (sv) { | ||
for (mg = SvMAGIC(sv); mg; mg = mg->mg_moremagic) { | ||
if (mg->mg_type == type && mg->mg_virtual == vtbl) { | ||
return mg; | ||
} | ||
} | ||
} | ||
|
||
return NULL; | ||
} | ||
|
||
#endif |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Не utf8-безопасно.