ฟังก์ชั่นหรือมาโครสามารถระบุคำเตือนของคอมไพเลอร์ไบต์ได้หรือไม่?


15

ฉันกำลังเขียนฟังก์ชั่นซึ่งโดยหลักการแล้วจะมีจำนวนของข้อโต้แย้งโดยพลการ อย่างไรก็ตามในทางปฏิบัติก็ควรเท่านั้นที่เคยได้รับการผ่าน แม้จำนวนของการขัดแย้งและจะก่อให้เกิดผลที่ไม่พึงประสงค์อย่างอื่น

นี่คือตัวอย่างจำลองสำหรับบริบท:

(defun my-caller (&rest args)
  (while args
    (call-other-function (pop args) (pop args))))

เมื่อไฟล์ elisp ถูกคอมไพล์ด้วยไบต์คอมไพเลอร์ไบต์จะส่งคำเตือนเมื่อเห็นฟังก์ชันที่ถูกเรียกใช้ด้วยจำนวนอาร์กิวเมนต์ที่ไม่ถูกต้อง เห็นได้ชัดว่ามันจะไม่มีทางเกิดขึ้นmy-callerเพราะมันถูกกำหนดให้ใช้หมายเลขใด ๆ

ถึงกระนั้นก็อาจจะมีคุณสมบัติสัญลักษณ์ที่ฉันสามารถตั้งค่าหรือ(declare)รูปแบบที่ฉันสามารถเพิ่มความหมายของมัน สิ่งที่ต้องแจ้งให้ผู้ใช้ทราบว่าฟังก์ชั่นนี้ควรได้รับจำนวนอาร์กิวเมนต์ที่เท่ากัน

  1. มีวิธีแจ้ง byte-compiler ของข้อ จำกัด นี้หรือไม่?
  2. ถ้าไม่ใช่มันเป็นไปได้ไหมที่มีแมโครแทนที่จะใช้ฟังก์ชั่น

"... เมื่อเห็นว่ามีการเรียกใช้ฟังก์ชันด้วยจำนวนอาร์กิวเมนต์ที่ไม่ถูกต้อง "
itsjeyd

คำตอบ:


13

แก้ไข : วิธีที่ดีกว่าการทำเช่นนี้ใน Emacs ล่าสุดคือการกำหนดแมโครคอมไพเลอร์เพื่อตรวจสอบจำนวนข้อโต้แย้ง คำตอบเดิมของฉันที่ใช้แมโครปกติจะถูกเก็บไว้ด้านล่าง แต่คอมไพเลอร์ - แมโครนั้นยอดเยี่ยมเพราะมันไม่ได้ป้องกันการส่งผ่านฟังก์ชันไปยังfuncallหรือapplyที่รันไทม์

ในเวอร์ชันล่าสุดของ Emacs คุณสามารถทำได้โดยการกำหนดคอมไพเลอร์มาโครสำหรับฟังก์ชันของคุณซึ่งตรวจสอบจำนวนอาร์กิวเมนต์และสร้างคำเตือน (หรือแม้แต่ข้อผิดพลาด) หากมันไม่ตรงกัน ความละเอียดเพียงอย่างเดียวคือแมโครคอมไพเลอร์ควรส่งคืนฟอร์มการเรียกใช้ฟังก์ชันดั้งเดิมโดยไม่มีการเปลี่ยนแปลงสำหรับการประเมินหรือการรวบรวม สิ่งนี้ทำได้โดยใช้&wholeอาร์กิวเมนต์และคืนค่าของมัน สามารถทำได้เช่นนี้:

