web-dev-qa-db-ja.com

関数がC / C ++に存在するかどうかを確認する方法

コード内の特定の状況では、その関数が定義されている場合にのみ関数を呼び出すことになり、そうでない場合はすべきではありません。どうすればこれを達成できますか?

like:
if (function 'sum' exists ) then invoke sum ()

この質問をする他の方法かもしれません:実行時に関数が定義されているかどうかを判断する方法と定義されている場合は、呼び出します。

24
Whoami

他の返信は役に立つアドバイスですが(dlsym、関数ポインターなど)、あなたはコンパイルできません関数を参照するC++コード存在しません。少なくとも、関数はdeclaredである必要があります。そうでない場合、コードはコンパイルされません。何も(コンパイルユニット、オブジェクトファイル、ライブラリ)が関数を定義しない場合、リンカは文句を言います(弱い場合を除き、以下を参照)。

しかし、なぜそれを求めているのかを本当に説明する必要があります。推測できません。あなたの述べられていない目標を達成する方法はいくつかあります。

dlsymname mangling なしで、つまりextern "C"として宣言された関数を必要とすることが多いことに注意してください。

LinuxでGCCを使用してコーディングする場合は、weak関数属性 を宣言で使用することもできます。次に、リンカは未定義の弱いシンボルをnullに設定します。

補遺

一部の入力から関数名を取得している場合は、関数のサブセットのみがそのように呼び出すことができることに注意してください(注意せずに任意の関数を呼び出すと、クラッシュします!)そのサブセットを明示的に構築することをお勧めします。次に、std::mapまたはdlsymを使用できます(サブセットの各関数はextern "C"と宣言されています)。 dlopenNULLパスを使用すると、メインプログラムへのハンドルが提供されます。これを正しく機能させるには、-rdynamicとリンクする必要があります。

本当に適切に定義された関数のサブセットのみを名前で呼び出す必要があります。たとえば、おそらくこの方法でabortexit、またはforkを呼び出す必要はありません。

NB。呼び出された関数のシグネチャが動的にわかっている場合は、 libffi を使用して呼び出すことができます。

11

「合計」を宣言するときは、次のように宣言できます。

#define SUM_EXISTS
int sum(std::vector<int>& addMeUp) {
    ...
}

次に、それを使用するようになったとき、あなたは行くことができます:

#ifdef SUM_EXISTS
int result = sum(x);
...
#endif

実行時にすべてが行われるスクリプト言語を使用していると思います。 C++で覚えておくべき主なことは、2つのフェーズです。

  • コンパイル時間
    • プリプロセッサの実行
    • テンプレートコードは実際のソースコードに変わります
    • ソースコードはマシンコードに変換されます
  • ランタイム
    • マシンコードが実行されます

したがって、すべての#defineそしてそのようなことがコンパイル時に起こります。

....

実行時にすべてを本当に実行したい場合は、いくつかの コンポーネントアーキテクチャ製品 を使用することに興味があるかもしれません。

または多分 プラグインの種類のアーキテクチャ はあなたが求めているものです。

13
matiu

私は、ポスターが実際にSFINAEチェック/ディスパッチの線に沿ってもっと何かを探していたのではないかと思います。 C++テンプレートを使用すると、目的の関数(存在する場合)を呼び出すテンプレート関数と、何もしない(関数が存在しない場合)テンプレート関数をテンプレートに定義できます。次に、最初のテンプレートを目的の関数に依存させることができます。これにより、関数が存在しない場合にテンプレートの形式が正しくなくなります。 C++テンプレートの置換の失敗はエラーではないため(SFINAE)、これは有効です。したがって、コンパイラーは2番目のケース(たとえば、何も実行できない可能性があります)にフォールバックします。

優れた例については、こちらをご覧ください: 関数の存在を確認するためのテンプレートを作成することは可能ですか?

11
fredbaba

GCCを使用すると、次のことができます。

