C-구조체 내부의 함수


82

구조 내부에 함수를 만들려고하는데 지금까지 다음 코드가 있습니다.

typedef struct client_t client_t, *pno;
struct client_t
{
        pid_t pid;
        char password[TAM_MAX]; // -> 50 chars
        pno next;

        pno AddClient() 

        {
            /* code */
        }

};

int main()
{

    client_t client;

    //code ..

    client.AddClient();

}

오류 : client.h : 24 : 2 : 오류 : '{'토큰 앞에 ' :', ',', ';', '}'또는 ' attribute '가 있어야합니다.

올바른 방법은 무엇입니까?


12
C의 구조체에는 함수를 사용할 수 없습니다. 함수 포인터로 대략적으로 시뮬레이션 할 수 있습니다.
Fingolfin

1
함수 포인터가 허용 가능한 대체물입니까? stackoverflow.com/a/840703/635678
Dan O '

답변:


104

직접 수행 할 수는 없지만 함수 포인터를 사용하고 "this"매개 변수를 명시 적으로 전달하여 동일한 것을 에뮬레이션 할 수 있습니다.

typedef struct client_t client_t, *pno;
struct client_t
{
        pid_t pid;
        char password[TAM_MAX]; // -> 50 chars
        pno next;

        pno (*AddClient)(client_t *);    
};

pno client_t_AddClient(client_t *self) { /* code */ }

int main()
{

    client_t client;
    client.AddClient = client_t_AddClient; // probably really done in some init fn

    //code ..

    client.AddClient(&client);

}

그러나 이것을하는 것은 당신에게 정말로 많은 것을 사지 않는다는 것이 밝혀졌습니다. 따라서 외부 함수를 호출하고 인스턴스를 전달할 수 있기 때문에이 스타일로 구현 된 C API가 많지 않습니다.


3
X11 API는 이와 같은 일을합니다. XImage를 참조하십시오 .
Hydranix

1
Windows API에있는 코드는 그것으로 가득 차 있습니다. 기술적으로 "윈도우 클래스"는 두 개의 콜백 함수 포인터와 오픈 엔드가있는 구조체입니다 (등록하는 동안 제공 할 수있는 "윈도우 클래스 특정 데이터"에 대해)
Swift-Friday Pie

1
해당 단계를 따랐지만 struct.function = newFunction을 수행 하면 컴파일러가 인쇄합니다. error : expected '=', ',', ';', 'asm'또는 ' attribute'before '.' 토큰
Bonfra

이것은 또한 읽기 기능과 쓰기 기능이 있지만 한 유형의 하드 드라이브에서 nexf로 다른 공통 대상 (예 : 하드 드라이브)을 원할 때 유용합니다.
Menotdan

런타임에 실제 구현을 선택하면 많은 것을 살 수 있습니다. 정적 함수 만있는 경우 모든 함수 호출에서 항상 해당 함수 내에서 다시 선택해야합니다. 함수 포인터를 사용하면 선택이 한 번만 발생합니다. 그리고 선택은 구조체의 수명 내내 변경 될 수도 있습니다.
Mecki

23

다른 사람들이 언급했듯이 구조 내부에 함수 포인터를 직접 포함하는 것은 일반적으로 콜백 함수와 같은 특수 목적을 위해 예약되어 있습니다.

아마도 원하는 것은 가상 메소드 테이블과 비슷합니다.

typedef struct client_ops_t client_ops_t;
typedef struct client_t client_t, *pno;

struct client_t {
    /* ... */
    client_ops_t *ops;
};

struct client_ops_t {
    pno (*AddClient)(client_t *);
    pno (*RemoveClient)(client_t *);
};

pno AddClient (client_t *client) { return client->ops->AddClient(client); }
pno RemoveClient (client_t *client) { return client->ops->RemoveClient(client); }

이제 더 많은 작업을 추가해도 client_t구조 의 크기가 변경되지 않습니다 . 이제 이러한 종류의 유연성은 여러 종류의 클라이언트를 정의해야하거나 client_t인터페이스 사용자가 작업 동작 방식을 확장 할 수 있도록하려는 경우에만 유용 합니다.