(require 'cl-lib)

(defun my-caller (&rest args)
  (while args
    (message "%S %S" (pop args) (pop args))))

(define-compiler-macro my-caller (&whole form &rest args)
  (when (not (cl-evenp (length args)))
    (byte-compile-warn "`my-caller' requires an even number of arguments"))
  form)

(my-caller 1 2 3 4)
(my-caller 1 2)
(funcall #'my-caller 1 2 3 4)       ; ok
(apply #'my-caller '(1 2))          ; also ok
(my-caller 1)                       ; produces a warning
(funcall #'my-caller 1 2 3)         ; no warning!
(apply #'my-caller '(1 2 3))        ; also no warning

โปรดทราบว่าfuncallและapplyสามารถใช้งานได้ แต่จะข้ามการตรวจสอบอาร์กิวเมนต์โดยแมโครคอมไพเลอร์ แม้จะมีชื่อของพวกเขาแมโครคอมไพเลอร์ก็ดูเหมือนว่าจะได้รับการขยายตัวในหลักสูตรของ 'ตีความ' การประเมินผลผ่านทางC-xC-e, M-xeval-bufferดังนั้นคุณจะได้รับข้อผิดพลาดในการประเมินผลเช่นเดียวกับการรวบรวมตัวอย่างนี้


คำตอบเดิมมีดังนี้:

นี่คือวิธีที่คุณสามารถใช้คำแนะนำของ Jordon เพื่อ "ใช้มาโครที่จะให้คำเตือนเมื่อถึงเวลาการขยาย" กลายเป็นเรื่องง่ายมาก:

(require 'cl-lib)

(defmacro my-caller (&rest args)
  (if (cl-evenp (length args))
      `(my-caller--function ,@args)
    (error "Function `my-caller' requires an even number of arguments")))

(defun my-caller--function (&rest args)
  ;; function body goes here
  args)

(my-caller 1 2 3 4)
(my-caller 1 2 3)

การพยายามรวบรวมข้างต้นในไฟล์จะล้มเหลว (ไม่มีการสร้าง.elcไฟล์) โดยมีข้อความแสดงข้อผิดพลาดที่สามารถคลิกได้ดีในบันทึกการคอมไพล์โดยระบุ:

test.el:14:1:Error: `my-caller' requires an even number of arguments

คุณสามารถแทนที่(error …)ด้วย(byte-compile-warn …)เพื่อสร้างคำเตือนแทนข้อผิดพลาดช่วยให้การรวบรวมเพื่อดำเนินการต่อ (ขอบคุณ Jordon ที่ชี้เรื่องนี้ในความคิดเห็น)

เนื่องจากแมโครถูกขยายในเวลารวบรวมจึงไม่มีบทลงโทษที่เกี่ยวข้องกับการตรวจสอบนี้ แน่นอนคุณไม่สามารถหยุดคนอื่นโทรmy-caller--functionโดยตรง แต่อย่างน้อยคุณสามารถโฆษณามันเป็นฟังก์ชั่น "ส่วนตัว" โดยใช้การประชุมยัติภังค์คู่

ข้อเสียที่โดดเด่นของการใช้แมโครเพื่อจุดประสงค์นี้my-callerคือไม่มีฟังก์ชั่นชั้นหนึ่งอีกต่อไป: คุณไม่สามารถส่งผ่านไปยังfuncallหรือapplyที่รันไทม์ (หรืออย่างน้อยก็จะไม่ทำสิ่งที่คุณคาดหวัง) ในแง่ที่ว่าวิธีนี้ไม่ดีเท่าที่จะสามารถประกาศเตือนคอมไพเลอร์สำหรับฟังก์ชั่นจริง แน่นอนว่าการใช้applyจะทำให้ไม่สามารถตรวจสอบจำนวนข้อโต้แย้งที่ส่งผ่านไปยังฟังก์ชันในเวลารวบรวมได้ดังนั้นบางทีนี่อาจเป็นการแลกเปลี่ยนที่ยอมรับได้


2
คำเตือนการรวบรวมถูกสร้างขึ้นด้วยbyte-compile-warn
Jordon Biondo

ตอนนี้ฉันสงสัยว่าสิ่งนี้สามารถทำได้อย่างมีประสิทธิภาพมากขึ้นโดยการกำหนดแมโครคอมไพเลอร์สำหรับฟังก์ชั่น สิ่งนี้จะกำจัดข้อเสียของการไม่ไปapplyหรือfuncallwrapper แมโคร ฉันจะลองและแก้ไขคำตอบถ้าใช้งานได้
Jon O.

11

ใช่คุณสามารถใช้byte-defop-compilerเพื่อระบุฟังก์ชั่นที่รวบรวมฟังก์ชั่นของคุณbyte-defop-compilerได้จริง ๆ มีบิวท์อินบางตัวเพื่อช่วยให้คุณระบุว่าฟังก์ชั่นของคุณควรให้คำเตือนตามจำนวน args

เอกสาร

เพิ่มรูปแบบคอมไพเลอร์สำหรับ FUNCTION หากฟังก์ชันเป็นสัญลักษณ์ตัวแปร "byte-SYMBOL" จะต้องตั้งชื่อ opcode ที่จะใช้ หากฟังก์ชั่นเป็นรายการองค์ประกอบแรกคือฟังก์ชั่นและองค์ประกอบที่สองคือสัญลักษณ์ bytecode องค์ประกอบที่สองอาจเป็นศูนย์ซึ่งหมายความว่าไม่มี opcode COMPILE-HANDLER เป็นฟังก์ชั่นที่ใช้ในการรวบรวม byte-op นี้หรืออาจเป็นตัวย่อ 0, 1, 2, 3, 0-1 หรือ 1-2 หากเป็นศูนย์ตัวจัดการจะเป็น "byte-compile-SYMBOL


การใช้

ในกรณีเฉพาะของคุณคุณสามารถใช้หนึ่งในตัวย่อเพื่อกำหนดว่าฟังก์ชั่นของคุณควรได้รับสอง args

(byte-defop-compiler my-caller 2)

ตอนนี้คุณกำลังใช้งานฟังก์ชั่นเตือนเมื่อคอมไพล์ด้วยอะไรนอกจาก 2 args

หากคุณต้องการให้คำเตือนที่เฉพาะเจาะจงมากขึ้นและเขียนฟังก์ชั่นคอมไพเลอร์ของคุณเอง ดูbyte-compile-one-argและฟังก์ชั่นอื่น ๆ ที่คล้ายกันใน bytecomp.el สำหรับการอ้างอิง

โปรดทราบว่าคุณไม่ได้เพียงแค่ระบุฟังก์ชั่นบางอย่างเพื่อจัดการกับการตรวจสอบความถูกต้อง รวบรวมฟังก์ชั่นอีกครั้งใน bytecomp.el จะช่วยให้คุณมีการอ้างอิงที่ดี


เส้นทางที่ปลอดภัยยิ่งขึ้น

นี่ไม่ใช่สิ่งที่ฉันได้เห็นเอกสารหรือพูดคุยออนไลน์ แต่โดยรวมแล้วฉันจะบอกว่านี่เป็นเส้นทางที่ไม่ควรใช้ เส้นทางที่ถูกต้อง (IMO) คือการเขียน defuns ของคุณด้วยลายเซ็นที่เป็นคำอธิบายหรือใช้แมโครที่จะให้คำเตือน ณ เวลาที่ขยายการตรวจสอบความยาวของ args ของคุณและการใช้byte-compile-warnหรือerrorเพื่อแสดงข้อผิดพลาด นอกจากนี้ยังอาจเป็นประโยชน์สำหรับคุณในeval-when-compileการตรวจสอบข้อผิดพลาด

นอกจากนี้คุณยังจะต้องกำหนดฟังก์ชันของคุณก่อนที่จะใช้งานและการเรียกbyte-defop-compilerจะต้องเป็นก่อนที่คอมไพเลอร์จะได้รับการเรียกใช้ฟังก์ชันของคุณ

อีกครั้งดูเหมือนจะไม่ได้รับการบันทึกหรือแนะนำจากสิ่งที่ฉันเห็น (อาจผิด) แต่ฉันคิดว่ารูปแบบการติดตามที่นี่จะเป็นการระบุไฟล์ส่วนหัวสำหรับแพ็คเกจของคุณที่เต็มไปด้วย defuns ที่ว่างเปล่า byte-defop-compilerและโทรไปยัง โดยทั่วไปจะเป็นแพคเกจที่ต้องการก่อนที่แพคเกจจริงของคุณจะสามารถรวบรวมได้

ความเห็น: จากสิ่งที่ฉันรู้ซึ่งไม่มากเพราะฉันเพิ่งเรียนรู้เกี่ยวกับสิ่งนี้ทั้งหมดฉันจะแนะนำให้คุณไม่ทำสิ่งนี้ เคย


1
ที่เกี่ยวข้อง: มีbytecomp-simplifyซึ่งสอนคำเตือนเพิ่มเติม byte-compiler
Wilfred Hughes
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.