2010-08-13 24 views
5

Tôi đang tìm kiếm một công cụ có thể phát hiện ra lệnh cho cặp chức năng cuộc gọi theo kiểu lồng nhau như hình dưới đây:Làm thế nào để phát hiện đúng chức năng cặp gọi

f() // depth 0 
    f() //depth 1 
    g() 
g() 

Tại mỗi chiều sâu của cuộc gọi f() phải có một tiếng gọi của g() tạo cặp gọi hàm. Điều này đặc biệt quan trọng trong mục nhập cảnh quan trọng và thoát.

+0

Tại sao không gọi 'g' từ 'f' trước khi trở về từ 'f'? – Chubsdad

+0

@chubsdad: Bởi vì có mã ở giữa 'f()' và 'g()', có lẽ - nghĩ 'lock()' và 'unlock()' thay vì 'f()' và 'g()'. – caf

Trả lời

11

Trong C++, một tùy chọn là bao bọc các cuộc gọi đến f()g() trong hàm tạo và hủy của một lớp và chỉ gọi các hàm đó bằng cách khởi tạo một thể hiện của lớp đó. Ví dụ,

struct FAndGCaller 
{ 
    FAndGCaller() { f(); } 
    ~FAndGCaller() { g(); } 
}; 

này sau đó có thể được sử dụng trong bất kỳ khối phạm vi như vậy:

{ 
    FAndGCaller call_f_then_later_g; // calls f() 

} // calls g() 

Rõ ràng trong mã thực bạn muốn đặt tên cho điều thích hợp hơn, và thường bạn sẽ chỉ đơn giản là muốn để có các nội dung của f()g() trong các hàm tạo và các cơ quan hủy, thay vì trong các hàm riêng biệt.

Thành ngữ này về Quản lý tài nguyên phạm vi giới hạn (SBRM, hoặc thường được gọi là Thu nhận tài nguyên là Khởi tạo, RAII) là khá phổ biến.

+0

Nếu f() ném một lỗi, g() sẽ không bao giờ được gọi, nhưng tài nguyên của lớp/struct sẽ được giải phóng. –

+1

@ PC2st: Đúng, nhưng nếu 'g()' làm ngược lại (hoặc "undoes") cái 'f()' làm, thì nếu 'f()' ném một ngoại lệ bạn không muốn gọi 'g() '. –

0

Quét qua mã (đó là phần khó) và mỗi lần bạn thấy yêu cầu f(), tăng bộ đếm. Mỗi khi bạn thấy yêu cầu g(), hãy giảm bộ đếm. Cuối cùng, bộ đếm sẽ trở lại bằng không. Nếu nó đi tiêu cực, đó là một vấn đề là tốt (bạn đã có một cuộc gọi đến g() mà không phải là trước bởi một cuộc gọi phù hợp với f()).

Quét mã chính xác là phần cứng mặc dù - với C và (đặc biệt) C++, viết mã để hiểu mã nguồn là cực kỳ khó. Trái phép, tôi không biết một công cụ hiện có cho công việc cụ thể này. Bạn chắc chắn có thể nhận được clang (cho một ví dụ) để làm điều đó, nhưng trong khi nó sẽ dễ dàng hơn nhiều so với làm nó hoàn toàn trên của riêng bạn, nó vẫn sẽ không được tầm thường.

+0

Vấn đề với cách tiếp cận này là nó không kiểm tra các mức của 'f()' s và 'g()' s - nó sẽ đảm bảo rằng bạn có đúng số tiền, nhưng nó sẽ không đảm bảo rằng bạn có họ phản chiếu nhau trên cùng một cấp độ. Ngoài ra, OP yêu cầu một công cụ để sử dụng, không phải là một cách để xem mã. –

+0

@ adam_0: ít nhất dựa trên mã mà anh ta đã hiển thị, các cấp được * xác định * bởi các lệnh gọi đến 'f()' và 'g()'. Nếu có nhiều cấp độ hơn thế, anh ta có thể cần phải cho chúng tôi biết nó là gì để có được một câu trả lời có ý nghĩa. Tôi nghi ngờ có một công cụ hiện có thực hiện chính xác những gì anh ta muốn, vì vậy tôi đang cố gắng nói cho anh ta cách xây dựng một công cụ. –

+0

Một tùy chọn khác ngoài việc kêu gọi là sử dụng thư viện phân tích cú pháp C++ của Nokia đi kèm với Qt-Creator. Đó là nền tảng, khép kín (libCPlusPlus. {So, dll}) và không quá khó để bắt đầu, nhưng nó vẫn còn xa tầm thường. Nó được viết khá tốt và được hỗ trợ tốt vì nó đã được sử dụng trong nhiều dự án khác nhau của họ. –

2

Bạn có thể lạm dụng một số for cho việc này.

#define SAVETHEDAY for (bool seen = ((void)f(), true); seen; seen = ((void)g(), false)) 

Toán tử dấu phẩy luôn cho phép chức năng của bạn f được thực hiện trước tuyên bố phụ thuộc và g sau đó. Ví dụ

SAVETHEDAY { 

    SAVETHEDAY { 

    } 
} 

Ưu điểm:

  • Làm cho mức làm tổ rõ ràng.
  • Làm việc cho C++ và C99.
  • Giả for vòng lặp sẽ là được tối ưu hóa bởi bất kỳ trình biên dịch phong nha nào.

Nhược điểm:

  • Bạn có thể có những bất ngờ với break, returncontinue bên trong khối, vì vậy g có thể không được gọi trong một tình huống như vậy.
  • Đối với C++, đây là không an toàn chống lại một throw bên trong, một lần nữa g có thể không được gọi là
  • sẽ được tán thành bởi nhiều người kể từ khi là trong một số loại mở rộng ngôn ngữ (s).
  • sẽ được tán thành bởi nhiều người đặc biệt đối với C++ từ thường macro như vậy mà giấu mã được cho là ác

Vấn đề với continue có thể được sửa chữa bằng cách làm mọi thứ một chút khéo léo hơn. Hai nhược điểm đầu tiên có thể được phá vỡ trong C++ bằng cách sử dụng kiểu giả như for-biến đổi mà chỉ có fg trong hàm tạo và hàm hủy.

+0

Ngạc nhiên nhưng vô dụng ... – Joshua

+1

@Joshua: Tiếng Anh của tôi có lẽ không đủ tốt để biết nếu * cleaver * chỉ là lỗi đánh máy hoặc nếu bạn cho nó ý nghĩa sâu sắc hơn. Nhưng dù sao đi nữa, với tôi nó không vô dụng vì tôi sử dụng nó '' hàng ngày '' nó là một thành ngữ rất tiện lợi để bảo vệ ví dụ như một phần quan trọng hoặc để đảm bảo rằng một số mã khởi tạo được chạy chính xác một lần. –

+0

Nó chỉ là một lỗi đánh máy.Tôi thường làm cái đó. – Joshua

0

Công cụ Coccinelle để tìm kiếm ngữ nghĩa và vá mã C được thiết kế cho loại tác vụ này (xem thêm this LWN article trên công cụ).

Các vấn đề liên quan