เพิ่มข้อความที่กำหนดเองในการยืนยัน?


129

มีวิธีเพิ่มหรือแก้ไขข้อความที่ส่งโดยการยืนยันหรือไม่? ฉันต้องการใช้สิ่งที่ต้องการ

assert(a == b, "A must be equal to B");

แล้วคอมไพเลอร์เพิ่มบรรทัด , เวลาและอื่น ๆ ...

เป็นไปได้ไหม?


คุณสามารถกำหนดแมโครเช่นนี้
HelloGoodbye

คำตอบ:


240

การแฮ็กที่ฉันเคยเห็นคือการใช้ตัว&&ดำเนินการ เนื่องจากตัวชี้ "เป็นจริง" หากไม่ใช่ค่าว่างคุณสามารถทำสิ่งต่อไปนี้ได้โดยไม่ต้องแก้ไขเงื่อนไข:

assert(a == b && "A is not equal to B");

เนื่องจากassertแสดงเงื่อนไขที่ล้มเหลวมันจะแสดงข้อความของคุณด้วย หากยังไม่เพียงพอคุณสามารถเขียนmyAssertฟังก์ชันหรือมาโครของคุณเองเพื่อแสดงสิ่งที่คุณต้องการ


27
อีกทางเลือกหนึ่งคือการย้อนกลับตัวถูกดำเนินการและใช้ตัวดำเนินการลูกน้ำ คุณต้องมีวงเล็บพิเศษเพื่อไม่ให้เครื่องหมายจุลภาคเป็นตัวคั่นระหว่างอาร์กิวเมนต์:assert(("A must be equal to B", a == b));
Keith Thompson

3
แม้ว่าจะเป็นการดีที่จะสามารถพิมพ์ค่าของตัวแปรได้ดังใน:assert(a == b && "A (" << A << ") is not equal to B (" << B << ")");
Frank

7
@Frank printfจะส่งคืนค่าที่ไม่ใช่ศูนย์หากมันพิมพ์อะไรออกมาดังนั้นคุณสามารถทำอะไรบางอย่างเช่นassert(a == b && printf("a (%i) is not equal to b (%i)", a, b))แม้ว่าในตอนนั้นคุณควรเขียนกระดาษห่อตัวยืนยันของคุณเอง
zneak

1
รหัสไม่ถูกต้อง! ฉันไม่เข้าใจสิ่งนี้! ถ้า a == b เป็นเท็จนิพจน์และควรเป็นเท็จด้วยดังนั้นจึงไม่ควรประเมินสตริง
ragnarius

1
@TUIlover นั่นไม่ใช่วิธีการทำงานของตัวอักษรสตริง C ค่าคงที่ในการคอมไพล์และการใช้งานในบริบทนี้ได้รับการปรับให้เหมาะสมที่สุด ไม่มีต้นทุนรันไทม์
zneak

45

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

assert(("A must be equal to B", a == b));

(คัดลอกมาจากความคิดเห็นด้านบนเพื่อการมองเห็นที่ดีขึ้น)


