web-dev-qa-db-ja.com

Delphiで現在のプロシージャ/関数の名前を取得する方法(文字列として)

プロシージャ/関数内で、現在のプロシージャ/関数の名前を文字列として取得することは可能ですか?コンパイル時に展開される「マクロ」があると思います。

私のシナリオは次のとおりです。レコードが与えられるプロシージャがたくさんあり、それらはすべてレコードの有効性をチェックすることから始める必要があるため、レコードを「バリデータプロシージャ」に渡します。バリデータープロシージャ(すべてのプロシージャで同じ)は、レコードが無効な場合に例外を発生させます。例外のメッセージに、バリデータープロシージャの名前ではなく、バリデーターを呼び出した関数/プロシージャの名前を含めます。手順(当然)。

つまり、私は持っています

procedure ValidateStruct(const Struct: TMyStruct; const Sender: string);
begin
 if <StructIsInvalid> then
    raise Exception.Create(Sender + ': Structure is invalid.');
end;

その後

procedure SomeProc1(const Struct: TMyStruct);
begin
  ValidateStruct(Struct, 'SomeProc1');
  ...
end;

...

procedure SomeProcN(const Struct: TMyStruct);
begin
  ValidateStruct(Struct, 'SomeProcN');
  ...
end;

代わりに次のようなものを書くことができれば、エラーが発生しにくくなります

procedure SomeProc1(const Struct: TMyStruct);
begin
  ValidateStruct(Struct, {$PROCNAME});
  ...
end;

...

procedure SomeProcN(const Struct: TMyStruct);
begin
  ValidateStruct(Struct, {$PROCNAME});
  ...
end;

そして、コンパイラが{$ PROCNAME}を検出するたびに、「マクロ」を文字列リテラルとして現在の関数/プロシージャの名前に置き換えるだけです。

更新

最初のアプローチの問題は、エラーが発生しやすいことです。たとえば、コピー&ペーストが原因で間違ってしまうことがよくあります。

  procedure SomeProc3(const Struct: TMyStruct);
  begin
    ValidateStruct(Struct, 'SomeProc1');
    ...
  end;

またはタイプミス:

procedure SomeProc3(const Struct: TMyStruct);
begin
  ValidateStruct(Struct, 'SoemProc3');
  ...
end;

または単に一時的な混乱:

procedure SomeProc3(const Struct: TMyStruct);
begin
  ValidateStruct(Struct, 'SameProc3');
  ...
end;
26

私たちは似たようなことをしていて、慣例にのみ依存しています:const SMethodNameを最初に関数名を保持する
次にすべてのルーチンは同じテンプレートに従いますそして、この定数をアサートおよびその他の例外発生で使用します。
constがルーチン名に近接しているため、タイプミスや不一致が長く続く可能性はほとんどありません。
もちろんYMMV.。

procedure SomeProc1(const Struct: TMyStruct);
const
  SMethodName = 'SomeProc1';
begin
  ValidateStruct(Struct, SMethodName);
  ...
end;

...

procedure SomeProcN(const Struct: TMyStruct);
const
  SMethodName = 'SomeProcN';
begin
  ValidateStruct(Struct, SMethodName);
  ...
end;
10
François

これはこの質問の複製だと思います: Delphi 7で現在のメソッドの名前を取得する方法は?

その答えは、そのためには、プロジェクトに何らかの形式のデバッグ情報が必要であり、たとえば、 [〜#〜] jcl [〜#〜] 関数を使用して情報を抽出する必要があるということです。それから。

D2009/2010では新しいRTTIサポートを使用していないことを付け加えておきますが、それを使って何か賢いことができたとしても、私は驚かないでしょう。たとえば、これは クラスのすべてのメソッドをリストする の方法を示し、各メソッドは TRttiMethod で表されます。これは、 「反映されたエンティティの名前を指定する」Nameプロパティ を持つTRttiNamedObjectの子孫です。現在の場所、つまり現在のメソッドへの参照を取得する方法があるはずです。これはすべて当て推量ですが、試してみてください。

8
David

効果を実現する別の方法は、次のような特別なコメントにソースメタデータを入力することです。

ValidateStruct(Struct, 'Blah'); // LOCAL_FUNCTION_NAME

次に、コンパイル前のビルドイベントでソースに対してサードパーティのツールを実行して、そのようなコメントに「LOCAL_FUNCTION_NAME」が含まれる行を見つけ、すべての文字列リテラルをそのようなコードが表示されるメソッド名に置き換えます。コードは

ValidateStruct(Struct, 'SomeProc3'); // LOCAL_FUNCTION_NAME

コード行が「SomeProc3」メソッド内にある場合。たとえば、Pythonでこのようなツールを作成することはまったく難しくありません。また、Delphiで行われるこのテキスト置換も十分に簡単です。

置換が自動的に行われるということは、同期について心配する必要がないことを意味します。たとえば、リファクタリングツールを使用してメソッド名を変更すると、次のコンパイラパスで文字列リテラルが自動的に更新されます。

カスタムソースプリプロセッサのようなもの。

私はこの質問に+1を付けました。これは、特にアサーションの失敗に関するメッセージについて、これまで何度も経験した状況です。スタックトレースにデータが含まれていることは知っていますが、アサーションメッセージ内にルーチン名を含めると、作業が少し簡単になり、OPが指摘したように、手動で行うと古いメッセージの危険性が生じます。

[〜#〜] edit [〜#〜]JcdDebug.pas他の回答で強調表示されているメソッドは、デバッグ情報が存在する場合、私の回答よりもはるかに単純であるように見えます。

2
Caleb Hattingh

コンパイル時マクロはありませんが、十分なデバッグ情報が含まれている場合は、コールスタックを使用してそれを見つけることができます。 これと同じ質問 を参照してください。

2
Lars Truijens

私はあなたがそれを間違った方法でやっていると思います:最初にエラーがあるかどうかをチェックし、それから(つまり:あなたは発信者の名前が必要です)JclDebugのようなツールを使ってリターンを渡すことによって発信者の名前を取得しますスタックからスタックへのアドレス。

プロシージャ名を取得することは、パフォーマンスの面で非常にコストがかかるため、絶対に必要な場合にのみ取得する必要があります。

0
dummzeuch

私はデザインを通して同様の問題を解決しました。あなたはすでにこれをしているように見えるので、あなたの例は私を混乱させます。

検証関数を次のように1回ラップします。

procedure SomeValidateProc3(const Struct: TMyStruct);
  begin
    ValidateStruct(Struct, 'SomeProc3');
  end;

次に、繰り返し呼び出す代わりに:

ValidateStruct(Struct, 'SomeProc3");

あなたが呼ぶ:

SomeValidateProc3(Struct);

タイプミスがある場合、コンパイラはそれをキャッチします。

SoemValidateProc3(Struct);

「ValidateName」のようなラッパー関数にもっと意味のある名前を使用すると、コードも読みやすくなります。

0
Marcus Adams