web-dev-qa-db-ja.com

C-構造体内の関数

構造体内に関数を作成しようとしていますが、これまでのところ次のコードがあります:

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:エラー:予想される「:」、「、」、「;」、「}」または「属性 '' {'トークンの前。

正しい方法はどれですか?

67
xRed

直接行うことはできませんが、関数ポインターを使用して「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の多くは表示されません。

91
FatalError

他の人が指摘したように、関数ポインターを構造内に直接埋め込むことは、通常、コールバック関数のような特別な目的のために予約されています。

おそらく必要なのは、仮想メソッドテーブルのようなものです。

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デバイスドライバーインターフェイスにもこのようなレイヤーがあります。

21
jxh

これは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);

}
12
QSQ

これはどう?

#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;
} 
10
rogal

構造に従ってコードをグループ化しようとしています。 Cのグループ化はファイルごとです。すべての関数と内部変数をヘッダーまたはヘッダーとcソースファイルからコンパイルされたオブジェクト「.o」ファイルに入れます。

オブジェクト指向言語ではないCプログラムでは、オブジェクト指向をゼロから作り直す必要はありません。

これは前に見たことがあります。奇妙なことです。一部のコーダーは、変更したいオブジェクトを、それを変更するための標準的な方法ですが、変更する関数に渡すことを嫌います。

クラスオブジェクトが常にメンバー関数の最初のパラメーターであるという事実を隠したが、隠されているため、私はC++を非難します。そのため、オブジェクトが関数に渡されているように見えますが、そうではありません。

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

Cは柔軟性があり、参照によって渡すことができます。

C関数は多くの場合、ステータスバイトまたはintのみを返しますが、これは多くの場合無視されます。あなたの場合、適切な形式は

 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にあります

4
Chris Reid