Đảm bảo tại thời điểm biên dịch rằng một phương thức được gọi ở chính xác một nơi


15

Tôi tò mò về việc liệu có thể đảm bảo tại thời điểm biên dịch rằng một phương thức được gọi ở chính xác một nơi không.

Lưu ý rằng sẽ ổn nếu hàm được gọi nhiều lần (ví dụ: trong một vòng lặp) - nhưng không nên gọi nó trong hai vòng riêng biệt.

Điều này có thể được chia thành hai phần, tôi cũng quan tâm đến các giải pháp bao gồm một trong hai phần:
(a) đảm bảo một phương thức được gọi ở ít nhất một nơi
(b) đảm bảo một phương thức được gọi ở nhiều nhất một nơi

Tôi có toàn quyền kiểm soát cấu trúc của mã và các thành ngữ khác nhau đạt được cùng một ý tưởng đều được chào đón.

// class.h

class MyClass {
  public:
    void my_method();
}

Sau đây không nên biên dịch (không bao giờ được gọi)

#include "class.h"

int main() {
  MyClass my_class;
}

Sau đây không nên biên dịch (được gọi ở nhiều nơi)

#include "class.h"

int main() {
  MyClass my_class;
  my_class.my_method();
  while(true) {
    my_class.my_method();
  }
}

Sau đây sẽ biên dịch (được gọi chính xác ở một nơi):

#include "class.h"

int main() {
  MyClass my_class;
  while(true) {
    my_class.my_method();
  }
}

2
Đừng biến nó thành một phương pháp. Đặt mã nội tuyến ở một nơi đó.
dùng207421

2
Tôi nghĩ bạn cũng có thể làm điều này với lambda (nó có thể là lambda trống) bởi vì kiểu đóng là duy nhất cho mỗi lambda. Một lần nữa, đây sẽ là một lỗi thời gian chạy nhưng đó không phải là những gì bạn yêu cầu. Nếu bạn cung cấp thêm chi tiết về vấn đề mà bạn đang cố gắng giải quyết thì chúng tôi có thể tìm ra cách khắc phục vấn đề này.
Indiana Kernick

2
Bạn có thể sử dụng __COUNTER__macro không chuẩn để làm điều này. Một cái gì đó như static_assert(__COUNTER__ == 0); my_class.my_method();. Tuy nhiên, bộ đếm đặt lại trong mỗi đơn vị dịch để bạn chỉ có thể kiểm tra chức năng được gọi một lần cho mỗi đơn vị dịch.
Indiana Kernick

4
Tại sao bạn muốn làm điều đó? Một phần của điểm của hàm là nó có thể được gọi từ nhiều nơi.
Chipster

4
Bạn nên giải thích lý do tại sao bạn muốn làm điều này. Có thể giải pháp bạn yêu cầu không phải là tốt nhất để thực hiện mục tiêu thực sự của bạn.
mười

Câu trả lời:


6

Phương pháp tiếp cận công nghệ thấp:

Vì bạn có quyền kiểm soát cấu trúc mã (bao gồm hệ thống xây dựng, tôi giả sử), đây là một giải pháp công nghệ thấp:

  • làm cho tên hàm đủ độc đáo
  • grep cho tên hàm trong mã của bạn. Bạn đang mong đợi nó hai lần (giả sử rằng bạn khai báo và định nghĩa được đặt ra):
    • Khi ở trong tiêu đề
    • Một lần tại trang web cuộc gọi duy nhất

Cách khác:

Nếu bạn thực sự, thực sự, thực sự muốn giải quyết nó bằng C ++, thì bạn có thể thử

  • Sử dụng bộ đếm thời gian biên dịch để tìm ra số lượng sử dụng trong một đơn vị biên dịch
  • Đảm bảo rằng hàm sẽ vi phạm ODR nếu tiêu đề được bao gồm trong nhiều đơn vị biên dịch.

