web-dev-qa-db-ja.com

Cで「プライベート/制限付き」関数を実装する方法は?

Cのインタビューで非常に興味深い質問を受けました:特定のf()関数からのみ呼び出すことができるように、関数g()を実装するにはどうすればよいですか? 。 g()以外の関数がf()を呼び出そうとすると、コンパイラエラーが発生します。

最初は、これは関数ポインタで行うことができましたが、実行時に呼び出しをブロックすることに近づくことができました。しかし、コンパイル時の戦略を考えることはできませんでした。これがansi Cを使用して可能かどうかさえわかりません。

誰かが何か考えを持っていますか?

28
luizleroy

これが1つの方法です。

_int f_real_name(void)
{
    ...
}

#define f f_real_name
int g(void)
{
    // call f()
}
#undef f

// calling f() now won't work
_

別の方法として、f()g()がファイル内の唯一の関数であることを保証できる場合、f()staticとして宣言します。

編集:コンパイラエラーを引き起こす別のマクロトリック:

_static int f(void) // static works for other files
{
    ...
}

int g(void)
{
    // call f()
}
#define f call function

// f() certainly produces compiler errors here
_
38
Chris Lutz

g()およびf()を同じモジュールに入れ、f()を静的として宣言します。キーワードは、f()を同じモジュールまたはソースファイル内の関数でのみ使用可能にします。

また、f() and g()を使用するモジュールでは他のメソッドを許可しないでください。そうしないと、f()を呼び出すことができます。

PS-クリス・ルッツの答えは実際に最高だと思います。このアプローチに言及しているだけでなく、より少ない環境条件で機能する巧妙なマクロの名前変更も行っています(これらの2つの機能のために特別にモジュールファイルを必要としません)。

マクロを使用すると、次のことも実行できることにも注意してください。

#define f() f_should_not_be_called_by_anything_except_g

これは、Niceエラーメッセージを表示し、オートコンプリーター(Visual Studioなど)は、ユーザーがf()を入力したときにそのヒントを表示します。

27
Walt W

staticキーワードを使用して、モジュールプライベート関数を作成できます。

_static void func(void)
{
    // ...
}
_

その場合、func()は、同じファイルで定義されている他の関数(技術的には、同じ翻訳単位:_#include_ディレクティブに定義が含まれている他の関数)からのみ呼び出すことができます。引き続きアクセスできます)。 funcには内部リンケージがあると言われています。他のすべての関数(つまり、staticキーワードなし)にはexternalリンケージがあると言われています。

それを超えて、いいえ、機能にアクセスできないようにする方法はありません。マクロを使用して関数の名前を変更できますが、他のコードは常に適切な名前でそれにアクセスできます。

11
Adam Rosenfield

GCCのオプションは 入れ子関数 を使用することです。標準のCではありませんが、非常にうまく機能します。

8
outis

f()g()を同じソースファイルに配置し、f() staticを宣言します。

8
John Millikin

偶然にのみ可能です。

関数f()およびg()が両方とも同じソースファイルにあり、ファイルに他の関数がない場合、およびg()は、f()への関数ポインタをその呼び出し元に返すことはないため、f()を静的にすると仕事。

他の関数を同じソースファイルに含める必要がある場合は、f()を静的関数としてファイルの下部に配置し、g()のみを定義します。直後にほぼ同じ効果が得られますが、「宣言がない」ことでエラーを生成するようコンパイラーに指示しなかった場合、他の関数が警告付きで呼び出す可能性があります。

#include <stdio.h>

extern void g(void);    /* in a header */

/* Other functions that may not call f() go here */

static void f(void)
{
    puts("X");
}

void g(void)
{
    f();
}

明らかに、この手法を同じファイル内の別の関数のペアに確実に拡張することはできません-x() and y()-such that x() and only x() can call y() while while g() and g()のみf()を同時に呼び出すことができます。

ただし、通常、プログラマーの規律に依存し、f()をソースファイルで静的にして、g()のみであるコメントを付けます。それを呼び出してから、コードを変更して、g()以外の関数がf()を呼び出すように)誰にでも懲戒することができます。

3

あなたはこれを行うことができます

//some code ...
//You Can't call f() in here - compile time error
//some code ...
void f()
{
    //implement f() in here
}
void g()
{
    //You can only call f() in here - no error
}
#define f() Gimme some ERRORS!!!
//some code ...
//You Can't call f() in here - compile time error
//some code ...
0
Tomer Wolberg