이런 종류의 구조는 실제 코드에 나타납니다. OpenSSL BIO 계층은 이와 유사하며 UNIX 장치 드라이버 인터페이스에도 이와 같은 계층이 있습니다.


14

이것은 C ++에서만 작동합니다. 구조체의 함수는 C의 기능이 아닙니다.

당신의 client.AddClient (); call ... 이것은 객체 지향 프로그래밍, 즉 C ++ 인 멤버 함수에 대한 호출입니다.

소스를 .cpp 파일로 변환하고 그에 따라 컴파일하고 있는지 확인하십시오.

C를 고수해야하는 경우 아래 코드는 동등한 것입니다.

typedef struct client_t client_t, *pno;
struct client_t
{
        pid_t pid;
        char password[TAM_MAX]; // -> 50 chars
        pno next;

};


pno AddClient(pno *pclient) 
{
    /* code */
}


int main()
{

    client_t client;

    //code ..

    AddClient(client);

}

1
이것은 유니 프로젝트이고 C를 사용해야합니다. C에서 원하는 것을 완성 할 수있는 방법이 있습니까?
xRed

해당 함수를 구조체 외부로 이동하고 인스턴스에 대한 포인터를 받아들이도록합니다.
user123

client.AddClient () 일은 약간의 복잡한 마법 없이는 불가능합니다 (다른 답변 참조).
QSQ

12

이건 어때?

#include <stdio.h>

typedef struct hello {
    int (*someFunction)();
} hello;

int foo() {
    return 0;
}

hello Hello() {
    struct hello aHello;
    aHello.someFunction = &foo;
    return aHello;
}

int main()
{
    struct hello aHello = Hello();
    printf("Print hello: %d\n", aHello.someFunction());

    return 0;
} 

솔루션과 같은 "조금"JavaScrip
Bitman

7

구조체에 따라 코드를 그룹화하려고합니다. C 그룹은 파일 단위입니다. 모든 함수와 내부 변수를 헤더 또는 헤더와 ac 소스 파일에서 컴파일 된 개체 ".o"파일에 넣습니다.

객체 지향 언어가 아닌 C 프로그램에 대해 객체 지향을 처음부터 다시 만들 필요는 없습니다.

나는 이것을 전에 본 적이 있습니다. 이상한 일입니다. 코더 중 일부는 변경하려는 객체를 변경하는 함수로 전달하는 것을 싫어합니다.

클래스 개체가 항상 멤버 함수의 첫 번째 매개 변수라는 사실을 숨겼지만 숨겨져 있기 때문에 C ++를 비난합니다. 따라서 객체가 함수에 전달 되어도 함수에 전달되지 않는 것처럼 보입니다.

Client.addClient(Client& c); // addClient first parameter is actually 
                             // "this", a pointer to the Client object.

C는 유연하며 참조로 전달 될 수 있습니다.

AC 함수는 종종 상태 바이트 또는 정수만 반환하며 종종 무시됩니다. 귀하의 경우 적절한 양식은

 err = addClient( container_t  cnt, client_t c);
 if ( err != 0 )
   { fprintf(stderr, "could not add client (%d) \n", err ); 

addClient는 Client.h 또는 Client.c에 있습니다.


접근 방식에는 장단점이 있습니다. 그건 그냥 있기 때문에 그것은 항상 완료되었습니다 방법 비 OO (을 모두 참조 do(object, subject))와 OO ( subject.do(object)) 배열 기능의 전략, 우리는 그것을 시도 중단해야 의미하지 않는다. 저는 이것이 C가 작성된 역사적인 방식이 아니며 C가 이런 방식으로 작성 될 필요 가 없다는 점을 지적하는 것이 타당하다고 생각합니다 (많은 언어, 특히 절차 적, 기능적 또는 논리 패러다임의 경우). 하지만 우리는 패턴을 적극적으로 낙담시킬 필요가 없습니다. 몇 가지 혜택도 함께 제공됩니다
hiljusti
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.