C의 함수에 기본 인수를 지정하는 방법이 있습니까?
C의 함수에 기본 인수를 지정하는 방법이 있습니까?
답변:
실제로는 아닙니다. 유일한 방법은 varargs 함수 를 작성하고 호출자가 전달하지 않는 인수의 기본값을 수동으로 채우는 것입니다.
open(2)
시스템 호출은 필수 인수에 따라 존재할 수 선택적 인수를 위해 이것을 사용하고, printf(3)
얼마나 많은 인수 지정이 될 것이라고 형식 문자열을 읽습니다. 둘 다 varargs를 매우 안전하고 효과적으로 사용하며 확실히 망칠 수는 있지만 printf()
특히 인기가있는 것 같습니다.
와, 모두가 여기 비관론자 야. 대답은 '예'입니다.
그것은 사소한 것이 아닙니다. 결국 우리는 핵심 함수, 지원 구조체, 래퍼 함수 및 래퍼 함수 주위의 매크로를 갖게 될 것입니다. 내 작품에는이 모든 것을 자동화하는 매크로 세트가있다. 흐름을 이해하면 같은 작업을 쉽게 수행 할 수 있습니다.
나는 이것을 다른 곳에 작성 했으므로 여기에 요약을 보완하는 자세한 외부 링크가 있습니다 : 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__});
이제 다음 모든 것이 작동합니다.
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
복합 이니셜 라이저가 정확한 규칙의 기본값을 설정하는 방법에 대한 규칙을 확인하십시오.
작동하지 않는 한 가지 : 결 f(0)
측값과 0을 구분할 수 없기 때문입니다. 내 경험상, 이것은 조심해야 할 것이지만, 필요가 발생할 때-기본값이 실제로 0 인 시간의 절반을 처리 할 수 있습니다.
명명 된 인수와 기본값이 실제로 C에서 코딩을 더 쉽고 재미있게 만든다고 생각하기 때문에 이것을 작성하는 데 어려움을 겪었습니다. 그리고 C는 매우 간단하고 여전히 모든 것을 가능하게하기에 충분합니다.
{}
(빈 이니셜 라이저) 오류 C99입니다.
#define vrange(...) CALL(range,(param){.from=1, .to=100, .step=1, __VA_ARGS__})
예. :-) 그러나 당신이 기대하는 방식이 아닙니다.
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는 메소드에 과부하를 허용하지 않으므로 두 가지 다른 함수가 생깁니다. 여전히 f2를 호출하면 실제로 기본값으로 f1을 호출하게됩니다. "반복하지 마십시오"솔루션으로 기존 코드를 복사 / 붙여 넣기하지 않아도됩니다.
기본값으로 명명 된 매개 변수 (전용)를 사용하는 함수를 만들 수 있습니다. 이것은 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));
사용자가 자신이 무엇을 작성해야할지 모른다면이 방법이 도움이 될 수 있습니다.
아니.
최신 C99 표준조차도이를 지원하지 않습니다.
짧은 대답 : 아니요.
약간 더 긴 대답 : 선택적 인수에 대해 구문 분석 하는 문자열을 전달 하는 오래되고 오래된 해결 방법이 있습니다 .
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
하고 발신자가 다른 옵션을 위해 필드를 채우고 주소로 전달하거나 NULL
기본 옵션으로 전달 하도록합니다.
아마도이 작업을 수행하는 가장 좋은 방법은 상황에 따라 가능하거나 그렇지 않을 수있는 C ++로 이동하여 '더 나은 C'로 사용하는 것입니다. 클래스, 템플릿, 연산자 오버로드 또는 기타 고급 기능을 사용하지 않고도 C ++을 사용할 수 있습니다.
이것은 함수 오버로딩과 기본 매개 변수 (및 당신이 선택한 다른 기능들)를 가진 C의 변형을 제공 할 것입니다. C ++의 제한된 하위 집합 만 사용하는 것이 진지하다면 약간의 훈련을 받아야합니다.
많은 사람들이 이런 식으로 C ++를 사용하는 것이 끔찍한 아이디어라고 말할 것입니다. 그러나 그것은 단지 의견 일뿐입니다. 나는 모든 것을 사지 않고도 편안하게 사용할 수있는 C ++의 기능을 사용하는 것이 타당하다고 생각합니다. C ++이 성공한 이유 중 상당 부분은 초기에 정확히 이런 방식으로 많은 프로그래머가 사용했기 때문이라고 생각합니다.
또 다른 옵션은 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);
아니,하지만 당신은 고려할 수 있습니다 설정 기본 인수를 사용하여 대략적인 기능 (또는 매크로)의를 :
// 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
}
예, C99의 기능으로이 작업을 수행 할 수 있습니다. 이것은 새로운 데이터 구조 등을 정의하지 않고 런타임에 호출 방법을 결정하지 않고 계산 오버 헤드없이 기능없이 작동합니다.
자세한 설명은 내 게시물을 참조하십시오
http://gustedt.wordpress.com/2010/06/03/default-arguments-for-c99/
젠스
일반적으로 아니요, 그러나 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) );
}
Jens Gustedt의 답변을 개선 하여 다음을 수행했습니다.
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__))
그리고 variadic 함수 이름을 선택하면 _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__)
예
매크로를 통해
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);
}
네 번째 인수를 원하면 추가 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);
}
부동 소수점 변수는 예외 ( 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;
}
}