كيف يمكنني الالتفاف على مكالمة "الموت" في مكتبة بيرل لا يمكنني تعديلها؟

StackOverflow https://stackoverflow.com/questions/451227

  •  19-08-2019
  •  | 
  •  

سؤال

نعم ، المشكلة هي مع مكتبة أستخدمها ، ولا ، لا يمكنني تعديلها. أحتاج إلى حل بديل.

في الأساس ، أنا أتعامل مع مكتبة بيرل مكتوبة بشكل سيء ، والتي تخرج مع "الموت" عند مواجهة حالة خطأ معينة في قراءة ملف. أسمي هذا الروتين من برنامج يحلق من خلال الآلاف من الملفات ، حفنة منها سيئة. ملفات سيئة تحدث ؛ أريد فقط أن يقوم روتيني بتسجيل خطأ ويتحرك.

إذا كان بإمكاني تعديل المكتبة ، فسأقوم ببساطة بتغيير

die "error";

إلى

print "error";return;

, ، لكن انا لا استطيع. هل هناك أي طريقة يمكنني من خلالها أريكة الروتين حتى لا تصطدم الملفات السيئة بالعملية بأكملها؟

سؤال المتابعة: استخدام "eval" لأحواض المكالمة المعرضة للتصادم يعمل بشكل جيد ، ولكن كيف يمكنني إعداد معالجة الأخطاء القابلة للقبض في هذا الإطار؟ لوصف:

لدي روتين فرعي يطلق على المكتبة-التي تُعزى في بعض الأحيان. بدلاً من الأريكة ، كل مكالمة داخل هذه الروتين الفرعي مع eval {} ، أسمح لها فقط بالموت ، واستخدام eval {} على المستوى الذي يستدعي روتين الفرعي الخاص بي:

my $status=eval{function($param);};
unless($status){print $@; next;}; # print error and go to next file if function() fails

ومع ذلك ، هناك شروط خطأ يمكنني القيام بها في الوظيفة (). ما هي الطريقة الأكثر ملاءمة/أناقة لتصميم اللقاح للأخطاء في الروتين الفرعي وروتين الاتصال بحيث أحصل على السلوك الصحيح لكل من الأخطاء التي تم صيدها وغير الملتوية؟

هل كانت مفيدة؟

المحلول

يمكنك لفه في eval. نرى:

perldoc -f eval

على سبيل المثال ، يمكنك الكتابة:

# warn if routine calls die
eval { routine_might_die }; warn $@ if $@;

سيؤدي ذلك إلى تحويل الخطأ المميت إلى تحذير ، وهو ما اقترحته إلى حد ما. إذا die يسمى، $@ يحتوي على السلسلة التي مرت عليها.

نصائح أخرى

هل هو فخ $SIG{__DIE__}؟ إذا كان الأمر كذلك ، فهو محلي أكثر مما أنت عليه. ولكن هناك استراتيجيات زوجين:

  • يمكنك استحضار الحزمة و تجاوز موت:

    package Library::Dumb::Dyer;
    use subs 'die';
    sub die {
        my ( $package, $file, $line ) = caller();
        unless ( $decider->decide( $file, $package, $line ) eq 'DUMB' ) {
            say "It's a good death.";
            die @_;
       }
    } 
    
  • إذا لم يكن كذلك ، يمكن فخ هو - هي. (ابحث عن $ sig على الصفحة ، لا يتعامل Markdown مع الرابط الكامل.)

    my $old_die_handler = $SIG{__DIE__};
    sub _death_handler { 
        my ( $package, $file, $line ) = caller();
        unless ( $decider->decide( $file, $package, $line ) eq 'DUMB DIE' ) {
            say "It's a good death.";
            goto &$old_die_handler;
        }
    }
    $SIG{__DIE__} = \&_death_handler;
    
  • قد تضطر إلى مسح المكتبة ، والعثور على فرع دائماً المكالمات ، واستخدم ذلك لتحميل الخاص بك $SIG معالج عن طريق تجاوز that.

    my $dumb_package_do_something_dumb = \&Dumb::do_something_dumb;
    *Dumb::do_something_dumb = sub { 
        $SIG{__DIE__} = ...
        goto &$dumb_package_do_something_dumb;
    };
    
  • أو تجاوز مصممة دائماً المكالمات...

    package Dumb; 
    use subs 'chdir';
    sub chdir { 
        $SIG{__DIE__} = ...
        CORE::chdir @_;
    };
    
  • إذا فشل كل شيء آخر ، فيمكنك ضرب عيون الحصان بهذا:

    package CORE::GLOBAL;
    use subs 'die';
    
    sub die { 
        ... 
        CORE::die @_;
    }
    

هذا سوف يتجاوز موت على الصعيد العالمي ، الطريقة الوحيدة التي يمكنك بها العودة die هو معالجته كـ CORE::die.

بعض مزيج من هذا سوف يعمل.

على الرغم من تغيير أ die لعدم الموت له حل محدد كما هو موضح في الإجابات الأخرى ، بشكل عام يمكنك دائمًا تجاوز الروتين الفرعي في حزم أخرى. أنت لا تغير المصدر الأصلي على الإطلاق.

أولاً ، قم بتحميل الحزمة الأصلية حتى تحصل على جميع التعريفات الأصلية. بمجرد أن يكون الأصل في مكانه ، يمكنك إعادة تعريف الروتين الفرعي المزعج:

 BEGIN {
      use Original::Lib;

      no warnings 'redefine';

      sub Original::Lib::some_sub { ... }
      }

يمكنك حتى قطع ولصق التعريف الأصلي وتعديل ما تحتاجه. إنه ليس حلاً رائعًا ، ولكن إذا لم تتمكن من تغيير المصدر الأصلي (أو ترغب في تجربة شيء ما قبل تغيير الأصل) ، فيمكنه العمل.

إلى جانب ذلك ، يمكنك نسخ الملف المصدر الأصلي إلى دليل منفصل للتطبيق الخاص بك. نظرًا لأنك تتحكم في هذا الدليل ، يمكنك تحرير الملفات فيه. يمكنك تعديل تلك النسخ وتحميلها عن طريق إضافة هذا الدليل إلى مسار البحث عن وحدة Perl:

use lib qw(/that/new/directory);
use Original::Lib;  # should find the one in /that/new/directory

تتمسك نسختك حتى لو قام شخص ما بتحديث الوحدة الأصلية (على الرغم من أنك قد تضطر إلى دمج التغييرات).

أنا أتحدث عن هذا قليلا في إتقان بيرل, ، حيث أعرض بعض التقنيات الأخرى للقيام بهذا النوع من الأشياء. الحيلة هي عدم كسر الأشياء أكثر. كيف لا تكسر الأشياء تعتمد على ما تفعله.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top