Objective-C ตัวประมวลผลล่วงหน้า

Objective-C Preprocessorไม่ได้เป็นส่วนหนึ่งของคอมไพเลอร์ แต่เป็นขั้นตอนแยกต่างหากในกระบวนการคอมไพล์ ในแง่ที่เข้าใจง่าย Objective-C Preprocessor เป็นเพียงเครื่องมือทดแทนข้อความและสั่งให้คอมไพเลอร์ทำการประมวลผลล่วงหน้าที่จำเป็นก่อนการคอมไพล์จริง เราจะอ้างถึง Objective-C Preprocessor ว่า OCPP

คำสั่งพรีโปรเซสเซอร์ทั้งหมดเริ่มต้นด้วยสัญลักษณ์ปอนด์ (#) ต้องเป็นอักขระ nonblank ตัวแรกและเพื่อความสามารถในการอ่านคำสั่งพรีโปรเซสเซอร์ควรเริ่มต้นในคอลัมน์แรก ส่วนต่อไปนี้แสดงรายการคำสั่งก่อนตัวประมวลผลที่สำคัญทั้งหมด -

ซีเนียร์ คำสั่งและคำอธิบาย
1

#define

แทนที่มาโครตัวประมวลผลล่วงหน้า

2

#include

แทรกส่วนหัวเฉพาะจากไฟล์อื่น

3

#undef

ไม่ได้กำหนดมาโครตัวประมวลผลล่วงหน้า

4

#ifdef

ส่งคืนค่าจริงหากกำหนดมาโครนี้

5

#ifndef

ส่งคืนค่าจริงหากไม่ได้กำหนดมาโครนี้

6

#if

ทดสอบว่าเงื่อนไขเวลาคอมไพล์เป็นจริงหรือไม่

7

#else

ทางเลือกสำหรับ #if

8

#elif

#else an #if ในหนึ่งคำสั่ง

9

#endif

สิ้นสุดเงื่อนไขก่อนตัวประมวลผล

10

#error

พิมพ์ข้อความแสดงข้อผิดพลาดบน stderr

11

#pragma

ออกคำสั่งพิเศษไปยังคอมไพลเลอร์โดยใช้วิธีการมาตรฐาน

ตัวอย่างตัวประมวลผลล่วงหน้า

วิเคราะห์ตัวอย่างต่อไปนี้เพื่อทำความเข้าใจคำสั่งต่างๆ

#define MAX_ARRAY_LENGTH 20

คำสั่งนี้บอกให้ OCPP แทนที่อินสแตนซ์ของ MAX_ARRAY_LENGTH ด้วย 20 ใช้#defineสำหรับค่าคงที่เพื่อเพิ่มความสามารถในการอ่าน

#import <Foundation/Foundation.h>
#include "myheader.h"

คำสั่งเหล่านี้บอกให้ OCPP ได้รับรากฐาน h จาก Foundation Frameworkและเพิ่มข้อความลงในไฟล์ต้นฉบับปัจจุบัน บรรทัดถัดไปบอกให้รับ OCPPmyheader.h จากโลคัลไดเร็กทอรีและเพิ่มเนื้อหาลงในซอร์สไฟล์ปัจจุบัน

#undef  FILE_SIZE
#define FILE_SIZE 42

สิ่งนี้บอกให้ OCPP ยกเลิกการกำหนด FILE_SIZE ที่มีอยู่และกำหนดเป็น 42

#ifndef MESSAGE
   #define MESSAGE "You wish!"
#endif

สิ่งนี้จะบอกให้ OCPP กำหนด MESSAGE ก็ต่อเมื่อยังไม่ได้กำหนด MESSAGE ไว้

#ifdef DEBUG
   /* Your debugging statements here */
#endif

สิ่งนี้บอกให้ OCPP ดำเนินการตามคำสั่งที่แนบมาหากมีการกำหนด DEBUG สิ่งนี้มีประโยชน์หากคุณส่งแฟล็ก -DDEBUGไปยังคอมไพเลอร์ gcc ในขณะคอมไพล์ สิ่งนี้จะกำหนด DEBUG เพื่อให้คุณสามารถเปิดและปิดการดีบักได้ทันทีในระหว่างการคอมไพล์

มาโครที่กำหนดไว้ล่วงหน้า

ANSI C กำหนดมาโครจำนวนหนึ่ง แม้ว่าแต่ละมาโครจะพร้อมใช้งานสำหรับการเขียนโปรแกรมของคุณ แต่ก็ไม่ควรแก้ไขมาโครที่กำหนดไว้ล่วงหน้าโดยตรง

ซีเนียร์ มาโครและคำอธิบาย
1

__DATE__

วันที่ปัจจุบันเป็นอักขระลิเทอรัลในรูปแบบ "MMM DD YYYY"

2

__TIME__

เวลาปัจจุบันเป็นอักขระลิเทอรัลในรูปแบบ "HH: MM: SS"

3

__FILE__

ซึ่งมีชื่อไฟล์ปัจจุบันเป็นสตริงลิเทอรัล

4

__LINE__

ซึ่งประกอบด้วยหมายเลขบรรทัดปัจจุบันเป็นค่าคงที่ทศนิยม

5

__STDC__

กำหนดเป็น 1 เมื่อคอมไพเลอร์เป็นไปตามมาตรฐาน ANSI

ลองดูตัวอย่างต่อไปนี้ -

#import <Foundation/Foundation.h>

int main() {
   NSLog(@"File :%s\n", __FILE__ );
   NSLog(@"Date :%s\n", __DATE__ );
   NSLog(@"Time :%s\n", __TIME__ );
   NSLog(@"Line :%d\n", __LINE__ );
   NSLog(@"ANSI :%d\n", __STDC__ );
   
   return 0;
}

เมื่อรหัสดังกล่าวอยู่ในไฟล์ main.m ถูกรวบรวมและดำเนินการจะให้ผลลัพธ์ดังต่อไปนี้ -

2013-09-14 04:46:14.859 demo[20683] File :main.m
2013-09-14 04:46:14.859 demo[20683] Date :Sep 14 2013
2013-09-14 04:46:14.859 demo[20683] Time :04:46:14
2013-09-14 04:46:14.859 demo[20683] Line :8
2013-09-14 04:46:14.859 demo[20683] ANSI :1

ตัวดำเนินการก่อนโปรเซสเซอร์

ตัวประมวลผลล่วงหน้า Objective-C มีตัวดำเนินการต่อไปนี้เพื่อช่วยคุณในการสร้างมาโคร -

ความต่อเนื่องของมาโคร (\)

โดยปกติมาโครจะต้องอยู่ในบรรทัดเดียว ตัวดำเนินการต่อเนื่องแมโครใช้เพื่อดำเนินการต่อมาโครที่ยาวเกินไปสำหรับบรรทัดเดียว ตัวอย่างเช่น -

#define  message_for(a, b)  \
   NSLog(@#a " and " #b ": We love you!\n")

สตริง (#)

ตัวดำเนินการ stringize หรือ number-sign ('#') เมื่อใช้ภายในนิยามมาโครจะแปลงพารามิเตอร์มาโครเป็นค่าคงที่ของสตริง ตัวดำเนินการนี้สามารถใช้ได้เฉพาะในมาโครที่มีอาร์กิวเมนต์หรือรายการพารามิเตอร์ที่ระบุ ตัวอย่างเช่น -

#import <Foundation/Foundation.h>

#define  message_for(a, b)  \
   NSLog(@#a " and " #b ": We love you!\n")

int main(void) {
   message_for(Carole, Debra);
   return 0;
}

เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -

2013-09-14 05:46:14.859 demo[20683] Carole and Debra: We love you!

การวางโทเค็น (##)

ตัวดำเนินการวางโทเค็น (##) ภายในนิยามมาโครรวมสองอาร์กิวเมนต์ อนุญาตให้โทเค็นสองโทเค็นที่แยกจากกันในนิยามมาโครรวมเป็นโทเค็นเดียว ตัวอย่างเช่น -

#import <Foundation/Foundation.h>

#define tokenpaster(n) NSLog (@"token" #n " = %d", token##n)

int main(void) {
   int token34 = 40;
   
   tokenpaster(34);
   return 0;
}

เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -

2013-09-14 05:48:14.859 demo[20683] token34 = 40

มันเกิดขึ้นได้อย่างไรเนื่องจากตัวอย่างนี้ส่งผลให้เกิดผลลัพธ์จริงต่อไปนี้จากตัวประมวลผลล่วงหน้า -

NSLog (@"token34 = %d", token34);

ตัวอย่างนี้แสดงการต่อโทเค็น ## n เข้ากับโทเค็น 34 และที่นี่เราได้ใช้ทั้งสองอย่าง stringize และ token-pasting.

ตัวดำเนินการที่กำหนด ()

ตัวประมวลผลล่วงหน้า definedตัวดำเนินการถูกใช้ในนิพจน์ค่าคงที่เพื่อพิจารณาว่าตัวระบุถูกกำหนดโดยใช้ #define หรือไม่ หากมีการกำหนดตัวระบุที่ระบุไว้ค่าจะเป็นจริง (ไม่ใช่ศูนย์) หากไม่ได้กำหนดสัญลักษณ์ไว้ค่าจะเป็นเท็จ (ศูนย์) ระบุตัวดำเนินการดังต่อไปนี้ -

#import <Foundation/Foundation.h>

#if !defined (MESSAGE)
   #define MESSAGE "You wish!"
#endif

int main(void) {
   NSLog(@"Here is the message: %s\n", MESSAGE);  
   return 0;
}

เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -

2013-09-14 05:48:19.859 demo[20683] Here is the message: You wish!

มาโครที่กำหนดพารามิเตอร์

หนึ่งในฟังก์ชันที่มีประสิทธิภาพของ OCPP คือความสามารถในการจำลองฟังก์ชันโดยใช้มาโครที่กำหนดพารามิเตอร์ ตัวอย่างเช่นเราอาจมีรหัสเพื่อยกกำลังสองจำนวนดังนี้ -

int square(int x) {
   return x * x;
}

เราสามารถเขียนโค้ดด้านบนโดยใช้มาโครได้ดังนี้ -

#define square(x) ((x) * (x))

ต้องกำหนดมาโครที่มีอาร์กิวเมนต์โดยใช้ #defineคำสั่งก่อนที่จะใช้งานได้ รายการอาร์กิวเมนต์อยู่ในวงเล็บและต้องตามด้วยชื่อแมโครทันที ไม่อนุญาตให้ใช้ช่องว่างระหว่างชื่อมาโครและวงเล็บเปิด ตัวอย่างเช่น -

#import <Foundation/Foundation.h>

#define MAX(x,y) ((x) > (y) ? (x) : (y))

int main(void) {
   NSLog(@"Max between 20 and 10 is %d\n", MAX(10, 20));  
   return 0;
}

เมื่อโค้ดด้านบนถูกคอมไพล์และเรียกใช้งานจะให้ผลลัพธ์ดังนี้ -

2013-09-14 05:52:15.859 demo[20683] Max between 20 and 10 is 20