3
นี่เป็นแนวทางที่ยอดเยี่ยมโดยมีปัญหาเล็ก ๆ อย่างหนึ่งจะแสดง "คำเตือน: ตัวดำเนินการด้านซ้ายของตัวดำเนินการลูกน้ำไม่มีผล" เมื่อคอมไพล์เป็น g ++ ด้วย `-Wunused-value
v010dya

1
หรือด้วยมาโคร: #ifndef m_assert #define m_assert (expr, msg) assert ((msg, expr))
#endif

การใช้มาโคร wrapper ช่วยให้คุณหลีกเลี่ยงคำเตือน gcc:#define m_assert(expr, msg) assert(( (void)(msg), (expr) ))
Jander

25

นี่คือมาโครยืนยันเวอร์ชันของฉันซึ่งยอมรับข้อความและพิมพ์ทุกอย่างออกมาอย่างชัดเจน:

#include <iostream>

#ifndef NDEBUG
#   define M_Assert(Expr, Msg) \
    __M_Assert(#Expr, Expr, __FILE__, __LINE__, Msg)
#else
#   define M_Assert(Expr, Msg) ;
#endif

void __M_Assert(const char* expr_str, bool expr, const char* file, int line, const char* msg)
{
    if (!expr)
    {
        std::cerr << "Assert failed:\t" << msg << "\n"
            << "Expected:\t" << expr_str << "\n"
            << "Source:\t\t" << file << ", line " << line << "\n";
        abort();
    }
}

ตอนนี้คุณสามารถใช้สิ่งนี้ได้

M_Assert(ptr != nullptr, "MyFunction: requires non-null argument");

และในกรณีที่ล้มเหลวคุณจะได้รับข้อความดังนี้:

ยืนยันล้มเหลว: MyFunction: ต้องการอาร์กิวเมนต์ที่ไม่ใช่ค่าว่าง

คาดว่า: ptr! = nullptr

ที่มา: C: \ MyProject \ src.cpp บรรทัด 22

สวยและสะอาดอย่าลังเลที่จะใช้ในรหัสของคุณ =)


ทำได้ดีนี่. มีประโยชน์มาก
Killrazor

ฉันสับสนเล็กน้อย #Expr ถือเป็นสตริงสำหรับการแทนที่โดยตรงหรือไม่ อะไรคือความแตกต่างระหว่าง #Expr และนิพจน์?
Minh Tran

@MinhTran x == yสมมติว่าสภาพยืนยันของคุณคือ จากนั้นนิพจน์จะขยายเข้าไปif( !(x == y))และนี่คือที่ตรวจสอบเงื่อนไขและ #Expr จะขยายเป็นสตริงลิเทอรั"x == y"ลซึ่งเราจะใส่ลงในข้อความแสดงข้อผิดพลาด
Eugene Magdalits

น่าเสียดายที่โซลูชันนี้ทำให้เกิดพฤติกรรมที่ไม่ได้กำหนดเนื่องจากใช้ตัวระบุที่สงวนไว้
จำ Monica

19
BOOST_ASSERT_MSG(expre, msg)

http://www.boost.org/doc/libs/1_51_0/libs/utility/assert.html

คุณสามารถใช้สิ่งนั้นโดยตรงหรือคัดลอกโค้ดของ Boost โปรดทราบว่า Boost assert เป็นส่วนหัวเท่านั้นดังนั้นคุณสามารถคว้าไฟล์เดียวนั้นได้หากคุณไม่ต้องการติดตั้ง Boost ทั้งหมด


@Jichao คุณใช้อินเทอร์เฟซยืนยันหมายความว่าอย่างไร
Tarc

7

เนื่องจากคำตอบของ zneak ทำให้โค้ดค่อนข้างซับซ้อนวิธีการที่ดีกว่าคือการแสดงความคิดเห็นเพียงข้อความสตริงที่คุณกำลังพูดถึง เช่น .:

assert(a == b); // A must be equal to B

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

เพราะในตอนท้ายของวันนี้:

assert(number_of_frames != 0); // Has frames to update

อ่านได้ดีกว่านี้:

assert(number_of_frames != 0 && "Has frames to update");

ในแง่ของการแยกวิเคราะห์รหัสของมนุษย์เช่น การอ่าน ยังไม่แฮ็คภาษา


1
"เนื่องจากผู้อ่านข้อผิดพลาดยืนยันจะค้นหาไฟล์และบรรทัดต่อไปจากข้อความแสดงข้อผิดพลาด" - เฉพาะในกรณีที่พวกเขาขยัน
Jason S

เฉพาะในกรณีที่พวกเขาต้องการแก้ไขข้อบกพร่องที่คุณหมายถึง ... ความคิดเห็นโง่ ๆ
เปลี่ยนแปลง

1
ไม่ยิ่งคุณทำให้คนอื่นเห็นปัญหาได้ง่ายเท่าไหร่พวกเขาก็จะมีโอกาสดำเนินการมากขึ้นเท่านั้น
Jason S

ยักไหล่ไม่เห็นด้วย
เปลี่ยนแปลง

2

assert เป็นการรวมมาโคร / ฟังก์ชัน คุณสามารถกำหนดแมโครของคุณเอง / ฟังก์ชั่นการใช้__FILE__, __BASE_FILE__, __LINE__ฯลฯ ที่มีฟังก์ชั่นของคุณเองที่จะใช้ข้อความที่กำหนดเอง


-6

สำหรับ vc ให้เพิ่มรหัสต่อไปนี้ใน assert.h,

#define assert2(_Expression, _Msg) (void)( (!!(_Expression)) || (_wassert(_CRT_WIDE(#_Msg), _CRT_WIDE(__FILE__), __LINE__), 0) )

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