在不同Perl版本下运行的程序之间传递对象 [英] Passing object between programs running under different perl versions

查看:109
本文介绍了在不同Perl版本下运行的程序之间传递对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

传递对象作为具有不同perl版本(从perl5.6.pl到perl5.24.pl的perl)的输入参数时遇到问题(无法从函数"$ from_5.24获取返回值" ").在有问题的代码下方提供..使用Windows平台,如何解决此问题.

Facing an issue while passing object as an input parameter with different perl versions from perl5.6.pl to perl5.24.pl (unable to get the return value from function "$from_5.24"). Provided below the code which has the problem. Using windows platform, how to solve this issue.

SharedBetweenPerls.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).我遇到以下格式错误的错误.

I have tried using serialize test code(SharedBetweenPerls.pm) given by zdim. I have got following malformed error.

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:\Perl\bin\perl524.exe', 'D:\sample_program\p5.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和模块版本之间),以及一种自定义的方法(演示)

Note   There are two serialization approaches below -- using Storable (may be hit or miss given that it need be between different Perl and thus module versions), and a custom one (demo)

这是上一个问题中提出的问题的更一般情况,其中现在要传递一个对象程序之间.这种变化带来了很大的不同.

This is a more general case of the problem posed in the previous question, where now an object is to be passed between programs. This change brings about a substantial difference.

必须对对象进行序列化才能传递.在此演示中,我为此目的使用可存储,但您可能需要查看一下 其他工具,或者编写自定义流程.

An object must be serialized in order to be passed around. I use Storable for that purpose in this demo, but you may need to look for other tools or perhaps write a custom process.

还有一些其他的调整,这将在下面进行讨论,这是文件.

With some other adjustments, to be discussed below, here are the files.

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)下运行的程序

The program that needs to run under 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下运行

The "main" program, that must run under an older 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

写入序列化对象的文件应比我在此演示中使用的文件好得多,并且

The file to which the serialized object is written should have a far better name than what I use in this demo, and File::Temp is the standard choice for handling temporary names.

此打印


Value for 'roleId' in the new object: 17
Return from function: 17

因此对于这个简单的玩具类来说,它可以正常工作-对象已正确传递.

So for this simple toy-class this works -- the object gets passed correctly.

但是,序列化绝不是一件小事.根据实际类的复杂程度,尤其是两个程序的模块版本的不同,可能会出现问题.对于v5.6和v5.24,我认为您需要保持双手交叉. (它可以与我的v5.16和v5.30一起使用,但v5.6非常老.)

However, serialization is by no means a trivial affair. Depending on how complex your actual class is and, in particular, how different the module versions for two programs are, there may be problems. With your combo of v5.6 and v5.24 I think you need to keep fingers crossed. (It worked with my v5.16 and v5.30 but v5.6 is very, very old.)

store + retrieve组合是使用文件传递复杂数据的方式(一种方式).我还尝试了freeze该对象并将其手动写入文件,然后在其中读取该文件并thaw进行了正常工作. (将冰冻的物体直接送入管道非常麻烦.)

The combination store+retrieve is (one way for) how complex data is passed using files. I also tried to freeze the object and manually write it to a file and then read that file in and thaw it, and that worked as well. (Passing the frozen object directly down the pipe is terribly troubled.)

但是传递整个对象可能只是行不通的,如果您的情况确实存在问题,那么该做什么将完全取决于您的类的实际情况.

But passing a whole object may just not work, and if there are indeed problems in your case then what to do would depend entirely on what your class is actually like.

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

One thing you can always do is to come up with a custom serialization approach, whereby needed data is passed along (via a file or suitably serialized for a pipe), not the whole object. Then the program on the other end can use that to construct the object.

评论

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

  • When a package defines a class there is no reason to export symbols

不要硬编码软件包名称;有__PACKAGE__.但是对于一个类,包的名称将作为构造函数中的第一个参数传递,并且应该使用

Don't hard-code the package name; there is __PACKAGE__ for that. But with a class the name of the package gets passed as the first argument in the constructor, and that should be used

请勿将对象用作任何旧的hashref,在该哈希引用中,只需取消引用键即可打印值.嘲笑一个类的内部,这是一个非常非常糟糕的主意-使用提供的方法. (为此,v.5.24.pl程序也需要使用该类加载程序包)

Don't use an object as any old hashref, where one simply dereferences a key to print a value. That pokes at a class internals and is a really, really bad idea -- use provided methods. (For that the v.5.24.pl program needs to load the package with the class, too)

如果您希望被调用程序能够与某个对象一起使用,则应在定义该类的位置加载程序包(因为不应将该对象用作纯粹的hashref)

If you want the called program to be able to work with an object it should load the package where that class is defined (since one shouldn't use the object as a mere hashref)

间接方法符号(new ClassName )可以避免.请改用常规方法调用(ClassName->new).一方面,构造函数一种方法

The indirect method notation (new ClassName) is great to avoid. Use a normal method call instead (ClassName->new). For one thing, the constructor is a method

程序的参数位于@ARGV中,而不位于@_

A program's arguments are in @ARGV, not in @_

上面的课程还需要很多,但这将带我们到其他地方

The class above needs a whole lot more but that would take us elsewhere

我建议使用模块来运行外部命令,而不是反引号(qx).尝试以下方法之一:Capture::TinyIPC::System::SimpleIPC::Run3IPC::Run

I recommend using a module to run external commands, not backticks (qx). Try some of: Capture::Tiny, IPC::System::Simple, IPC::Run3, IPC::Run

一个例子

在您的类中添加一个实现所需的反序列化的方法.它可以简单地使用所有属性及其值创建一个哈希,然后对其进行序列化-例如,从中生成一个JSON字符串. (如果某些属性的值是其他类的对象,则您需要做更多的工作.)

Add a method to your class that implements the needed de/serialization. It may simply create a hash with all attributes and their values, and then serialize it -- for example generate a JSON string out of it. (If the values of some attributes are objects of yet other classes then you'd have to do more work.)

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

Then the v5.6.pl program can do (using IPC::System::Simple module here)

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

然后v.5.24.pl程序可以完成

my ($init_json) = @ARGV;

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

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

So now the target program has an object built with needed data and can get to work.

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

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

We use JSON to serialize the hash of attributes and their values; that is done in the serialize method. Then such a JSON string can be used to construct a new object: The constructor checks whether it got a hashref or a string, and for a string it calls a method (init_from_json) which initializes the object.

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 a JSON string
        $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重建对象并对其进行处理.

Now the v5.6.pl program can create its object and serialize it, then invoke the v5.30.pl program with that JSON string passed to it as input. The v5.30.pl program can then rebuild the object from JSON and do its work with it.

根据具体情况,还有许多种其他方式.

There are many other ways to do this, depending on specifics.

如果要使用面向对象的代码框架(如MooseMoo),那么可以使用现成的工具和技术来提供帮助. (但是,如果您还没有使用这些框架,那么当然还有一条学习曲线.)

If you were to use a framework for OO code, like Moose or Moo, then there are ready tools and techniques which would help. (But then there'd also a learning curve of course, if you haven't used these frameworks.)

按要求提供完整的工作程序(最大程度地简化了调试程序)

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;

这篇关于在不同Perl版本下运行的程序之间传递对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