在不同perl版本下运行的程序之间传递对象



将对象作为输入参数传递时遇到问题,从perl5.6.pl到perl5.24.pl的不同perl版本(无法从函数"$from_5.24"获得返回值)。下面提供了存在问题的代码。使用windows平台,如何解决这个问题。

在Perls.pm:之间共享

package SharedBetweenPerls;
use warnings;
use strict;
use Exporter;
our @ISA = 'Exporter';
our @EXPORT_OK = qw(getVal);
sub new {
my $self = {
roleId => undef,
username => undef,
};
bless $self, 'SharedBetweenPerls';
return $self;
}
sub getVal{
my ($self) = @_;
return $self->{'roleId'};
}
1;

v5.24.pl:

use warnings;
use strict;
use v5.24;
use lib '.';
my ($self) = @_;
print $self->{'roleId'}; # Not working

v5.6.pl:

use warnings;
use strict;
use lib '.'; 
use SharedBetweenPerls;
my $obj =  new SharedBetweenPerls();
$obj->{'roleId'} = '10000';
$obj->{'username'} = 'test123';
my $roleId = $obj->getVal();
print "Value : $roleId n"; # working fine
my $from_5.24 = qx(path-to-perl-5.24 program_for_5.24.pl "$obj"); 
print "Return from function: $from_5.24"; # Not Working

我尝试过使用zdim提供的序列化测试代码(SharedBetweenPerls.pm)。我有以下格式错误。

malformed JSON string, neither tag, array, object, number, string or atom, at character offset 0 (before "roleId") at SharedBetweenPerls.pm

5.6.pl:

use warnings;
use strict;
use lib '.'; 
use SharedBetweenPerls;
use IPC::System::Simple qw(capturex);
#my $data = '{"roleId":31, "username":"test123"}';
#my $obj = SharedBetweenPerls->new($data);
my $obj = SharedBetweenPerls->new(roleId => 17, username => 'test123');
my $result = capturex('D:Perlbinperl524.exe', 'D:sample_programp5.24.pl', '$obj->serialize');
print "return from function: $result";

5.24.pl:

use warnings;
use strict;
use v5.24;
use lib '.';
use SharedBetweenPerls;
my ($init_json) = @ARGV;
my $obj = SharedBetweenPerls->new( $init_json );
print $obj->{'roleId'};

注意 下面有两种序列化方法——使用Storable(可能是偶然的,因为它需要在不同的Perl和模块版本之间)和自定义的


这是上一个问题中提出的一个更一般的情况,现在一个对象将在程序之间传递。这一变化带来了实质性的不同。

一个对象必须序列化才能传递,这样我们就可以通过某种管道逐字节地传递它。我在这个演示中使用Storable来实现这一目的,但你可能需要寻找其他工具,或者可能需要编写一个自定义过程(在末尾添加的部分演示)

下面将讨论一些其他调整,以下是文件。

包装SharedBetweenPerls.pm

package SharedBetweenPerls;    
use warnings;
use strict;
sub new {
my ($class, %args) = @_; 
my $self = { %args };
return bless $self, $class;
}
sub get_roleId {
my ($self) = @_; 
return $self->{'roleId'};
}
1;

需要在v5.24(v.5.24.pl)下运行的程序

use warnings;
use strict;
use Storable qw(retrieve);
use v5.24;    
use FindBin qw($RealBin); 
use lib $RealBin;       # look for modules in this script's directory
use SharedBetweenPerls;
my ($file) = @ARGV;
my $obj = retrieve($file) // warn "There were errors: $!";
print $obj->get_roleId;

";主";程序,该程序必须在旧的perl下运行

use warnings;
use strict;
use feature 'say';
use Storable qw(store);
use FindBin qw($RealBin); 
use lib $RealBin;   
use SharedBetweenPerls;
my $obj = SharedBetweenPerls->new(roleId => 17, username => 'test123');
my $roleId = $obj->get_roleId();
say "Value for 'roleId' in the new object: $roleId";
my $outfile = "obj_$$.storable";  # store the serialized object in a file
store($obj, $outfile) // warn "There were errors: $!";                 #/
# (replace my perlbrew path with your actual path to the v5.24 executable)
my $perl_524 = qq($ENV{HOME}/perl5/perlbrew/perls/perl-5.30.0/bin/perl);
my $cmd = qq($perl_524 v5.24.pl $outfile);
my $from_524 = qx( $cmd );
chomp $from_524;    
say "Return from function: $from_524";
unlink $outfile or warn "Can't unlink $outfile: $!";  # can delete file now

写入序列化对象的文件的名称应该比我在这个演示中使用的名称好得多,file::Temp是处理临时名称的标准选择。

这将打印

新对象中"roleId"的值:17从函数返回:17

所以对于这个简单的玩具类,这是有效的——对象被正确地传递。

然而,序列化绝非小事。根据实际类的复杂程度,特别是两个程序的模块版本的不同,可能会出现问题。有了v5.6和v5.24的组合,我认为你需要祈祷。(它适用于我的v5.16和v5.30,但v5.6非常非常旧。)

