ใช้ #ifdef เพื่อสลับไปมาระหว่างพฤติกรรมประเภทต่าง ๆ ในระหว่างการพัฒนา


28

เป็นวิธีปฏิบัติที่ดีหรือไม่ที่จะใช้ #ifdef ระหว่างการพัฒนาเพื่อสลับไปมาระหว่างพฤติกรรมประเภทต่าง ๆ ? ตัวอย่างเช่นฉันต้องการเปลี่ยนพฤติกรรมของรหัสที่มีอยู่ฉันมีความคิดหลายอย่างเกี่ยวกับวิธีการเปลี่ยนพฤติกรรมและจำเป็นต้องสลับระหว่างการใช้งานที่แตกต่างกันเพื่อทดสอบและเปรียบเทียบวิธีการที่แตกต่างกัน การเปลี่ยนแปลงรหัสมักจะซับซ้อนและมีผลต่อวิธีการต่าง ๆ ในไฟล์ที่แตกต่างกัน

ฉันมักจะแนะนำตัวระบุหลายตัวและทำอะไรแบบนั้น

void foo()
{
    doSomething1();
#ifdef APPROACH1
    foo_approach1();
#endif
    doSomething2();
#ifdef APPROACH2
    foo_approach2();
#endif
}

void bar()
{
    doSomething3();
#ifndef APPROACH3
    doSomething4();
#endif
    doSomething5();
#ifdef APPROACH2
    bar_approach2();
#endif
}

int main()
{
    foo();
    bar();
    return 0;
}

วิธีนี้ช่วยให้สามารถสลับไปมาระหว่างวิธีการต่าง ๆ ได้อย่างรวดเร็วและทำทุกอย่างในสำเนาของซอร์สโค้ดเดียว มันเป็นแนวทางที่ดีสำหรับการพัฒนาหรือมีแนวปฏิบัติที่ดีกว่านี้หรือไม่?



2
เนื่องจากคุณกำลังพูดถึงการพัฒนาผมเชื่อว่าคุณต้องทำทุกอย่างที่ทำได้ง่าย ๆ เพื่อสลับและทดลองใช้การใช้งานที่แตกต่างกัน นี่เป็นเหมือนความชอบส่วนตัวระหว่างการพัฒนาไม่ใช่วิธีปฏิบัติที่ดีที่สุดในการแก้ปัญหาเฉพาะ
Emerson Cardoso

1
ฉันขอแนะนำให้ใช้รูปแบบกลยุทธ์หรือความหลากหลายที่ดีของ ol เนื่องจากสิ่งนี้ช่วยรักษาจุดปลั๊กอินหนึ่งจุดสำหรับพฤติกรรมที่เปลี่ยนแปลงได้
pmf

4
พึงระลึกไว้ว่า IDEs บางตัวจะไม่ประเมินสิ่งใดเป็น#ifdefบล็อกหากปิดการใช้งานบล็อก เราพบกรณีที่รหัสสามารถค้างได้ง่ายและไม่รวบรวมหากคุณไม่ได้สร้างเส้นทางทั้งหมดเป็นประจำ
Berin Loritsch

ลองดูคำตอบที่ฉันให้ไว้กับคำถามอื่น มันวางแนวทางบางอย่างเพื่อให้#ifdefsยุ่งยากน้อยลงมาก
user1118321

คำตอบ:


9

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


gitมีความเชี่ยวชาญเป็นพิเศษในเรื่องแบบนี้ อาจจะไม่มากกรณีที่มีsvn, hgหรือคนอื่น ๆ แต่ก็ยังสามารถทำได้
twalberg

นั่นคือความคิดเริ่มต้นของฉันเช่นกัน "ต้องการยุ่งกับสิ่งที่แตกต่าง?" git branch!
Wes Toleman

42

เมื่อคุณถือค้อนทุกอย่างดูเหมือนเล็บ มันดึงดูดเมื่อคุณรู้วิธี#ifdefใช้งานมันเป็นวิธีการหนึ่งในการรับพฤติกรรมที่กำหนดเองในโปรแกรมของคุณ ฉันรู้เพราะฉันทำผิดพลาดเหมือนกัน

ฉันสืบทอดโปรแกรมดั้งเดิมที่เขียนใน MFC C ++ ซึ่งใช้#ifdefเพื่อกำหนดค่าเฉพาะแพลตฟอร์มแล้ว นั่นหมายความว่าฉันสามารถรวบรวมโปรแกรมของฉันเพื่อใช้กับแพลตฟอร์ม 32 บิตหรือแพลตฟอร์ม 64 บิตได้ง่ายๆโดยการกำหนด (หรือในบางกรณีไม่ได้กำหนด) ค่าแมโครเฉพาะ

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

ฉันถูกล่อลวงและฉันก็ให้ฉันเขียน#ifdefส่วนต่าง ๆ ในรหัสของฉันเพื่อแยกแยะพฤติกรรมที่แตกต่างกัน อย่าทำผิดพลาดมันไม่มีอะไรจะเหนือกว่าในตอนแรก มีการเปลี่ยนแปลงพฤติกรรมเล็กน้อยมากซึ่งทำให้ฉันสามารถแจกจ่ายโปรแกรมรุ่นใหม่ให้กับลูกค้าของเราและฉันไม่จำเป็นต้องมีรหัสฐานมากกว่าหนึ่งรุ่น

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

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


