16

独自の単体テスト ライブラリを作成するために使用できるマクロを作成しようとしています。私のヘッダーファイルは次のようになります。

#ifndef _TEST_H_
#define _TEST_H_

#include <stdio.h>
#include "hehe_stack.h"

static hehe_stack* tests;
typedef int (*testfunc)();

#define test_init() tests = hehe_stack_init();
#define test_register(test) hehe_stack_push(tests, test);
#define test_info() fprintf(stdout, "running %s :: %s \n", __FILE__, __func__);
#define test_run() testfunc = (int (*)()) hehe_stack_pop(tests); testfunc(); return 0;

#endif

各テスト .c ファイルで、多数の関数ポインターをテスト スタックにプッシュし、スタックから各関数ポインターをポップして呼び出します。私のスタック ポップ メソッドは void ポインターを返し、プッシュしている関数ポインターは int を返し、パラメーターを取りません。私の構文は間違っていますか?これができればいいなと思います。

4

3 に答える 3

33

char*C99 標準では、データへのポインター (標準では、「オブジェクトまたは不完全な型」、たとえばor void*) と関数へのポインターの間の変換は許可されていません。

6.3.2.3:8 あるタイプの関数へのポインターは、別のタイプの関数へのポインターに変換され、再び元に戻される場合があります。結果は元のポインタと等しくなります。変換されたポインターを使用して、ポイント先の型と互換性のない型を持つ関数を呼び出す場合、動作は未定義です。

理由の 1 つは、オブジェクトへのポインターと関数へのポインターが同じサイズである必要がないことです。アーキテクチャ例では、前者を 64 ビット、後者を 32 ビットにすることができます。

特定の関数型へのポインターから別の関数型へのポインターにキャストできます。これは、関数ポインターをデータ構造に格納する必要がある場合に使用することをお勧めする手法です。どの関数型でもかまいません。これは、データ ポインター用のデータ構造を再利用できないことを意味します。データ構造を複製し、関数ポインターを保持するように変更する必要があります。

呼び出す前に、適切な関数ポインター型にキャストバックすることを忘れないでください。そうしないと、これは未定義の動作になります。

Andrew Mellinger が指摘したように、一部のコンパイラでは各方向の変換が許可されていることに注意してください。C11 の附属書「J.5 共通拡張」リスト:

J.5.7 関数ポインタのキャスト

1 オブジェクトまたは void へのポインターを関数へのポインターにキャストして、データを関数として呼び出すことができます (6.5.4)。

2 関数へのポインターは、オブジェクトへのポインターまたは void にキャストでき、関数を検査または変更することができます (たとえば、デバッガーによって) (6.5.4)。

などの一部の POSIX インターフェースdlsym()も、事実上、これらの変換が POSIX システムの C コンパイラで有効であることを義務付けています。

于 2012-12-04T06:59:25.387 に答える
6

あなたはそれを行うことができますが、誤って行うことは想定されていないため、構文は特に扱いにくいものになっています。関数ポインターをキャストするのではなく、関数ポインターへのポインターをキャストしてから、それを割り当てます。

#define test_run() *((void**)&testfunc) = hehe_stack_pop(tests); testfunc(); return 0;

これは&testfuncへのポインタに変わりvoid*、それを逆参照して別の値を代入しますvoid*。これは正当です。

于 2012-12-04T06:06:55.010 に答える
0

void *ポインターを逆参照することは想定されていないため、提案されたコードはコンパイルされません(どうすればよいですか?特定のポインターに関する型情報はありません)。

cmotleyが彼のコメントで示唆している方法は、これを行う正しい方法ですが、読みやすくするために少し改善することをお勧めします。

typedef int (*tTestFuncSignature)(void)
#define test_run() tTestFuncSignature testfunc = hehe_stack_pop(tests); testfunc();

または、このマクロを使用して非表示の名前の衝突を回避することもできます。

#define test_run() ((tTestFuncSignature)hehe_stack_pop(tests))();

いずれの方法でも、(コントラクトなどによって)スタックに有効なポインターのみが含まれていることを確認するか、関数を呼び出す前に最初にポインターをテストする必要があります。

編集:修正されたコードフォーマット

于 2012-12-04T06:42:46.963 に答える