コンパイル時にメソッドが1か所で呼び出されるようにする


15

コンパイル時にメソッドが正確に1か所で呼び出されるようにすることが可能かどうかについて知りたいです。

関数が2回以上呼び出されても問題ないことに注意してください(例:ループ内)-ただし、2つの別々のループで呼び出されるべきではありません。

これは二つの部分に分けることができ、私もどちらかの一部を覆うソリューションに興味があります:
()メソッドを確保するが、少なくとも一箇所に呼び出された
中で呼び出された(b)の方法を確保多くても1つの場所で

私はコードの構造を完全に制御しており、同じアイデアを実現するさまざまなイディオムを歓迎します。

// class.h

class MyClass {
  public:
    void my_method();
}

以下はコンパイルされるべきではありません(決して呼び出されません)

#include "class.h"

int main() {
  MyClass my_class;
}

以下はコンパイルしないでください(複数の場所で呼び出されます)

#include "class.h"

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

以下はコンパイルする必要があります(1か所で呼び出されます)。

#include "class.h"

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

2
それを方法にしないでください。その1つの場所にコードをインラインで配置します。
user207421

2
ラムダを使用してこれを行うこともできると思います(空のラムダの場合もあります)。クロージャのタイプはラムダごとに一意であるためです。繰り返しますが、これは実行時エラーになりますが、それはあなたが要求したものではありません。解決しようとしている問題の詳細を提供すると、これを回避する方法を見つけることができる場合があります。
インディアナカーニック

2
__COUNTER__これを行うには、非標準のマクロを使用できます。のようなものstatic_assert(__COUNTER__ == 0); my_class.my_method();。ただし、カウンターは各翻訳単位でリセットされるため、関数が翻訳単位ごとに1回呼び出されることのみを確認できます。
インディアナカーニック

4
なぜそれをしたいのですか?関数のポイントの1つは、複数の場所から呼び出せることです。
チップスター

4
これを行う理由を説明する必要があります。たぶん、あなたが求めている解決策は、あなたの実際の目標を達成するための最良のものではありません。
テンフォア

回答:


6

ローテクアプローチ:

コード構造(ビルドシステムを含む)を制御できるため、ローテクソリューションを次に示します。

  • 関数名を十分に一意にする
  • コード内の関数名のgrep。あなたはそれを2回期待しています(宣言と定義が同じ場所にあると仮定します):
    • ヘッダーに一度
    • シングルコールサイトで一度

または:

あなたが本当に、本当に、本当にC ++でそれを解決したいなら、あなたは試すことができます

  • コンパイル時間カウンターを使用して、コンパイル単位内の使用回数を把握する
  • ヘッダーが複数のコンパイル単位に含まれている場合、関数がODRに違反することを確認してください。

ただし、コンパイル時間カウンターは黒魔術であり(たとえば、私はTMPが本当に好きです)、この目的でODR違反を強制すると、似たようなブードゥーのように見えます(少なくとも、リンクに失敗するテストケースが必要になります)。

しかし真剣に:

これを行わないでください。あなたが何をするにせよ、ラッパー関数によってほとんど努力なしでそれは倒錯することができます:

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

MyClass::my_method()ラッパーでのみ呼び出されます。他の誰もがおそらくコンパイラによってインライン化されるラッパーを呼び出すだけです。

他の人が示唆したように:何をしようとしているのかを説明すると、はるかに役立つ場合があります。


1

機能する可能性のある大まかなアイデアを以下に示します(コメントには長すぎますが、適切なSO回答には不完全です)。

テンプレートのインスタンス化をカウント/チェックすることで、これを実現できる場合があります。
テンプレートは使用時にのみインスタンス化されます

同様に、テンプレートメソッド/関数の本体は、呼び出されない場合は、構文解析もコンパイルもリンクもされません(有効な構文の保証を超えて)。これは、それらのボディ内のインスタンス化が行われないことを意味します)。

グローバルインスタンス化カウントとその上の静的アサート(または過去のインスタンス化をチェックする他のTMPメカニズム)を維持するテンプレートを作成できる場合があります。


「グローバル」インスタンス化カウントは、現在のコンパイル単位に対してローカルです。
atomymbol

1

CプリプロセッサとGNUインラインアセンブリを使用して、この質問に対する部分的な解決策があります。

ヘッダーファイル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:");

実装ファイル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;
}

このソリューションは、ラッパーマクロを使用せずにプログラムがアンダースコアで始まるメソッドを直接呼び出すことを妨げないという意味で部分的です。


0

constexprカウンターを使用します。別の質問に実装があります


1
このメソッドは形式が悪いようです。
チップスター

問題open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#2118は、それが標準のバグで、病気に形成されるべきであることをその質問の状態への回答の中で参照します。
SD57

それで、少なくともまだ、形式が正しくありませんか?
チップスター

まだ形式が正しくない場合は、できるだけ早く多くの人が使用して、このユースケースをサポートする必要があります。
user1685095
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.