สิ่งที่เกี่ยวกับรุ่นการแก้ปัญหาเช่น "ถ้าการแก้ปัญหากำหนดตัวแปร x ... " ดูเหมือนว่ามันจะเป็นประโยชน์สำหรับสิ่งต่าง ๆ เช่นการเข้าสู่ระบบ แต่ก็สามารถเปลี่ยนวิธีการทำงานของโปรแกรมทั้งหมดเมื่อเปิดใช้งานการดีบักและเมื่อมันไม่ .
WHN

8
@snb ฉันคิดเกี่ยวกับเรื่องนั้น ฉันยังคงต้องการเปลี่ยนแฟ้มการกำหนดค่าและทำให้บันทึกมีรายละเอียดเพิ่มเติม มิฉะนั้นมีบางอย่างผิดพลาดกับโปรแกรมที่กำลังใช้งานอยู่และคุณไม่มีวิธีแก้ไขข้อผิดพลาดโดยไม่ต้องเปลี่ยนโปรแกรมที่เรียกทำงานได้ทั้งหมด แม้ในสถานการณ์ในอุดมคตินี่ก็น้อยกว่าที่ต้องการ ;)
Neil

โอ้ใช่แล้วมันจะเป็นวิธีที่เหมาะกว่าที่จะไม่ต้องคอมไพล์คอมไพล์เดอร์ฉันไม่ได้คิดอย่างนั้น!
WHN

9
สำหรับตัวอย่างที่ดีที่สุดของสิ่งที่คุณกำลังอธิบายให้ดูที่ย่อหน้าที่ 2 ภายใต้หัวข้อย่อย "ปัญหาการดูแลรักษา" ในบทความนี้ว่าทำไม MS ถึงจุดที่พวกเขาต้องเขียน C runtime ใหม่เกือบทั้งหมดตั้งแต่สองสามปีก่อน . blogs.msdn.microsoft.com/vcblog/2014/06/10/…
Dan Neely

2
@snb ไลบรารีการบันทึกส่วนใหญ่สมมติว่ากลไกการบันทึกระดับ หากคุณต้องการข้อมูลบางอย่างที่บันทึกไว้ในระหว่างการดีบักคุณเข้าสู่ระบบด้วยระดับการบันทึกต่ำ ("Debug" หรือ "Verbose" โดยปกติ) จากนั้นแอปพลิเคชันจะมีพารามิเตอร์การกำหนดค่าที่บอกระดับการบันทึก ดังนั้นคำตอบก็ยังคงตั้งค่าสำหรับปัญหานี้ นอกจากนี้ยังมีประโยชน์มหาศาลของความสามารถในการเปิดระดับการบันทึกต่ำนี้ในสภาพแวดล้อมของลูกค้า
jpmc26

21

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

แต่คำเตือน: อย่าเก็บ #ifdef กิ่งมีความผิดหวังเล็กน้อยกว่าการเสียเวลาของฉันในการอ่านสิ่งเดียวกันใช้สี่วิธีที่แตกต่างกันเพียงเพื่อที่จะคิดว่าฉันควรจะอ่านอะไร

การอ่าน #ifdef นั้นต้องใช้ความพยายามอย่างที่คุณต้องจำไว้ก่อนที่จะข้ามไป! อย่าทำให้ยากกว่าที่เคยเป็นมา

ใช้ #ifdefs เท่าที่จำเป็น โดยทั่วไปมีวิธีที่คุณสามารถทำได้ภายในสภาพแวดล้อมการพัฒนาของคุณเพื่อความแตกต่างถาวรเช่นการสร้าง Debug / Release หรือสถาปัตยกรรมที่แตกต่างกัน

ฉันได้เขียนฟีเจอร์ของไลบรารีที่ขึ้นอยู่กับไลบรารี่ที่รวมอยู่ซึ่งต้องแยก #ifdef ดังนั้นในบางครั้งอาจเป็นวิธีเดียวหรือวิธีที่ง่ายที่สุด แต่ถึงอย่างนั้นคุณก็ต้องเสียใจกับการรักษาพวกเขา


1

การใช้ #ifdefs เช่นนั้นทำให้อ่านรหัสยากมาก

ดังนั้นไม่อย่าใช้ #ifdefs เช่นนั้น

อาจมีข้อโต้แย้งมากมายทำไมไม่ใช้ ifdefs สำหรับฉันอันนี้ก็เพียงพอแล้ว

void foo()
{
    doSomething1();
#ifdef APPROACH1
    foo_approach1();
#endif
    doSomething2();
#ifdef APPROACH2
    foo_approach2();
#endif
}

สามารถทำสิ่งต่างๆมากมายที่ทำได้:

void foo()
{
    doSomething1();
    doSomething2();
}

void foo()
{
    doSomething1();
    foo_approach1();
    doSomething2();
}

void foo()
{
    doSomething1();
    doSomething2();
    foo_approach2();
}

void foo()
{
    doSomething1();
    foo_approach1();
    doSomething2();
    foo_approach2();
}

ทั้งหมดขึ้นอยู่กับวิธีการที่กำหนดไว้หรือไม่ สิ่งที่มันไม่ชัดเจนในการมองครั้งแรก

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