Передача объекта между программами, работающими под разными версиями perl - PullRequest
0 голосов
/ 10 апреля 2020

Возникла проблема при передаче объекта в качестве входного параметра с различными perl версиями от perl5.6.pl до perl5.24.pl (невозможно получить возвращаемое значение из функции "$ from_5. 24" ). Ниже приведен код , в котором возникла проблема. Использование платформы windows, как решить эту проблему.

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**

Я пытался используя сериализованный тестовый код (SharedBetweenPerls.pm), заданный zdim . У меня следующая неправильная ошибка.

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'};

1 Ответ

4 голосов
/ 10 апреля 2020

Примечание Ниже приведены два подхода сериализации - использование 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;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...