Tuy nhiên, bộ đếm thời gian biên dịch là ma thuật đen (tôi và tôi thực sự thích TMP) và việc vi phạm ODR cho mục đích này có vẻ giống như voodoo tương tự (ít nhất là bạn sẽ yêu cầu một trường hợp thử nghiệm không liên kết).

Nhưng nghiêm túc:

Đừng làm điều này. Dù bạn làm gì, nó có thể bị biến đổi mà hầu như không cần nỗ lực bằng chức năng bao bọc:

auto call_my_method(MyClass& o)
{
   return o.my_method();
}

MyClass::my_method()chỉ được gọi trong trình bao bọc. Mọi người khác chỉ cần gọi trình bao bọc mà thậm chí có thể được trình biên dịch nội tuyến.

Như những người khác đề xuất: Nó có thể hữu ích hơn nhiều nếu bạn giải thích những gì bạn đang cố gắng làm.


1

Đây là một ý tưởng sơ bộ có thể hoạt động (quá dài cho một nhận xét - nhưng không đầy đủ cho một câu trả lời SO tốt).

Bạn có thể đạt được điều này bằng cách đếm / kiểm tra tức thời mẫu.
Mẫu chỉ được khởi tạo khi sử dụng .

Tương tự, các thân phương thức / hàm mẫu không được phân tích cú pháp cũng như được biên dịch hoặc liên kết (ngoài việc đảm bảo cú pháp hợp lệ) nếu chúng không bao giờ được gọi. Điều này có nghĩa là bất kỳ tức thời trong cơ thể của họ không được thực hiện).

Bạn có thể tạo một mẫu duy trì một số lượng khởi tạo toàn cầu và xác nhận tĩnh trên đó (hoặc một số cơ chế TMP khác để kiểm tra các tức thời trong quá khứ).


Số lượng khởi tạo "toàn cầu" sẽ là cục bộ của đơn vị biên dịch hiện tại.
atomsymbol

1

Có một giải pháp cho câu hỏi này bằng cách sử dụng bộ tiền xử lý C và lắp ráp nội tuyến GNU:

Tập tin tiêu đề a.h:

struct A {
    // Do not call this method directly, use the macro below to call it
    int _method_vUcaJB5NKSD3upQ(int i, int j);
};

// Use inline assembly to ensure that this macro is used at most once
#define method_vUcaJB5NKSD3upQ(args...) \
    _method_vUcaJB5NKSD3upQ(args); \
    asm (".global once_vUcaJB5NKSD3upQ; once_vUcaJB5NKSD3upQ:");

Hồ sơ thực hiện a.cc:

#include <iostream>
#include "a.h"

int A::_method_vUcaJB5NKSD3upQ(int i, int j) { return i+j+5; }

// Ensure that the macro is used at least once
extern "C" const char once_vUcaJB5NKSD3upQ;
static const char get_vUcaJB5NKSD3upQ = once_vUcaJB5NKSD3upQ;

int main() {
    A a;
    for(int i=0; i<7; i++) {
        // Use a separate statement to call the method
        // (terminated by a semicolon, it cannot be a sub-expression)
        auto x = a.method_vUcaJB5NKSD3upQ(2, 3);
        std::cout << x << std::endl;
    }
    return 0;
}

Giải pháp này là một phần theo nghĩa là nó không ngăn chương trình gọi phương thức bắt đầu bằng dấu gạch dưới trực tiếp mà không sử dụng macro trình bao bọc.


0

Sử dụng bộ đếm constexpr. Có một thực hiện trong một câu hỏi khác


1
Nghe có vẻ như phương pháp này không được hình thành.
Chipster

Vấn đề open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#2118 được tham chiếu trong một câu trả lời cho câu hỏi đó nói rằng đó là một lỗi của tiêu chuẩn và nên được tạo thành không đúng.
SD57

Vì vậy, nó không phải là hình thành, ít nhất là chưa?
Chipster

Nếu nó chưa được định hình, nó nên được sử dụng bởi càng nhiều người càng nhanh càng tốt, để họ phải hỗ trợ cho trường hợp sử dụng này!
dùng1685095
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.