C에서 기능 과부하를 달성하는 방법?
C에서 기능 과부하를 달성할 수 있는 방법은 없을까?나는 이렇게 과부하되는 간단한 기능들을 보고 있다.
foo (int a)
foo (char b)
foo (float c , int d)
나는 직진할 방법이 없다고 생각한다; 나는 해결책이 존재한다면 찾고 있다.
네!
이 질문을 받은 이후로, 표준 C(연장 없음)는 (운영자가 아닌) 기능 과부하에 대한 지원을 효과적으로 획득했다._Generic
(9에서 지원C11의 워드((버 4.9 이에 GCC에 있음)
(과부하는 질문에서 보여지는 패션에서 진정으로 "빌트인" 것은 아니지만, 그렇게 작동하는 것을 구현하는 것은 아주 쉽다.)
_Generic
동일한 패밀리의 컴파일 시간 연산자sizeof
그리고_Alignof
표준 섹션 6.5.1.1에 설명되어 있다.두 가지 주요 매개변수, 즉 표현식(런타임에 평가되지 않음)과 약간 닮은 유형/표현 연관 리스트를 수용한다.switch
막다_Generic
전체 유형의 식을 가져온 다음 목록에서 해당 유형의 최종 결과 식을 선택하기 위해 식에서 "수집"을 선택하십시오.
_Generic(1, float: 2.0,
char *: "2",
int: 2,
default: get_two_object());
위의 표현식은 다음을 평가한다.2
는 - 제어 의의 은.int
, 그래서 그것은 관련된 표현을 선택한다.int
그 당시는 이 것이 있지 이것의 어떤 것도 런타임에 남아 있지 않다.(더)default
절은 선택 사항이다. 절은 해제하고 유형이 일치하지 않으면 컴파일 오류가 발생한다.)
이것이 기능 과부하 시 유용한 방법은 C 전처리기사가 삽입하여 제어 매크로에 전달된 인수의 유형을 기반으로 결과식을 선택할 수 있다는 것이다.따라서(C 표준에서 예시):
#define cbrt(X) _Generic((X), \
long double: cbrtl, \
default: cbrt, \
float: cbrtf \
)(X)
이 매크로는 과부하된 것을 구현한다.cbrt
연산: 인수 유형을 매크로로 발송하고 적절한 구현 함수를 선택한 다음 원래 매크로 인수를 해당 함수에 전달함.
따라서 본래의 예를 구현하기 위해 다음과 같은 작업을 수행할 수 있다.
foo_int (int a)
foo_char (char b)
foo_float_int (float c , int d)
#define foo(_1, ...) _Generic((_1), \
int: foo_int, \
char: foo_char, \
float: _Generic((FIRST(__VA_ARGS__,)), \
int: foo_float_int))(_1, __VA_ARGS__)
#define FIRST(A, ...) A
이 경우 우리는 a를 사용할 수 있었다.default:
세 번째 사례에 대한 연관성, 그러나 그것은 그 원칙을 여러 주장으로 확장하는 방법을 보여주지 않는다.최종 결과는 당신이 사용할 수 있다는 것이다.foo(...)
코드의 유형에 대해 걱정하지 마십시오([1]).
더 복잡한 상황(예: 더 많은 수의 인수를 과부하하는 함수 또는 다양한 수의 함수)의 경우 유틸리티 매크로를 사용하여 자동으로 정적 디스패치 구조를 생성할 수 있다.
void print_ii(int a, int b) { printf("int, int\n"); }
void print_di(double a, int b) { printf("double, int\n"); }
void print_iii(int a, int b, int c) { printf("int, int, int\n"); }
void print_default(void) { printf("unknown arguments\n"); }
#define print(...) OVERLOAD(print, (__VA_ARGS__), \
(print_ii, (int, int)), \
(print_di, (double, int)), \
(print_iii, (int, int, int)) \
)
#define OVERLOAD_ARG_TYPES (int, double)
#define OVERLOAD_FUNCTIONS (print)
#include "activate-overloads.h"
int main(void) {
print(44, 47); // prints "int, int"
print(4.4, 47); // prints "double, int"
print(1, 2, 3); // prints "int, int, int"
print(""); // prints "unknown arguments"
}
(여기 구현) 그래서 어느 정도의 노력으로 보일러 판의 양을 과부하에 대한 네이티브 지원이 있는 언어와 거의 비슷하게 줄일 수 있다.
게다가 C99에서는 (유형이 아닌) 인수의 과부하가 이미 가능했다.
그러나 C가 유형을 평가하는 방법은 여러분을 혼란스럽게 할 수 있다는 점에 유의한다.이것은 선택될 것이다.foo_int
예를 들어 문자 그대로 전달하려고 할 때 문자열 리터럴을 지원하기 위해 과부하를 원할 경우 약간 혼란스러워야 한다.그래도 전체적으로 꽤 괜찮아.
가능성은 거의 없다.
- printf 스타일 함수(인수로 유형)
- opengl 스타일 함수(함수 이름에 유형)
- c c++의 부분 집합(c++ 컴파일러를 사용할 수 있는 경우)
다음은 내가 C:에서 과부하 기능을 입증하는 가장 명확하고 간결한 예다.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int addi(int a, int b) {
return a + b;
}
char *adds(char *a, char *b) {
char *res = malloc(strlen(a) + strlen(b) + 1);
strcpy(res, a);
strcat(res, b);
return res;
}
#define add(a, b) _Generic(a, int: addi, char*: adds)(a, b)
int main(void) {
int a = 1, b = 2;
printf("%d\n", add(a, b)); // 3
char *c = "hello ", *d = "world";
printf("%s\n", add(c, d)); // hello world
return 0;
}
https://gist.github.com/barosl/e0af4a92b2b8cabd05a7
이미 말한 바와 같이, 당신이 의미하는 과부하는 것은 C에 의해 지지되지 않는다.문제를 해결하기 위한 일반적인 관용어는 함수가 태그가 붙은 결합을 받아들이도록 하는 것이다.이것은 에 의해 구현된다.struct
매개변수, 여기서struct
그 자체는 다음과 같은 유형 지표로 구성된다.enum
, 그리고 aunion
다양한 유형의 값.예:
#include <stdio.h>
typedef enum {
T_INT,
T_FLOAT,
T_CHAR,
} my_type;
typedef struct {
my_type type;
union {
int a;
float b;
char c;
} my_union;
} my_struct;
void set_overload (my_struct *whatever)
{
switch (whatever->type)
{
case T_INT:
whatever->my_union.a = 1;
break;
case T_FLOAT:
whatever->my_union.b = 2.0;
break;
case T_CHAR:
whatever->my_union.c = '3';
}
}
void printf_overload (my_struct *whatever) {
switch (whatever->type)
{
case T_INT:
printf("%d\n", whatever->my_union.a);
break;
case T_FLOAT:
printf("%f\n", whatever->my_union.b);
break;
case T_CHAR:
printf("%c\n", whatever->my_union.c);
break;
}
}
int main (int argc, char* argv[])
{
my_struct s;
s.type=T_INT;
set_overload(&s);
printf_overload(&s);
s.type=T_FLOAT;
set_overload(&s);
printf_overload(&s);
s.type=T_CHAR;
set_overload(&s);
printf_overload(&s);
}
네가 말하는 뜻은 - 아니, 넌 할 수 없어.
선언할 수 있음va_arg
처럼 기능하다.
void my_func(char* format, ...);
그러나 첫 번째 변수에서 변수의 수와 변수의 유형에 대한 정보를 전달해야 할 것이다.printf()
한다
다음의 접근방식은 a2800276과 유사하지만 C99 매크로 마법이 추가되었다.
// we need `size_t`
#include <stddef.h>
// argument types to accept
enum sum_arg_types { SUM_LONG, SUM_ULONG, SUM_DOUBLE };
// a structure to hold an argument
struct sum_arg
{
enum sum_arg_types type;
union
{
long as_long;
unsigned long as_ulong;
double as_double;
} value;
};
// determine an array's size
#define count(ARRAY) ((sizeof (ARRAY))/(sizeof *(ARRAY)))
// this is how our function will be called
#define sum(...) _sum(count(sum_args(__VA_ARGS__)), sum_args(__VA_ARGS__))
// create an array of `struct sum_arg`
#define sum_args(...) ((struct sum_arg []){ __VA_ARGS__ })
// create initializers for the arguments
#define sum_long(VALUE) { SUM_LONG, { .as_long = (VALUE) } }
#define sum_ulong(VALUE) { SUM_ULONG, { .as_ulong = (VALUE) } }
#define sum_double(VALUE) { SUM_DOUBLE, { .as_double = (VALUE) } }
// our polymorphic function
long double _sum(size_t count, struct sum_arg * args)
{
long double value = 0;
for(size_t i = 0; i < count; ++i)
{
switch(args[i].type)
{
case SUM_LONG:
value += args[i].value.as_long;
break;
case SUM_ULONG:
value += args[i].value.as_ulong;
break;
case SUM_DOUBLE:
value += args[i].value.as_double;
break;
}
}
return value;
}
// let's see if it works
#include <stdio.h>
int main()
{
unsigned long foo = -1;
long double value = sum(sum_long(42), sum_ulong(foo), sum_double(1e10));
printf("%Le\n", value);
return 0;
}
Leushenko의 대답은 정말 멋지다 - 오로지: the others.foo
예제가 GCC로 컴파일되지 않음, 다음에서 실패함foo(7)
, 비틀거리면서FIRST
매크로 및 실제 함수 호출((_1, __VA_ARGS__)
의 쉼표로 은.게다가, 만약 우리가 다음과 같은 추가적인 과부하를 제공하기를 원한다면 우리는 곤란하다.foo(double)
.
그래서 나는 공허한 과부하를 허용하는 것을 포함하여 그 대답을 조금 더 자세히 설명하기로 했다.foo(void)
- 상당한 문제를 일으킨...)
현재 아이디어는 다음과 같다: 다른 매크로에서 둘 이상의 일반 매크로를 정의하고 인수의 수에 따라 올바른 매크로를 선택하라!
다음과 같은 대답에 근거하여 논쟁의 수는 꽤 쉽다.
#define foo(...) SELECT(__VA_ARGS__)(__VA_ARGS__)
#define SELECT(...) CONCAT(SELECT_, NARG(__VA_ARGS__))(__VA_ARGS__)
#define CONCAT(X, Y) CONCAT_(X, Y)
#define CONCAT_(X, Y) X ## Y
잘됐네, 어느 쪽이든 하기로 하지SELECT_1
또는SELECT_2
(또는 더 많은 논쟁을 원하거나, 원하면/해결하면) 따라서 우리는 단지 다음과 같은 적절한 정의가 필요하다.
#define SELECT_0() foo_void
#define SELECT_1(_1) _Generic ((_1), \
int: foo_int, \
char: foo_char, \
double: foo_double \
)
#define SELECT_2(_1, _2) _Generic((_1), \
double: _Generic((_2), \
int: foo_double_int \
) \
)
좋아, 이미 보이드 과부하를 추가했어. 하지만, 이 것은 사실 C 표준에서 다루지 않아, 빈 가변적 인수를 허용하지 않아. 즉, 우리는 컴파일러 확장에 의존해!
맨 처음에 비어 있는 매크로 통화 (foo()
은 빈을 생산한다은 여전히 토큰을 생산하지만, 빈 토큰을 생산한다.그래서 개표 매크로가 실제로 빈 매크로 호출에도 0 대신 1을 돌려준다.다음에 쉼표를 붙이면 이 문제를 "쉽게" 없앨 수 있다.__VA_ARGS__
리스트가 비어 있는지 여부에 따라 조건부로 다음을 수행하십시오.
#define NARG(...) ARG4_(__VA_ARGS__ COMMA(__VA_ARGS__) 4, 3, 2, 1, 0)
그것은 쉬워 보였지만.COMMA
매크로가 꽤 무거운 것이다. 다행히도, 주제는 이미 Jens Gustedt(고마워 Jens)의 블로그에서 다루고 있다.기본 요령은 괄호 뒤에 따르지 않으면 함수 매크로가 확장되지 않는다는 것이다, 자세한 설명을 위해 옌스의 블로그를 살펴보라...우리는 단지 매크로를 우리의 필요에 맞게 약간만 수정하면 된다(단순히 이름을 사용하고 간결함을 주장하지 않겠다).
#define ARGN(...) ARGN_(__VA_ARGS__)
#define ARGN_(_0, _1, _2, _3, N, ...) N
#define HAS_COMMA(...) ARGN(__VA_ARGS__, 1, 1, 1, 0)
#define SET_COMMA(...) ,
#define COMMA(...) SELECT_COMMA \
( \
HAS_COMMA(__VA_ARGS__), \
HAS_COMMA(__VA_ARGS__ ()), \
HAS_COMMA(SET_COMMA __VA_ARGS__), \
HAS_COMMA(SET_COMMA __VA_ARGS__ ()) \
)
#define SELECT_COMMA(_0, _1, _2, _3) SELECT_COMMA_(_0, _1, _2, _3)
#define SELECT_COMMA_(_0, _1, _2, _3) COMMA_ ## _0 ## _1 ## _2 ## _3
#define COMMA_0000 ,
#define COMMA_0001
#define COMMA_0010 ,
// ... (all others with comma)
#define COMMA_1111 ,
그리고 이제 우린 괜찮아...
하나의 블록에 있는 전체 코드:
/*
* demo.c
*
* Created on: 2017-09-14
* Author: sboehler
*/
#include <stdio.h>
void foo_void(void)
{
puts("void");
}
void foo_int(int c)
{
printf("int: %d\n", c);
}
void foo_char(char c)
{
printf("char: %c\n", c);
}
void foo_double(double c)
{
printf("double: %.2f\n", c);
}
void foo_double_int(double c, int d)
{
printf("double: %.2f, int: %d\n", c, d);
}
#define foo(...) SELECT(__VA_ARGS__)(__VA_ARGS__)
#define SELECT(...) CONCAT(SELECT_, NARG(__VA_ARGS__))(__VA_ARGS__)
#define CONCAT(X, Y) CONCAT_(X, Y)
#define CONCAT_(X, Y) X ## Y
#define SELECT_0() foo_void
#define SELECT_1(_1) _Generic ((_1), \
int: foo_int, \
char: foo_char, \
double: foo_double \
)
#define SELECT_2(_1, _2) _Generic((_1), \
double: _Generic((_2), \
int: foo_double_int \
) \
)
#define ARGN(...) ARGN_(__VA_ARGS__)
#define ARGN_(_0, _1, _2, N, ...) N
#define NARG(...) ARGN(__VA_ARGS__ COMMA(__VA_ARGS__) 3, 2, 1, 0)
#define HAS_COMMA(...) ARGN(__VA_ARGS__, 1, 1, 0)
#define SET_COMMA(...) ,
#define COMMA(...) SELECT_COMMA \
( \
HAS_COMMA(__VA_ARGS__), \
HAS_COMMA(__VA_ARGS__ ()), \
HAS_COMMA(SET_COMMA __VA_ARGS__), \
HAS_COMMA(SET_COMMA __VA_ARGS__ ()) \
)
#define SELECT_COMMA(_0, _1, _2, _3) SELECT_COMMA_(_0, _1, _2, _3)
#define SELECT_COMMA_(_0, _1, _2, _3) COMMA_ ## _0 ## _1 ## _2 ## _3
#define COMMA_0000 ,
#define COMMA_0001
#define COMMA_0010 ,
#define COMMA_0011 ,
#define COMMA_0100 ,
#define COMMA_0101 ,
#define COMMA_0110 ,
#define COMMA_0111 ,
#define COMMA_1000 ,
#define COMMA_1001 ,
#define COMMA_1010 ,
#define COMMA_1011 ,
#define COMMA_1100 ,
#define COMMA_1101 ,
#define COMMA_1110 ,
#define COMMA_1111 ,
int main(int argc, char** argv)
{
foo();
foo(7);
foo(10.12);
foo(12.10, 7);
foo((char)'s');
return 0;
}
이것은 전혀 도움이 되지 않을 수 있지만, 만약 당신이 clang을 사용하고 있다면 당신은 과부하할 수 있는 속성을 사용할 수 있다 - 이것은 C로 컴파일할 때에도 작동한다.
http://clang.llvm.org/docs/AttributeReference.html#overloadable
헤더
extern void DecodeImageNow(CGImageRef image, CGContextRef usingContext) __attribute__((overloadable));
extern void DecodeImageNow(CGImageRef image) __attribute__((overloadable));
실행
void __attribute__((overloadable)) DecodeImageNow(CGImageRef image, CGContextRef usingContext { ... }
void __attribute__((overloadable)) DecodeImageNow(CGImageRef image) { ... }
C++만 쓰고 이 C++만 빼고 다른 C++ 기능은 다 쓰면 안 되는 거야?
만약 여전히 엄격한 C가 아니라면 나는 대신 다양한 기능을 추천할 것이다.
응, 그런 것 같아.
여기 예를 들어보자:
void printA(int a){
printf("Hello world from printA : %d\n",a);
}
void printB(const char *buff){
printf("Hello world from printB : %s\n",buff);
}
#define Max_ITEMS() 6, 5, 4, 3, 2, 1, 0
#define __VA_ARG_N(_1, _2, _3, _4, _5, _6, N, ...) N
#define _Num_ARGS_(...) __VA_ARG_N(__VA_ARGS__)
#define NUM_ARGS(...) (_Num_ARGS_(_0, ## __VA_ARGS__, Max_ITEMS()) - 1)
#define CHECK_ARGS_MAX_LIMIT(t) if(NUM_ARGS(args)>t)
#define CHECK_ARGS_MIN_LIMIT(t) if(NUM_ARGS(args)
#define print(x , args ...) \
CHECK_ARGS_MIN_LIMIT(1) printf("error");fflush(stdout); \
CHECK_ARGS_MAX_LIMIT(4) printf("error");fflush(stdout); \
({ \
if (__builtin_types_compatible_p (typeof (x), int)) \
printA(x, ##args); \
else \
printB (x,##args); \
})
int main(int argc, char** argv) {
int a=0;
print(a);
print("hello");
return (EXIT_SUCCESS);
}
0번 출력하고 안녕..인쇄A와 인쇄B에서.
만약 컴파일러가 gcc이고 새로운 과부하를 추가할 때마다 손 업데이트를 하는 것을 마다하지 않는다면 매크로 마법을 좀 부릴 수 있고, 전화 걸기 측면에서 원하는 결과를 얻을 수 있다면, 쓰는 것은 그렇게 좋지 않다...하지만 그것은 가능하다.
_builtin_builtin_built_p를 보고 나서, 그것을 이용하여 다음과 같은 일을 하는 매크로를 정의한다.
#define foo(a) \
((__builtin_types_compatible_p(int, a)?foo(a):(__builtin_types_compatible_p(float, a)?foo(a):)
하지만 넌 고약해, 그냥 그러지 마.
편집: C1X는 다음과 같은 유형의 일반 표현식에 대한 지원을 받게 될 것이다.
#define cbrt(X) _Generic((X), long double: cbrtl, \
default: cbrt, \
float: cbrtf)(X)
보통 이름 앞에 종류를 나타내는 사마귀가 붙거나 붙는다.매크로를 피할 수 있는 경우도 있지만, 그것은 오히려 당신이 무엇을 하려고 하는가에 달려 있다.C에는 다형증이 없고 강요만 있을 뿐이다.
매크로로 간단한 일반 작업 수행:
#define max(x,y) ((x)>(y)?(x):(y))
컴파일러가 유형화를 지원하는 경우 보다 복잡한 작업을 매크로에 넣을 수 있다.그런 다음 동일한 작동을 지원하는 기호 foo(x)를 다른 유형으로 설정할 수 있지만, 서로 다른 과부하 간의 동작을 변경할 수는 없다.매크로가 아닌 실제 기능을 원할 경우 이름에 유형을 붙여넣고 두 번째 붙여넣기를 사용하여 액세스(해 본 적이 없음)할 수 있다.
아래 코드는 기능 과부하 이해에 도움이 되길 바란다.
#include <stdio.h>
#include<stdarg.h>
int fun(int a, ...);
int main(int argc, char *argv[]){
fun(1,10);
fun(2,"cquestionbank");
return 0;
}
int fun(int a, ...){
va_list vl;
va_start(vl,a);
if(a==1)
printf("%d",va_arg(vl,int));
else
printf("\n%s",va_arg(vl,char *));
}
다음 기능을 다음과 같이 선언해 보십시오.extern "C++"
컴파일러가 이를 지원하는 경우 http://msdn.microsoft.com/en-us/library/s6y4zxec(VS.80).aspx
참조URL: https://stackoverflow.com/questions/479207/how-to-achieve-function-overloading-in-c
'IT이야기' 카테고리의 다른 글
Vuejs 모듈 없음 오류 웹 팩을 사용하여 '@/구성 요소/ 오류를 해결할 수 없음 (0) | 2022.04.17 |
---|---|
Vue를 통해 모듈식 수입 확대 (0) | 2022.04.17 |
Oracle JDK와 OpenJDK의 차이점 (0) | 2022.04.17 |
서로 다른 .c 파일 간에 변수를 공유하려면 어떻게 해야 하는가? (0) | 2022.04.17 |
리눅스 c 프로그램에서 pthread의 스레드 ID를 얻는 방법? (0) | 2022.04.17 |