これはあなたが望むものに近いかもしれないと思いますが、確信が持てません。私の理解では、任意の数の引数を持つc関数を許可し、型をチェックしてコンパイル時に削除できるという考えです。
標準を引用して、識別子でのアンダースコアの使用について警告させてください。予約済みの識別子に遭遇する場合があります。ただし、これの可能性は私にはわかりません。
ISO/IEC 9899:1999 (E) 7.1.3
— アンダースコアと大文字または別のアンダースコアで始まるすべての識別子は、常に使用のために予約されています。
このソリューションには GCC が必要です。GCC のバージョンは、ウィーク シンボルもサポートする必要があります。考え方は、コンパイラが弱いシンボルを使用して正しい関数定義を探せるようにすることです。さらに、関数の内容は、コンパイラがデッド ブランチをプルーニングする必要があるという知識を使用して単純化されます。つまり、次のようになります。
if (0) { ... }
さらに分析することなくコンパイル時に(GCC 4.xは確かにこれを行います)。存在しないオプション パラメータを C プリプロセッサ (cpp) シンボルとして定義することにより、関数本体に cpp 条件を含めることを回避できます (必要な場合)。以下の f_opt0 に対して opt1 と opt2 がどのように定義されているかを参照してください。
#include <assert.h>
#include <stdio.h>
extern void f_opt0(int a, int b) __attribute__((weak));
extern void f_opt1(int a, int b, int opt1) __attribute__((weak));
extern void f_opt2(int a, int b, int opt1, int opt2) __attribute__((weak));
#ifdef OPT0
void f_opt0(int a, int b) {
#define opt1 0
#define opt2 0
#endif
#ifdef OPT1
void f_opt1(int a, int b, int opt1) {
#define opt2 0
#endif
#ifdef OPT2
void f_opt2(int a, int b, int opt1, int opt2) {
#endif
if (opt1) printf("opt1=%d\n", opt1);
if (opt2) printf("opt2=%d\n", opt2);
printf("a+b=%d\n", a+b);
#undef opt1
#undef opt2
}
#define f(a, b, o1, o2) \
if (f_opt2) f_opt2(a, b, o1, o2); \
else if (f_opt1) f_opt1(a, b, o1); \
else if (f_opt0) f_opt0(a, b); \
else { assert(0 && "no f() defined!"); }
int main(void) {
f(1, 2, 1, 1);
return 0;
}
私のテストは非常に限られており、これを C の優れた設計とは言いません。問題が発生しやすく、理解するのが面倒です。しかし、それがあなたの目標を達成することを願っています。