After several weeks of intense development, I feel comfortable in releasing
(unleashing?) a new module on CPAN:
Object::InsideOut
Object::InsideOut provides comprehensive support for implementing classes
using the inside-out object model.
It implements inside-out objects as anonymous scalar references that
have been blessed into a class with the scalar containing the ID for the
object (usually a sequence). Object data (i.e., fields) are stored in
arrays within the class's package and are indexed on the object's ID.
This module offers all the capabilities of Class::Std with the following
additional key advantages:
- Speed -
Up to 40%
faster than 'blessed hash' objects for fetching and setting data, and 2 to 6
times faster than Class::Std.
- Threads -
Object::InsideOut is thread safe, and
thoroughly supports sharing objects between threads using 'threads::shared'.
Class::Std is not usable in
threaded applications (or applications that use fork under ActivePerl).
- Flexibility -
Allows control over object ID specification, accessor naming, parameter name
matching, and more.
- mod_perl and Runtime Loading -
Usable from within mod_perl, and supports classes that may be loaded at
runtime (i.e., using eval { require ...; };).
- Exception Objects -
As recommended in 'Perl Best Practices', Object::InsideOut uses
Exception::Class for handling errors in an OO-compatible manner.
- Object Serialization - Object dumping and reloading can be accomplished in either an automated
fashion or through the use of class-supplied subroutines. Class::Std provides
a _DUMP method that is handy for debugging only, and provides no reloading capability.
Rationale:
Prior to the publication of Perl Best Practices, I came across
discussions of inside-out objects here on Perl Monks, and I liked the concept
so much that I converted my other module ( Math::Random::MT::Auto)
to using inside-out objects, including tackling the problem of using inside-out
objects in threaded code (i.e., use threads;).
With the release of Perl Best Practices, I looked at converting
Math::Random::MT::Auto to using Class::Std instead of my own inside-out object
support code. To my dismay, I found that Class::Std was not thread-safe. Further,
its object structure (empty scalar ref) and single method of determining object
IDs (refaddr) were incompatible with what I had written.
While I laud Damian Conway for the work he did on Class::Std, its slowness (due
to calculating the object ID at every turn), lack of thread support, and
inflexibilities (lack of support for user-supplied object ID, accessor naming
conventions, and so on) lead me to further development of my inside-out support
code. In overcoming these deficiencies, the resulting module architecture was
such that it could not be combined with Class:Std, and thus required the
issuance of an new and separate module to CPAN.
As part of the development of this module, I made sure that it incorporated all
of the functionality of Class::Std so that there would be no want for using
this module. Further, I discovered that, unlike hash-based objects, it was
possible to share scalar-based objects between threads when using
threads::shared.
All in all, I feel that Object::InsideOut provides comprehensive support for
the development of inside-out object classes that provides speed and
flexibility, as well as the capability to work with objects in a threaded
application complete with object sharing, if needed.
Plea:
I would like to invite my fellow monks to look at what I've developed. Hopefully,
some of you may find it useful. I would, of course, appreciate any critiques,
suggestions, ideas, etc. that you might have. Thank you.
Update:
The lastest version of Object::InsideOut supports using arrays for storing object
field data. This resulted in an impressive speed improvement (mentioned above) that I
hope will be a further incentive to my fellow monks to give Object::InsideOut a try.
Remember: There's always one more bug.
Re: New Module Announcement: Object::InsideOut
by Ovid (Cardinal) on Nov 01, 2005 at 00:01 UTC
|
I see you're using a CHECK block. This means it has the same fatal flaw as Class::Std. Namely, it won't run under mod_perl. Do you have any plans to get around this issue or is there something I'm not seeing?
| [reply] |
|
Thank you for bringing this to my attention. I have developed a solution for this issue and have uploaded it to CPAN. The solution entailed packaging all the CHECK phase code into a subroutine. For regular usage, this will get run during the CHECK phase.
For code run under mod_perl, or code that is loaded at runtime using require, the subroutine will be called when the first object is created.
In all cases, initialization occurs as necessary and is completely transparent.
I was not able to test this under mod_perl, but it works properly for runtime loaded code.
Remember: There's always one more bug.
| [reply] |
Re: New Module Announcement: Object::InsideOut
by xdg (Monsignor) on Nov 01, 2005 at 02:02 UTC
|
Kudos! I'm very happy to see more work done in this area, particularly that which addresses some of the glaring shortcomings of Class::Std. I look forward to studying it in more detail.
One point of note -- by storing the id in the blessed scalar, you eliminate the ability of an inside-out object to subclass any kind of object. That is one of the two main benefits of inside-out objects (the other being privacy). (See various threads in Threads and fork and CLONE, oh my!). It's at least worth noting in your documentation as you're using a more specific variation of the inside-out technique that trades speed for some flexibility of design.
-xdg
Code written by xdg and posted on PerlMonks is public domain. It is provided as is with no warranties, express or implied, of any kind. Posted code may not have been tested. Use of posted code is at your own risk.
| [reply] |
|
| [reply] |
|
| [reply] |
Re: New Module Announcement: Object::InsideOut
by BrowserUk (Pope) on Nov 01, 2005 at 04:21 UTC
|
I tried to build this on AS810 (5.8.4) and I am getting test failures in 03-threads.t.
Tracing the testscript through, the failure appears to be when the test for is_sharing() is called at InsideOut.pm(388):
# Share the hash, if applicable
if (is_sharing($pkg)) {
threads::shared::share($hash)
}
and
sub is_sharing : PRIVATE
{
my $class = $_[0];
# If not 'use threads::shared;', return false
if (! $threads::shared::threads_shared) {
return;
}
return ($IS_SHARING{$class}->[0]);
}
The if suceeds, so the function returns undef with the result that the hash is not shared.
Without claiming to have read or traced every line, I cannot see anywhere in InsideOut.pm where threads::shared is ever used or required. What am I missing?
Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
"Science is about questioning the status quo. Questioning authority".
In the absence of evidence, opinion is indistinguishable from prejudice.
| [reply] [d/l] [select] |
|
All the failures you received were due to your not having Exception::Class installed ("Can't locate Exception/Class.pm in @INC"), and had nothing to do with the presense or absense of threads::shared.
As to your question concerning threads::shared, it is documented in the POD, namely, the application must use threads::shared;. t/03-threads.t is testing threads without object sharing and thus does not use threads::shared. However, t/04-shared.t does. Hope that helps.
Remember: There's always one more bug.
| [reply] [d/l] |
|
C:\Perl\packages\Object-InsideOut-0.02.00>perl -MException::Class -wle
+"print $Exception::Class::VERSION"
1.22
C:\Perl\packages\Object-InsideOut-0.02.00>nmake test
Microsoft (R) Program Maintenance Utility Version 8.00.40607.16
Copyright (C) Microsoft Corporation. All rights reserved.
C:\Perl\bin\perl.exe "-MExtUtils::Command::MM" "-e" "test_harn
+ess(0, 'blib\lib', 'blib\arch')" t\01-basic.t t\02-auto.t t\03-thread
+s.t t\04-shared.t
t\01-basic......ok
t\02-auto.......ok
t\03-threads....ok 3/12thread failed to start: Can't locate object met
+hod "x" via package "UNIVERSAL" at t\03-threads.t line 48.
t\03-threads....ok 5/12# Looks like you planned 12 tests but only ran
+6.
t\03-threads....dubious
Test returned status 6 (wstat 1536, 0x600)
DIED. FAILED tests 7-12
Failed 6/12 tests, 50.00% okay
t\04-shared.....ok
Failed Test Stat Wstat Total Fail Failed List of Failed
----------------------------------------------------------------------
+---------
t\03-threads.t 6 1536 12 12 100.00% 7-12
Failed 1/4 test scripts, 75.00% okay. 6/53 subtests failed, 88.68% oka
+y.
NMAKE : fatal error U1077: 'C:\Perl\bin\perl.exe' : return code '0xff'
Stop.
As you can see, I have installed Exception::Class, but I am still getting failures from 03threads.t
Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
Lingua non convalesco, consenesco et abolesco. -- Rule 1 has a caveat! -- Who broke the cabal?
"Science is about questioning the status quo. Questioning authority".
In the absence of evidence, opinion is indistinguishable from prejudice.
| [reply] [d/l] |
|
|
|
Re: New Module Announcement: Object::InsideOut
by Aristotle (Chancellor) on Oct 31, 2005 at 21:40 UTC
|
Neat. I like the idea of using $$self to avoid the constant ident $self calls.
I took only a cursory look, but so far I like what I’m seeing.
Makeshifts last the longest.
| [reply] |
Re: New Module Announcement: Object::InsideOut
by Errto (Vicar) on Nov 01, 2005 at 02:45 UTC
|
Seems pretty neat. One little technical question: in the second example in the SYNOPSIS, how does it know that the 'INFO' argument to new() corresponds to the %info hash? As I read it you would need to add 'Field' => \%info to the initArgs hash to do that. | [reply] [d/l] |
|
| [reply] |
Re: New Module Announcement: Object::InsideOut
by diotalevi (Canon) on Nov 03, 2005 at 16:18 UTC
|
Did you cover this test case? That ${} dereferencing (or actually, anything) is still legal? That numfication still worked correctly?
use Test::More tests => 2;
my $obj = Foo::Bar->new;
is( $obj->some_method, "ok" );
is( 0 + $obj, 42 );
package Foo::Bar;
use overload
'${}' => 'some_method'
'0+' => 'numify';
use Object::InsideOut;
sub numify { 42 }
sub some_method { "ok" }
| [reply] [d/l] |
|
Because the object ID is stored in the object's scalar ref (i.e., $$self), overloading ${} deferencing is not allowed. I document this fact in the POD.
However, all other forms of object deferencing through overloading are supported: Stringify, numerify, boolify, arrayify, hashify, globify and codify. Object::InsideOut simplifies defining these operations through the use of subroutine attributes. For example, your code would become:
use Test::More 'no_plan';
package Foo::Bar; {
use Object::InsideOut;
sub numify :Numerify { 42 }
}
package main;
my $obj = Foo::Bar->new();
is(0 + $obj, 42);
Remember: There's always one more bug.
| [reply] [d/l] |
|
If you treat the scalar as an opaque object, store nothing inside it and use overload::StrVal as the key instead, you can allow ${} to be overloaded as well. Data::Postponed uses blessed scalars as the object and allows anything what so ever to be overloaded.
| [reply] |
|
|
|
Re: New Module Announcement: Object::InsideOut
by polettix (Vicar) on Jan 10, 2006 at 11:45 UTC
|
I'd make it clearer in the documentation that you can safely access data using $$self for read reasons. Current docs concentrate in setting values, not getting, and I had my hard time to find something corresponding to the suggested ->set() method.
I needed this mainly to build up a private accessor for a member variable. Which leads to a little feature request: would it be possible to automate the creation of private/protected methods? Or, if already present... would it be possible to read it clearly in the docs?
Flavio
perl -ple'$_=reverse' <<<ti.xittelop@oivalf
Don't fool yourself.
| [reply] [d/l] [select] |
|
| [reply] |
|
|