Pastikan pada waktu kompilasi bahwa suatu metode dipanggil tepat di satu tempat

15

Saya ingin tahu apakah mungkin untuk memastikan pada waktu kompilasi bahwa suatu metode dipanggil tepat di satu tempat.

Perhatikan bahwa tidak apa-apa jika fungsi dipanggil lebih dari satu kali (misalnya dalam satu lingkaran) - tetapi tidak boleh dipanggil dalam dua loop terpisah.

Ini dapat dipecah menjadi dua bagian, saya juga tertarik pada solusi yang mencakup bagian mana pun:
(a) memastikan suatu metode dipanggil di setidaknya satu tempat
(b) memastikan suatu metode dipanggil di paling banyak satu tempat

Saya memiliki kontrol penuh atas struktur kode, dan berbagai idiom yang mencapai ide yang sama dipersilakan.

// class.h

class MyClass {
  public:
    void my_method();
}

Yang berikut tidak boleh dikompilasi (tidak pernah dipanggil)

#include "class.h"

int main() {
  MyClass my_class;
}

Yang berikut tidak boleh dikompilasi (dipanggil di lebih dari satu tempat)

#include "class.h"

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

Yang berikut harus dikompilasi (dipanggil tepat di satu tempat):

#include "class.h"

int main() {
  MyClass my_class;
  while(true) {
    my_class.my_method();
  }
}
yoyoy
sumber
2
Jangan menjadikannya metode. Letakkan kode sebaris di satu tempat itu.
user207421
2
Saya pikir Anda juga bisa melakukan ini dengan lambda (bisa jadi lambda kosong) karena jenis penutupannya unik untuk setiap lambda. Sekali lagi, ini akan menjadi kesalahan runtime tetapi bukan itu yang Anda minta. Jika Anda memberikan detail lebih lanjut tentang masalah yang Anda coba selesaikan, maka kami mungkin dapat menemukan jalan keluarnya.
Indiana Kernick
2
Anda bisa menggunakan __COUNTER__makro non-standar untuk melakukan ini. Sesuatu seperti static_assert(__COUNTER__ == 0); my_class.my_method();. Namun, penghitung diatur ulang di setiap unit terjemahan sehingga Anda hanya dapat memeriksa bahwa fungsi tersebut dipanggil satu kali per unit terjemahan.
Indiana Kernick
4
Mengapa Anda ingin melakukan itu? Bagian dari titik fungsi adalah dapat dipanggil dari banyak tempat.
Chipster
4
Anda harus menjelaskan mengapa Anda ingin melakukan ini. Mungkin solusi yang Anda minta bukanlah yang terbaik untuk mencapai tujuan Anda yang sebenarnya.
tenfour

Jawaban:

6

Pendekatan Teknologi Rendah:

Karena Anda memiliki kendali atas struktur kode (yang mencakup sistem build, saya asumsikan), berikut adalah solusi berteknologi rendah:

  • membuat nama fungsi cukup unik
  • grep untuk nama fungsi dalam kode Anda. Anda mengharapkannya dua kali (dengan asumsi bahwa Anda mendeklarasikan dan mendefinisikannya):
    • Sekali di header
    • Sekali di situs panggilan tunggal

Kalau tidak:

Jika Anda benar-benar ingin menyelesaikannya dengan C ++, maka Anda bisa mencobanya

  • Gunakan penghitung waktu kompilasi untuk mengetahui jumlah penggunaan dalam unit kompilasi
  • Pastikan bahwa fungsi tersebut akan melanggar ODR jika header disertakan dalam beberapa unit kompilasi.

Namun, kompilasi penghitung waktu adalah ilmu hitam (kata saya, dan saya sangat suka TMP), dan memaksa pelanggaran ODR untuk tujuan ini tampak seperti voodoo yang sama (setidaknya Anda akan memerlukan uji kasus yang gagal menghubungkan).

Tapi serius:

Jangan lakukan ini. Apa pun yang Anda lakukan, itu dapat diputarbalikkan dengan hampir tanpa usaha oleh fungsi pembungkus:

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

MyClass::my_method()disebut hanya di bungkusnya. Semua orang hanya memanggil pembungkus yang mungkin bahkan diuraikan oleh kompiler.

Seperti yang disarankan orang lain: Mungkin akan jauh lebih bermanfaat jika Anda menjelaskan apa yang ingin Anda lakukan.

Rumburak
sumber
1

Inilah ide kasar yang mungkin berhasil (terlalu lama untuk komentar - tetapi tidak lengkap untuk jawaban SO yang baik).

Anda mungkin dapat mencapai ini dengan menghitung / memeriksa Instansi template.
Template hanya dipakai saat digunakan .

Demikian pula, metode templat / fungsi fungsi tidak diuraikan atau dikompilasi atau dihubungkan (di luar memastikan sintaks yang valid) jika mereka tidak pernah dipanggil. Ini berarti bahwa setiap instantiasi dalam tubuh mereka tidak dibuat).

Anda mungkin dapat membuat templat yang mempertahankan beberapa jumlah instantiasi global dan menyatakan statis tentang itu (atau mekanisme TMP lain untuk memeriksa instantiasi sebelumnya).

Adi Shavit
sumber
Jumlah instantiasi "global" akan menjadi lokal untuk unit kompilasi saat ini.
atomsymbol
1

Ada solusi parsial untuk pertanyaan ini menggunakan C preprocessor dan GNU inline assembly:

File tajuk 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:");

File implementasi 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;
}

Solusi ini sebagian dalam arti bahwa itu tidak mencegah program untuk memanggil metode yang dimulai dengan garis bawah secara langsung tanpa menggunakan pembungkus makro.

simbol atom
sumber
0

Gunakan penghitung constexpr. Ada implementasi dalam pertanyaan lain

SD57
sumber
1
Sepertinya metode ini tidak sempurna.
Chipster
Masalah open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#2118 yang dirujuk dalam jawaban untuk pertanyaan itu menyatakan bahwa itu adalah bug dari standar dan harus dibuat cacat.
SD57
Jadi itu belum terbentuk, setidaknya belum?
Chipster
Jika belum terbentuk dengan buruk, itu harus digunakan oleh sebanyak mungkin orang secepat mungkin, sehingga mereka harus mendukung kasus penggunaan ini!
user1685095