Derleme zamanında bir yöntemin tam olarak tek bir yerde çağrıldığından emin olun


15

Derleme zamanında bir yöntemin tam olarak tek bir yerde çağrılmasını sağlamanın mümkün olup olmadığını merak ediyorum.

İşlev bir kereden fazla çağrıldığında (örn. Bir döngüde) TAMAM olduğunu unutmayın - ancak iki ayrı döngüde çağrılmamalıdır.

Bu iki parçaya ayrılabilir, ben de her iki parçayı da kapsayan çözümlerle ilgileniyorum:
(a) bir yöntemin en az bir yerde
çağrılmasını sağlamak (b) bir yöntemin en fazla bir yerde çağrılmasını sağlamak

Ben kodun yapısı üzerinde tam kontrole sahip ve aynı fikri elde farklı deyimler bekliyoruz.

// class.h

class MyClass {
  public:
    void my_method();
}

Aşağıdakiler derlenmemelidir (asla çağrılmamalıdır)

#include "class.h"

int main() {
  MyClass my_class;
}

Aşağıdakiler derlenmemelidir (birden fazla yerde çağrılır)

#include "class.h"

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

Aşağıdakiler derlenmelidir (tam olarak tek bir yerde çağrılır):

#include "class.h"

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

2
Bir yöntem yapma. Kodu tek bir yerde satır içine koyun.
user207421

2
Kapanış türü her lambda için benzersiz olduğu için bunu bir lambda ile de yapabileceğinizi düşünüyorum (boş bir lambda olabilir). Yine, bu bir çalışma zamanı hatası olurdu ama istediğin bu değil. Çözmeye çalıştığınız sorun hakkında daha fazla ayrıntı sağlarsanız, bunun için bir yol bulabiliriz.
Indiana Kernick

2
Bunu __COUNTER__yapmak için standart olmayan makroyu kullanabilirsiniz. Gibi bir şey static_assert(__COUNTER__ == 0); my_class.my_method();. Bununla birlikte, sayaç her çeviri biriminde sıfırlanır, böylece işlevin çeviri birimi başına yalnızca bir kez çağrıldığını kontrol edebilirsiniz.
Indiana Kernick

4
Neden bunu yapmak istiyorsun? Bir işlevin noktasının bir kısmı, birden çok yerden çağrılabilmesidir.
Chipster

4
Bunu neden yapmak istediğinizi açıklamalısınız . Belki de istediğin çözüm gerçek hedeflerine ulaşmak için en iyisi değildir.
tenfour

Yanıtlar:


6

Düşük Teknoloji Yaklaşımı:

Kod yapısı üzerinde kontrolünüz olduğundan (derleme sistemini içerir, sanırım), düşük teknoloji çözümü:

  • işlev adını yeterince benzersiz yap
  • kodunuzdaki işlev adı için grep. Bunu iki kez bekliyorsunuz (beyan ve tanımın birlikte bulunduğunu varsayarak):
    • Bir kez başlıkta
    • Bir kez tek çağrı sitesinde

Alternatif:

Gerçekten, gerçekten, gerçekten C ++ ile çözmek istiyorsanız, o zaman deneyebilirsiniz

  • Derleme birimlerindeki kullanım sayısını anlamak için bir derleme zaman sayacı kullanın
  • Üstbilgi birden çok derleme birimine dahil edilmişse, işlevin ODR'yi ihlal edeceğinden emin olun.

Ancak, derleme zaman sayaçları kara büyü (ben ve TMP'yi gerçekten seviyorum diyor) ve bu amaç için ODR ihlallerini zorlamak benzer vudu gibi görünüyor (en azından bağlantı kuramayan bir test vakasına ihtiyacınız olacak).

Ama ciddice:

Bunu yapma. Ne yaparsanız yapın, bir sarmalayıcı işlevi ile neredeyse hiç çaba harcamadan saptırılabilir:

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

MyClass::my_method()yalnızca sarmalayıcıda çağrılır. Diğer herkes sadece derleyici tarafından satırlanmış olan sarıcıyı çağırır.

Diğerlerinin önerdiği gibi: Ne yapmaya çalıştığınızı açıklamak çok daha yararlı olabilir.


1

İşte işe yarayacak kaba bir fikir (bir yorum için çok uzun - ama iyi bir SO cevabı için eksik).

Şablon örneklerini sayarak / kontrol ederek bunu başarabilirsiniz.
Şablonlar yalnızca kullanım sırasında başlatılır .

Benzer şekilde, şablon yöntemi / işlev gövdeleri hiçbir zaman çağrılmazsa ayrıştırılmaz, derlenmez veya bağlanmaz (geçerli sözdiziminin sağlanmasının ötesinde). Bu, vücutlarında herhangi bir örneklemenin yapılmadığı anlamına gelir).

Bununla ilgili bazı küresel örnekleme sayısını ve statik onaylamayı koruyan bir şablon oluşturabilirsiniz (veya geçmiş örneklemeleri kontrol etmek için başka bir TMP mekanizması).


"Global" örnekleme sayısı mevcut derleme birimi için yerel olacaktır.
atomsymbol

1

C ön işlemcisi ve GNU satır içi montajı kullanılarak bu sorunun kısmi bir çözümü vardır:

Başlık dosyası 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:");

Uygulama dosyası 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;
}

Bu çözüm, programın alt çizgi ile başlayan yöntemi sarma makrosu kullanmadan doğrudan çağırmasını engellememesi anlamında kısmi bir yaklaşımdır.


0

Bir constexpr sayacı kullanın. Başka bir soruda bir uygulama var


1
Bu yöntem kötü biçimlendirilmiş gibi geliyor.
Chipster

Bu soruya verilen bir cevaba atıfta bulunan open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#2118 sorunu , standardın bir hatası olduğunu ve yanlış biçimlendirilmesi gerektiğini belirtir.
SD57

Yani kötü biçimlendirilmemiş, en azından henüz değil mi?
Chipster

Henüz kötü biçimlendirilmemişse, mümkün olduğu kadar çabuk insanlar tarafından kullanılmalıdır, böylece bu kullanım durumunu desteklemeleri gerekir!
user1685095
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.