Cの関数にデフォルトの引数を指定する方法はありますか?
22 に答える
うわー、誰もがこのあたりのような悲観論者です。答えはイエスです。
些細なことではありません。最終的には、コア関数、サポート構造体、ラッパー関数、およびラッパー関数の周りのマクロができあがります。私の仕事では、これらすべてを自動化するための一連のマクロがあります。フローを理解すれば、同じことを簡単に行うことができます。
私はこれを他の場所で書いたので、ここに要約を補足するための詳細な外部リンクがあります:http: //modelingwithdata.org/arch/00000022.htm
向きを変えたい
double f(int i, double x)
デフォルトをとる関数に(i = 8、x = 3.14)。コンパニオン構造体を定義します。
typedef struct {
int i;
double x;
} f_args;
関数の名前を変更f_base
し、デフォルトを設定してベースを呼び出すラッパー関数を定義します。
double var_f(f_args in){
int i_out = in.i ? in.i : 8;
double x_out = in.x ? in.x : 3.14;
return f_base(i_out, x_out);
}
次に、Cの可変個引数マクロを使用してマクロを追加します。このようにして、ユーザーは実際にf_args
構造体にデータを入力していることを知り、通常どおりに実行していると考える必要がありません。
#define f(...) var_f((f_args){__VA_ARGS__});
OK、これで次のすべてが機能します。
f(3, 8); //i=3, x=8
f(.i=1, 2.3); //i=1, x=2.3
f(2); //i=2, x=3.14
f(.x=9.2); //i=8, x=9.2
複合初期化子が正確なルールのデフォルトを設定する方法に関するルールを確認してください。
うまくいかないことが1つf(0)
あります。それは、欠落している値とゼロを区別できないためです。私の経験では、これは注意が必要なことですが、必要に応じて対処することができます---デフォルトの半分の時間は実際にはゼロです。
名前付き引数とデフォルトは本当にCでのコーディングをより簡単に、そしてさらに楽しくするのだと思うので、私はこれを書くのに苦労しました。そして、Cはとてもシンプルでありながら、これをすべて可能にするのに十分なものを持っているという点で素晴らしいです。
はい。:-)しかし、あなたが期待するような方法ではありません。
int f1(int arg1, double arg2, char* name, char *opt);
int f2(int arg1, double arg2, char* name)
{
return f1(arg1, arg2, name, "Some option");
}
残念ながら、Cではメソッドをオーバーロードできないため、2つの異なる関数になってしまいます。それでも、f2を呼び出すことにより、実際にはデフォルト値でf1を呼び出すことになります。これは「Don'tRepeatYourself」ソリューションであり、既存のコードのコピー/貼り付けを回避するのに役立ちます。
あまり。唯一の方法は、varargs関数を記述し、呼び出し元が渡さない引数のデフォルト値を手動で入力することです。
デフォルト値に名前付きパラメーター(のみ)を使用する関数を作成できます。これはbk。の答えの続きです。
#include <stdio.h>
struct range { int from; int to; int step; };
#define range(...) range((struct range){.from=1,.to=10,.step=1, __VA_ARGS__})
/* use parentheses to avoid macro subst */
void (range)(struct range r) {
for (int i = r.from; i <= r.to; i += r.step)
printf("%d ", i);
puts("");
}
int main() {
range();
range(.from=2, .to=4);
range(.step=2);
}
C99標準では、初期化の後半の名前が前の項目を上書きすることを定義しています。また、いくつかの標準的な位置パラメータを設定することもできます。それに応じて、マクロと関数のシグネチャを変更するだけです。デフォルト値のパラメーターは、名前付きパラメータースタイルでのみ使用できます。
プログラム出力:
1 2 3 4 5 6 7 8 9 10
2 3 4
1 3 5 7 9
OpenCVは次のようなものを使用します:
/* in the header file */
#ifdef __cplusplus
/* in case the compiler is a C++ compiler */
#define DEFAULT_VALUE(value) = value
#else
/* otherwise, C compiler, do nothing */
#define DEFAULT_VALUE(value)
#endif
void window_set_size(unsigned int width DEFAULT_VALUE(640),
unsigned int height DEFAULT_VALUE(400));
ユーザーが何を書くべきかわからない場合は、このトリックが役立ちます。
いいえ、それはC++言語の機能です。
いいえ。
最新のC99標準でさえこれをサポートしていません。
おそらくこれを行うための最良の方法(状況によっては可能かどうかは不明です)は、C ++に移行し、それを「より良いC」として使用することです。クラス、テンプレート、演算子のオーバーロード、またはその他の高度な機能を使用せずにC++を使用できます。
これにより、関数のオーバーロードとデフォルトのパラメーター(および使用することを選択したその他の機能)を備えたCのバリアントが提供されます。C ++の制限されたサブセットのみを使用することを真剣に考えている場合は、少し訓練する必要があります。
多くの人が、このようにC ++を使用するのはひどい考えだと言うでしょう、そして彼らは意味があるかもしれません。しかし、それは単なる意見です。全部を購入しなくても、快適なC++の機能を使用することは有効だと思います。C ++が成功した理由の重要な部分は、C ++が初期の頃、まさにこのように非常に多くのプログラマーによって使用されていたためだと思います。
簡単な答え:いいえ。
少し長い答え:オプションの引数のために解析する文字列を渡すという古い、古い回避策があります。
int f(int arg1, double arg2, char* name, char *opt);
ここで、optには「name = value」ペアなどが含まれる場合があり、これを次のように呼び出します。
n = f(2,3.0,"foo","plot=yes save=no");
明らかに、これはたまにしか役に立ちません。一般に、機能ファミリへの単一のインターフェイスが必要な場合。
このアプローチは、C ++のプロのプログラム(たとえばROOTなど)によって記述された素粒子物理学コードにも見られます。主な利点は、下位互換性を維持しながら、ほぼ無期限に拡張できることです。
さらに別のオプションはstruct
sを使用します:
struct func_opts {
int arg1;
char * arg2;
int arg3;
};
void func(int arg, struct func_opts *opts)
{
int arg1 = 0, arg3 = 0;
char *arg2 = "Default";
if(opts)
{
if(opts->arg1)
arg1 = opts->arg1;
if(opts->arg2)
arg2 = opts->arg2;
if(opts->arg3)
arg3 = opts->arg3;
}
// do stuff
}
// call with defaults
func(3, NULL);
// also call with defaults
struct func_opts opts = {0};
func(3, &opts);
// set some arguments
opts.arg3 = 3;
opts.arg2 = "Yes";
func(3, &opts);
マクロを使用する別のトリック:
#include <stdio.h>
#define func(...) FUNC(__VA_ARGS__, 15, 0)
#define FUNC(a, b, ...) func(a, b)
int (func)(int a, int b)
{
return a + b;
}
int main(void)
{
printf("%d\n", func(1));
printf("%d\n", func(1, 2));
return 0;
}
渡される引数が1つだけの場合b
、デフォルト値(この場合は15)を受け取ります
いいえ。
はい、C99の機能を使用してこれを行うことができます。これは、新しいデータ構造などを定義することなく、また関数が実行時にどのように呼び出されるかを決定する必要がなく、計算のオーバーヘッドなしで機能します。
詳細な説明については、私の投稿を参照してください。
http://gustedt.wordpress.com/2010/06/03/default-arguments-for-c99/
イェンス
いいえ。ただし、一連の関数(またはマクロ)を使用して、デフォルトの引数を使用して概算することを検討してください。
// No default args
int foo3(int a, int b, int c)
{
return ...;
}
// Default 3rd arg
int foo2(int a, int b)
{
return foo3(a, b, 0); // default c
}
// Default 2nd and 3rd args
int foo1(int a)
{
return foo3(a, 1, 0); // default b and c
}
Jens Gustedtの回答を改善して、次のようにしました。
- インライン関数は使用されません
- デフォルトは前処理中に計算されます
- モジュール式の再利用可能なマクロ
- 許可されたデフォルトに対して引数が不十分な場合に意味のある一致をするコンパイラエラーを設定することが可能
- 引数の型が明確なままである場合、パラメータリストの末尾を形成するためにデフォルトは必要ありません
- C11と相互運用する_Generic
- 引数の数で関数名を変えてください!
variadic.h:
#ifndef VARIADIC
#define _NARG2(_0, _1, _2, ...) _2
#define NUMARG2(...) _NARG2(__VA_ARGS__, 2, 1, 0)
#define _NARG3(_0, _1, _2, _3, ...) _3
#define NUMARG3(...) _NARG3(__VA_ARGS__, 3, 2, 1, 0)
#define _NARG4(_0, _1, _2, _3, _4, ...) _4
#define NUMARG4(...) _NARG4(__VA_ARGS__, 4, 3, 2, 1, 0)
#define _NARG5(_0, _1, _2, _3, _4, _5, ...) _5
#define NUMARG5(...) _NARG5(__VA_ARGS__, 5, 4, 3, 2, 1, 0)
#define _NARG6(_0, _1, _2, _3, _4, _5, _6, ...) _6
#define NUMARG6(...) _NARG6(__VA_ARGS__, 6, 5, 4, 3, 2, 1, 0)
#define _NARG7(_0, _1, _2, _3, _4, _5, _6, _7, ...) _7
#define NUMARG7(...) _NARG7(__VA_ARGS__, 7, 6, 5, 4, 3, 2, 1, 0)
#define _NARG8(_0, _1, _2, _3, _4, _5, _6, _7, _8, ...) _8
#define NUMARG8(...) _NARG8(__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1, 0)
#define _NARG9(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, ...) _9
#define NUMARG9(...) _NARG9(__VA_ARGS__, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
#define __VARIADIC(name, num_args, ...) name ## _ ## num_args (__VA_ARGS__)
#define _VARIADIC(name, num_args, ...) name (__VARIADIC(name, num_args, __VA_ARGS__))
#define VARIADIC(name, num_args, ...) _VARIADIC(name, num_args, __VA_ARGS__)
#define VARIADIC2(name, num_args, ...) __VARIADIC(name, num_args, __VA_ARGS__)
// Vary function name by number of arguments supplied
#define VARIADIC_NAME(name, num_args) name ## _ ## num_args ## _name ()
#define NVARIADIC(name, num_args, ...) _VARIADIC(VARIADIC_NAME(name, num_args), num_args, __VA_ARGS__)
#endif
簡略化された使用シナリオ:
const uint32*
uint32_frombytes(uint32* out, const uint8* in, size_t bytes);
/*
The output buffer defaults to NULL if not provided.
*/
#include "variadic.h"
#define uint32_frombytes_2( b, c) NULL, b, c
#define uint32_frombytes_3(a, b, c) a, b, c
#define uint32_frombytes(...) VARIADIC(uint32_frombytes, NUMARG3(__VA_ARGS__), __VA_ARGS__)
そして_Genericで:
const uint8*
uint16_tobytes(const uint16* in, uint8* out, size_t bytes);
const uint16*
uint16_frombytes(uint16* out, const uint8* in, size_t bytes);
const uint8*
uint32_tobytes(const uint32* in, uint8* out, size_t bytes);
const uint32*
uint32_frombytes(uint32* out, const uint8* in, size_t bytes);
/*
The output buffer defaults to NULL if not provided.
Generic function name supported on the non-uint8 type, except where said type
is unavailable because the argument for output buffer was not provided.
*/
#include "variadic.h"
#define uint16_tobytes_2(a, c) a, NULL, c
#define uint16_tobytes_3(a, b, c) a, b, c
#define uint16_tobytes(...) VARIADIC( uint16_tobytes, NUMARG3(__VA_ARGS__), __VA_ARGS__)
#define uint16_frombytes_2( b, c) NULL, b, c
#define uint16_frombytes_3(a, b, c) a, b, c
#define uint16_frombytes(...) VARIADIC(uint16_frombytes, NUMARG3(__VA_ARGS__), __VA_ARGS__)
#define uint32_tobytes_2(a, c) a, NULL, c
#define uint32_tobytes_3(a, b, c) a, b, c
#define uint32_tobytes(...) VARIADIC( uint32_tobytes, NUMARG3(__VA_ARGS__), __VA_ARGS__)
#define uint32_frombytes_2( b, c) NULL, b, c
#define uint32_frombytes_3(a, b, c) a, b, c
#define uint32_frombytes(...) VARIADIC(uint32_frombytes, NUMARG3(__VA_ARGS__), __VA_ARGS__)
#define tobytes(a, ...) _Generic((a), \
const uint16*: uint16_tobytes, \
const uint32*: uint32_tobytes) (VARIADIC2( uint32_tobytes, NUMARG3(a, __VA_ARGS__), a, __VA_ARGS__))
#define frombytes(a, ...) _Generic((a), \
uint16*: uint16_frombytes, \
uint32*: uint32_frombytes)(VARIADIC2(uint32_frombytes, NUMARG3(a, __VA_ARGS__), a, __VA_ARGS__))
また、_Genericと組み合わせることのできない可変個引数関数名の選択を使用します。
// winternitz() with 5 arguments is replaced with merkle_lamport() on those 5 arguments.
#define merkle_lamport_5(a, b, c, d, e) a, b, c, d, e
#define winternitz_7(a, b, c, d, e, f, g) a, b, c, d, e, f, g
#define winternitz_5_name() merkle_lamport
#define winternitz_7_name() winternitz
#define winternitz(...) NVARIADIC(winternitz, NUMARG7(__VA_ARGS__), __VA_ARGS__)
通常はありませんが、gccではfuncA()の最後のパラメーターをマクロでオプションにすることができます。
funcB()では、特別な値(-1)を使用して、「b」パラメーターのデフォルト値が必要であることを通知します。
#include <stdio.h>
int funcA( int a, int b, ... ){ return a+b; }
#define funcA( a, ... ) funcA( a, ##__VA_ARGS__, 8 )
int funcB( int a, int b ){
if( b == -1 ) b = 8;
return a+b;
}
int main(void){
printf("funcA(1,2): %i\n", funcA(1,2) );
printf("funcA(1): %i\n", funcA(1) );
printf("funcB(1, 2): %i\n", funcB(1, 2) );
printf("funcB(1,-1): %i\n", funcB(1,-1) );
}
はい
マクロを介して
3パラメータ:
#define my_func2(...) my_func3(__VA_ARGS__, 0.5)
#define my_func1(...) my_func2(__VA_ARGS__, 10)
#define VAR_FUNC(_1, _2, _3, NAME, ...) NAME
#define my_func(...) VAR_FUNC(__VA_ARGS__, my_func3, my_func2, my_func1)(__VA_ARGS__)
void my_func3(char a, int b, float c) // b=10, c=0.5
{
printf("a=%c; b=%d; c=%f\n", a, b, c);
}
4番目の引数が必要な場合は、my_func3を追加する必要があります。VAR_FUNC、my_func2、およびmy_funcの変更に注意してください
4パラメータ:
#define my_func3(...) my_func4(__VA_ARGS__, "default") // <== New function added
#define my_func2(...) my_func3(__VA_ARGS__, (float)1/2)
#define my_func1(...) my_func2(__VA_ARGS__, 10)
#define VAR_FUNC(_1, _2, _3, _4, NAME, ...) NAME
#define my_func(...) VAR_FUNC(__VA_ARGS__, my_func4, my_func3, my_func2, my_func1)(__VA_ARGS__)
void my_func4(char a, int b, float c, const char* d) // b=10, c=0.5, d="default"
{
printf("a=%c; b=%d; c=%f; d=%s\n", a, b, c, d);
}
唯一の例外は、float変数にデフォルト値を指定できないことです(3つのパラメーターの場合のように最後の引数である場合を除く)。これは、マクロ引数内で受け入れられないピリオド('。')が必要なためです。しかし、my_func2マクロ( 4つのパラメーターの場合)に見られるような回避策を理解できます
プログラム
int main(void)
{
my_func('a');
my_func('b', 20);
my_func('c', 200, 10.5);
my_func('d', 2000, 100.5, "hello");
return 0;
}
出力:
a=a; b=10; c=0.500000; d=default
a=b; b=20; c=0.500000; d=default
a=c; b=200; c=10.500000; d=default
a=d; b=2000; c=100.500000; d=hello
はい、何かを同時に行うことができます。ここでは、取得できるさまざまな引数リストを知っている必要がありますが、すべてを処理するための同じ関数があります。
typedef enum { my_input_set1 = 0, my_input_set2, my_input_set3} INPUT_SET;
typedef struct{
INPUT_SET type;
char* text;
} input_set1;
typedef struct{
INPUT_SET type;
char* text;
int var;
} input_set2;
typedef struct{
INPUT_SET type;
int text;
} input_set3;
typedef union
{
INPUT_SET type;
input_set1 set1;
input_set2 set2;
input_set3 set3;
} MY_INPUT;
void my_func(MY_INPUT input)
{
switch(input.type)
{
case my_input_set1:
break;
case my_input_set2:
break;
case my_input_set3:
break;
default:
// unknown input
break;
}
}
CだけでVARARGSを使用する必要はありません。例を次に示します。
int funcA_12(int a1, int a2) { ... }
#define funcA(a1) funcA_12(a1, 0)
この答えは、上記の2つの関数の方法と非常に似ていますが、この場合、引数を定義する関数名にマクロを使用しています。
https://github.com/cindRoberta/C/blob/master/structure/function/default_parameter.c
#include<stdio.h>
void f_impl(int a, float b) {
printf("%d %g\n", a, b);
}
#define f_impl(...) f_macro(__VA_ARGS__, 3.7)
#define f_macro(a, b, ...) f_impl(a, b)
int main(void) {
f_impl(1);
f_impl(1, 2, 3, 4);
return 0;
}
私はこれをより良い方法で行う方法を知っています。パラメータにNULLを割り当てるだけなので、値はありません。次に、パラメータ値がNULL
であるかどうかを確認し、デフォルト値に変更します。
void func(int x){
if(x == NULL)
x = 2;
....
}
ただし、警告が発生します。パラメータ値が次の場合、何もしない値を割り当てることをお勧めします。
void func(int x){
if(x == 1)
x = 2;
....
}
上記の例でx
は1
、関数がそれを2
;に変更します。
@ user904963のおかげで、編集:数値のすべての範囲をカバーする必要がある場合は、パラメーターをデフォルトに設定するかどうかを関数に伝えるだけで、別の引数を追加するのは難しくありません。
void func(int x, bool useDefault){
if(useDefault) //useDefault == true
x = 2;
....
}
ただし、含めることを忘れないでくださいstdbool.h
なぜこれができないのですか。
オプションの引数にデフォルト値を指定します。このように、関数の呼び出し元は必ずしも引数の値を渡す必要はありません。引数はデフォルト値を取ります。そして、簡単にその議論はクライアントにとってオプションになります。
例えば
void foo(int a、int b = 0);
ここで、bはオプションの引数です。