10

複数ある場合enum、例:

 enum Greetings{ hello, bye, how };

 enum Testing { one, two, three };

正しい使用法を強制するにはどうすればよいenumですか?たとえば、デバッグと読みやすさを向上させるために、誰かが使用helloする必要があるときに使用したくありません。one

4

6 に答える 6

15

Cでは、ボイラープレートコードを使用して偽造できます。

typedef enum { HELLO_E, GOODBYE_E } greetings_t;
struct greetings { greetings_t greetings; };
#define HELLO ((struct greetings){HELLO_E})
#define GOODBYE ((struct greetings){GOODBYE_E})

typedef enum { ONE_E, TWO_E } number_t;
struct number { number_t number; };
#define ONE ((struct number){ONE_E})
#define TWO ((struct number){TWO_E})

void takes_greeting(struct greetings g);
void takes_number(struct number n);

void test()
{
    takes_greeting(HELLO);
    takes_number(ONE);

    takes_greeting(TWO);
    takes_number(GOODBYE);
}

これによりオーバーヘッドが発生することはなく、警告の代わりにエラーが発生します。

$ gcc -c -std = c99 -Wall -Wextra test2.c
test2.c:関数'test'内:
test2.c:19:エラー:「takes_greeting」の引数1に互換性のないタイプ
test2.c:20:エラー:「takes_number」の引数1に互換性のないタイプ

私はGNU拡張機能を使用しておらず、誤った警告は生成されないことに注意してください。エラーのみ。また、汚れと同じくらい古いバージョンのGCCを使用していることにも注意してください。

$ gcc --version
powerpc-apple-darwin9-gcc-4.0.1(GCC)4.0.1(Apple Inc.ビルド5493)
Copyright(C)2005 Free Software Foundation、Inc.
これは自由ソフトウェアです。コピー条件については、ソースを参照してください。ありません
保証; 商品性や特定の目的への適合性についてもそうではありません。

これは、C99の複合リテラルをサポートするすべてのコンパイラで機能するはずです。

于 2013-02-11T23:09:23.630 に答える
11

Clangは次の警告を生成します。これはあなたができる最善の方法です(ただし、ユーザーは警告をエラーにアップグレードできます)。

enum Greetings { hello, bye, how };
enum Count { one, two, three };

void takes_greeting(enum Greetings x) {}
void takes_count(enum Count x) {}

int main() {
    takes_greeting(one);
    takes_count(hello);
}

コンパイラ出力:

cc     foo.c   -o foo
foo.c:8:17: warning: implicit conversion from enumeration type 'enum Count' to different enumeration type 'enum Greetings' [-Wenum-conversion]
        takes_greeting(one);
        ~~~~~~~~~~~~~~ ^~~
foo.c:9:14: warning: implicit conversion from enumeration type 'enum Greetings' to different enumeration type 'enum Count' [-Wenum-conversion]
        takes_count(hello);
        ~~~~~~~~~~~ ^~~~~

ユーザーがコンパイラーからのエラーや警告を無視しようとしている場合、ユーザーを支援するためにできることはあまりありません。

于 2013-02-11T22:48:54.863 に答える
4

残念ながらenum、Cの型システムの弱点です。型の変数はenumそのenum型ですが、で宣言する定数enumは型intです。

だからあなたの例では

enum Greetings{ hello, bye, how };
enum Testing { one, two, three };

enum Greetings const holla = hello;
enum Testing const eins = one;

hellooneは、同じ値、つまり0、と同じタイプの2つの名前ですint

hollaeins再び値0がありますが、それぞれのタイプがあります。

「実際の」定数、つまり必要なタイプと値を持つエンティティに対して「公式」タイプの安全性を強制したい場合は、より複雑な構造を使用する必要があります。

#define GREETING(VAL) ((enum Greetings){ 0 } = (VAL))
#define HELLO GREETING(hello)

マクロでの割り当てGREETINGにより、結果が「右辺値」になることが保証されるため、変更することはできず、コンパイラーはその型と値のみを取得します。

于 2013-02-11T23:09:20.267 に答える
3

これはあなたが聞きたくない答えです。Cでは、実際にはできません。これで、CコードがC ++の「CleanC」サブセットに含まれている場合、C ++コンパイラでコンパイルして、間違ったenum/int値などを使用した場合のすべてのエラーを取得できます。

于 2013-02-11T22:59:48.757 に答える
2

有効な範囲も確保したい場合は、整数値を取得するためのポインター逆参照のわずかなオーバーヘッドと、多くの定型文の入力を伴う手法があります。それ以外の場合に必要となる範囲チェックコードを作成することからあなたを活用するので、それはまだ役に立つかもしれません。

Greetings.h:

#ifndef GREETINGS_H
#define GREETINGS_H

struct greetings;
typedef struct greetings Greetings;

extern const Greetings * const Greetings_hello;
extern const Greetings * const Greetings_bye;
extern const Greetings * const Greetings_how;

const char *Greetings_str(const Greetings *g);
int Greetings_int(const Greetings *g);

#endif

Greetings.c:

#include "greetings.h"

struct greetings {
    const int val;
};

static const Greetings hello = { 0 };
static const Greetings bye = { 1 };
static const Greetings how = { 2 };

const Greetings * const Greetings_hello = &hello;
const Greetings * const Greetings_bye = &bye;
const Greetings * const Greetings_how = &how;

static const char * const Greetings_names[] = {
    "hello",
    "bye",
    "how"
};

const char *
Greetings_str(const Greetings *g)
{
    return Greetings_names[g->val];
}

int
Greetings_int(const Greetings *g)
{
    return g->val;
}

main.cの例:

#include <stdio.h>
#include "greetings.h"

void
printTest(const Greetings *greeting)
{
    if (greeting == Greetings_how) return;
    puts(Greetings_str(greeting));
}

int
main()
{
    const Greetings *g = Greetings_hello;
    printTest(g);
}

はい、タイプするのはたくさんですが、完全なタイプと範囲の安全性が得られます。他のコンパイルユニットはインスタンス化できないstruct greetingsため、完全に安全です。


編集2015-07-04:NULLから保護するには、2つの可能性があります。#define Greetings_hello 0デフォルト値には(現在使用されているポインターの代わりに)NULLを使用してください。これは非常に便利ですが、デフォルトの列挙値の型安全性が低下します。NULLは任意の列挙に使用できます。または、無効であると宣言し、アクセサメソッドでチェックしてエラーを返すか、GCCなどを使用__attribute__((nonnull()))してコンパイル時にそれをキャッチします(例:greetings.h)。

const char *Greetings_str(const Greetings *g)
        __attribute__((nonnull(1)));
于 2015-07-01T15:20:39.357 に答える
-3

列挙型をtypedefしてから、それらの型の変数と関数の引数を宣言できます。

于 2013-02-11T22:48:32.803 に答える