28

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

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

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

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;
4

6 に答える 6

12

私たちは似たようなことをしており、慣習のみに依存しています: 関数名を保持するconstを一番SMethodName最初に置きます。
次に、すべてのルーチンが同じ template に従い、この const を Assert およびその他の例外発生で使用します。
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;
于 2010-05-12T17:40:19.943 に答える
8

これはこの質問の複製だと思います: How to get current method's name in Delphi 7?

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

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

于 2010-05-12T11:04:31.963 に答える
2

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

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

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

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

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

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

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

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

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

于 2010-05-12T12:52:52.583 に答える
2

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

于 2010-05-12T11:05:44.257 に答える
0

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

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

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

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

ValidateStruct(Struct, 'SomeProc3");

あなたが呼ぶ:

SomeValidateProc3(Struct);

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

SoemValidateProc3(Struct);

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

于 2010-05-12T13:57:15.933 に答える
0

間違った方法でやっていると思います: まず、エラーがあるかどうかを確認してから (つまり、呼び出し元の名前が必要です)、JclDebug などのツールを使用して、戻り値を渡して呼び出し元の名前を取得します。スタックからそれにアドレスします。

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

于 2010-05-12T20:50:23.080 に答える