void func(int argc, char *argv[]) __attribute__((weak)); // weak declaration must always be present

// optional definition:
/*void func(int argc, char *argv[]) { 
    printf("ENCONTRE LA FUNC\n");
    for(int aa = 0; aa < argc; aa++){
        printf("arg %d = %s \n", aa, argv[aa]);
    }
}*/

int main(int argc, char *argv[]) {
    if (func){ 
        func(argc, argv); 
    } else {
        printf("no encontre la func\n");
    }
}

Funcのコメントを外すと実行され、それ以外の場合は「no encontre la func\n」と出力されます。

関数へのポインタを使用します。

 //initialize
 typedef void (*PF)();
 std::map<std::string, PF> defined_functions;
 defined_functions["foo"]=&foo;
 defined_functions["bar"]=&bar;
 //if defined, invoke it
 if(defined_functions.find("foo") != defined_functions.end())
 {
     defined_functions["foo"]();
 }
10
BruceAdi

呼び出す関数が含まれているライブラリがわかっている場合は、 dlsym() および dlerror() を使用してそこにあるかどうか、そして関数へのポインタが何であるかを調べてください。

編集:私はおそらくこのアプローチを実際には使用しないでしょう-その代わりに、私はマティウのソリューションをお勧めします。しかし、dlsym()はあまり知られていないので、指摘しておこうと思いました。

5
Timothy Jones

それをサポートするコンパイラには#pragma weakを使用できます( 弱いシンボル ウィキペディアのエントリを参照)。

この例とコメントは 共有ライブラリと動的読み込みの裏話 からのものです。

#pragma weak debug
extern void debug(void);
void (*debugfunc)(void) = debug;
int main() {
    printf(“Hello World\n”);
    if (debugfunc) (*debugfunc)();
}

弱いプラグマを使用して、未解決のシンボル[..]を無視するようリンカーに強制できます。プログラムは、オブジェクトファイルでdebug()が実際に定義されているかどうかに関係なくコンパイルおよびリンクします。シンボルが未定義のままである場合、リンカは通常その値を0に置き換えます。したがって、この手法は、アプリケーション全体を再コンパイルする必要のないオプションのコードをプログラムが呼び出すための有用な方法となります。

2
mihai

つまり、c ++ 11を使用している場合は、ファンクタを使用することになります。

これをファイルの先頭に置く必要があります:

#include <functional>

ファンクタのタイプは次の形式で宣言されます。

std::function< return_type (param1_type, param2_type) >

次のように、合計のファンクタを保持する変数を追加できます。

std::function<int(const std::vector<int>&)> sum;

物事を簡単にするために、paramタイプを短くしましょう:

using Numbers = const std::vectorn<int>&;

次に、ファンクター変数に次のいずれかを入力します。

ラムダ:

sum = [](Numbers x) { return std::accumulate(x.cbegin(), x.cend(), 0); } // std::accumulate comes from #include <numeric>

関数ポインタ:

int myFunc(Numbers nums) {
    int result = 0;
    for (int i : nums)
        result += i;
    return result;
}
sum = &myFunc;

「バインド」によって作成されたもの:

struct Adder {
    int startNumber = 6;
    int doAdding(Numbers nums) {
        int result = 0;
        for (int i : nums)
            result += i;
        return result;
    }
};
...
Adder myAdder{2}; // Make an adder that starts at two
sum = std::bind(&Adder::doAdding, myAdder);

最後にそれを使用するには、単純なifステートメントです。

if (sum)
    return sum(x);

要約すると、ファンクタは関数への新しいポインタですが、より汎用性があります。コンパイラが十分確実であればインライン化されますが、一般的には関数ポインタと同じです。

Std :: bindおよびlambdaと組み合わせると、古いスタイルのC関数ポインターよりもはるかに優れています。

ただし、c ++ 11以降の環境で動作することを覚えておいてください。 (CまたはC++ 03にはありません)。

2
matiu