Примечание Ниже приведены два подхода сериализации - использование Storable
(может быть попадание или пропуск при условии, что он должен быть между различными Perl и, следовательно, версиями модуля), и пользовательский one (demo)
Это более общий случай проблемы, поставленной в предыдущем вопросе , где теперь объект должен передаваться между программами. Это изменение приводит к существенной разнице.
Объект должен быть сериализован , чтобы его можно было обойти. Я использую 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;
"main" "Программа, которая должна работать под более старой 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
Файл, в который записан сериализованный объект, должен иметь гораздо лучшее имя, чем то, что я использую в этой демонстрации, и Файл: : Temp является стандартным выбором для обработки временных имен.
Это печатает
Value for 'roleId' in the new object: 17
Return from function: 17
Так что для этого простого игрушечного класса это работает - объект передается правильно.
Однако сериализация ни в коем случае не является тривиальным делом. В зависимости от сложности вашего фактического класса и, в частности, от того, насколько разные версии модулей для двух программ, могут возникнуть проблемы. С вашей комбинацией v5.6 и v5.24 я думаю, что вам нужно держать пальцы скрещенными. (Он работал с моими v5.16 и v5.30, но v5.6 очень, очень старый.)
Комбинация store
+ retrieve
- это (один из способов) передачи сложных данных с использованием файлы. Я также попытался freeze
объект и вручную записать его в файл, а затем прочитать этот файл в и thaw
его, и это тоже сработало. (Передача замороженного объекта прямо по трубе ужасно затруднена.)
Но передача целого объекта может просто не сработать, и если в вашем случае действительно есть проблемы, то что делать, будет зависеть полностью от того, какой у вас класс на самом деле, как.
Одна вещь, которую вы всегда можете сделать, это придумать индивидуальный подход к сериализации, при котором необходимые данные передаются (через файл или соответствующим образом сериализуются для канала), не весь объект. Тогда программа на другом конце может использовать это для создания объекта. †
Комментарии
Когда пакет определяет класс, то нет причина экспорта символов
Не указывайте название пакета жестко; для этого есть __PACKAGE__
. Но с классом имя пакета передается в качестве первого аргумента в конструкторе, и его следует использовать
Не используйте объект в качестве какого-либо старого hashref, где просто разыменовывает ключ, чтобы напечатать значение. Это тыкает во внутренний класс и является действительно очень плохой идеей - используйте предоставленные методы. (Для этого программе v.5.24.pl
необходимо также загрузить пакет с классом)
Если вы хотите, чтобы вызываемая программа могла работать с объектом, она должна загрузить пакет где этот класс определен (поскольку не следует использовать объект в качестве простого хэш-значения)
Непрямая нотация метода (new ClassName
) отлично подходит для избежания , Вместо этого используйте обычный вызов метода (ClassName->new
). Во-первых, конструктор является методом
Аргументы программы находятся в @ARGV
, а не в @_
Классу выше нужно намного больше, но это приведет нас куда-то еще
Я рекомендую использовать модуль для запуска внешних команд, а не обратных символов (qx
). Попробуйте некоторые из: Capture::Tiny
, IPC::System::Simple
, IPC::Run3
, IPC::Run
† Пример
Добавить метод для вашего класса, который реализует необходимую де / сериализацию. Он может просто создать ha sh со всеми атрибутами и их значениями, а затем сериализовать его - например, сгенерировать из него строку JSON. (Если значения некоторых атрибутов являются объектами еще других классов, вам придется проделать дополнительную работу.)
Тогда программа v5.6.pl
может выполнить (используя IP C :: System Модуль Simple здесь
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 );
Так что теперь целевая программа имеет объект, созданный с необходимыми данными, и может приступить к работе.
Вот пример очень базовый c и грубый пример. Обратите внимание, что это может быть плохой выбор для вашего проекта, о котором я ничего не знаю, в то время как если его можно использовать, то он требует больше работы и проверок.
Мы используем JSON сериализовать ха sh атрибутов и их значений; это делается методом serialize
. Затем такую строку JSON можно использовать для создания нового объекта: конструктор проверяет, получил ли он хеш-значение или строку, а для строки он вызывает метод (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 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
может создать свой объект и сериализовать его, а затем вызвать программу v5.30.pl
с этой строкой JSON, переданной ей в качестве входных данных. Затем программа v5.30.pl
может перестроить объект из JSON и выполнить с ним свою работу.
Существует множество других способов сделать это, в зависимости от особенностей.
Если бы вы использовали фреймворк для ОО-кода, такой как Moose
или Moo
, то есть готовые инструменты и методы, которые могут помочь. (Но тогда, конечно, была бы и кривая обучения, если вы не использовали эти фреймворки.)
В соответствии с просьбой, завершите работающие программы (максимально упрощенные для облегчения отладки) -
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;