组合store+retrieve是(一种方式)如何使用文件传递复杂数据。我还尝试freeze对象,并手动将其写入一个文件,然后在中读取该文件并thaw它,这也很有效。(将冻结的物体直接沿管道向下传递会出现问题。)

但是传递整个对象可能不起作用,如果您的案例中确实存在问题,那么该怎么做将完全取决于您的类的实际情况。

您总是可以做的一件事是想出一种自定义的序列化方法,通过这种方法,所需的数据将被传递(通过文件或为管道进行适当的序列化),而不是整个对象。然后另一端的程序可以使用它来构造对象

如果数据是使用文件传递的,那么什么是明确的选项,那么我们谈论的是持久性。

评论

  • 当一个包定义了一个类时,没有理由导出符号

  • 不要硬编码软件包名称;因此存在CCD_ 8。但是对于类,包名称会作为构造函数中的第一个参数传递,并且应该使用

  • 不要将对象用作任何旧的hashref,只需取消引用键即可打印值。这触及了类的内部,是一个非常非常糟糕的想法——使用提供的方法。(为此,v.5.24.pl程序也需要加载带有类的包)

  • 如果你想让被调用的程序能够处理一个对象,它应该加载定义该类的包(因为不应该把对象仅仅用作hashref)

  • 间接方法表示法(new ClassName)非常值得避免。请改用普通方法调用(ClassName->new)。首先,构造函数是一个方法

  • 程序的自变量在@ARGV中,而不是在@_

  • 上面的课程需要更多,但这会把我们带到其他地方

  • 我建议使用模块来运行外部命令,而不是backticks(qx)。尝试一些:IPC::System::SimpleCapture::TinyIPC::Run3IPC::Run


示例

向类中添加一个方法,以实现所需的反序列化。它可以简单地创建一个包含所有属性及其值的散列,然后对其进行序列化——例如,从中生成一个JSON字符串

然后v5.6.pl程序可以进行(此处使用IPC::System::Simple模块)

use IPC::System::Simple qw(capturex);
...
my $from_524 = capturex($perl_524, 'v5.24.pl', $obj->serialize);

然后CCD_ 20程序可以进行

my ($init_json) = @ARGV;
my $obj = SharedBetweenPerls->new( $init_json );

因此,现在目标程序有了一个用所需数据构建的对象,可以开始工作了。

这是一个非常基本和粗糙的示例请注意,对于你的项目来说,这可能是一个糟糕的选择,我对此一无所知,而如果它可以使用,那么它需要更多的工作/检查

我使用JSON来序列化属性及其值的哈希;这是在CCD_ 21方法中完成的。然后,这样一个JSON字符串可以用来构造一个新的对象:构造函数检查它是否得到了hashref或字符串,对于字符串,它调用一个初始化对象的方法(init_from_json)。

sub new {                              # WARNING: a sketchy demo only
my ($class, $args) = @_; 
my $self = {}; 
bless $self, $class;
my $ref = ref $args;
if (not $ref) {                    # a string; better be JSON
$self->init_from_json($args);
}   
elsif ($ref eq 'HASH') {           # straight-up attributes, initialize
$self->{$_} = $args->{$_}  for keys %$args;
}   
else { croak "Unsupported invocation..." }  # print user message etc
return $self;
}
sub serialize {
my $self = shift;
require JSON; JSON->import('encode_json');
my %attr = map { $_ => $self->{$_} } keys %$self;
return encode_json(%attr);  # (no objects please)
}
sub init_from_json {
my ($self, $args_json) = @_; 
require JSON; JSON->import('decode_json');
my $args = decode_json($args_json);
$self->{$_} = $args->{$_} for keys %$args;
}
...

现在,v5.6.pl程序可以创建其对象并对其进行序列化,然后使用传递给它的JSON字符串作为输入来调用v5.30.pl程序。然后v5.30.pl程序可以从JSON重建对象并使用它进行工作

根据具体情况,还有许多其他方法可以做到这一点。

如果您要使用OO代码的框架,如MooseMoo,那么有现成的工具和技术会有所帮助。(如果你没有使用过这些框架,那么也会有一些学习曲线。)


根据要求,完成工作程序(最大限度地简化以帮助调试)--

v5.6.pl

use warnings;
use strict;

use IPC::System::Simple qw(capturex);
use FindBin qw($RealBin); 
use lib $RealBin;   
use SharedBetweenPerls;
my $obj = SharedBetweenPerls->new( { roleId => 17, data => [3..7] } );
# (replace my perlbrew path with your actual path to the v5.24 executable)
my $perl_524 = qq($ENV{HOME}/perl5/perlbrew/perls/perl-5.30.0/bin/perl);
my $from_524 = capturex( $perl_524, 'v5.30.pl', $obj->serialize );
print "Return from function: $from_524";

v5.30.pl

use warnings;
use strict;
use v5.24;
use FindBin qw($RealBin); 
use lib $RealBin;   
use SharedBetweenPerls;
my ($init_json) = @ARGV;
my $obj = SharedBetweenPerls->new($init_json);
print $obj->get_roleId;

相关内容

最新更新