質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.40%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

意見交換

クローズ

8回答

693閲覧

関数マクロの引数にEnumの型(※値ではない)を入れることの是非について

azu1129

総合スコア8

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

2グッド

0クリップ

投稿2024/06/02 02:52

編集2024/06/02 03:08

2

0

テーマ、知りたいこと

C言語の関数マクロの作法に関する質問になります。

以下のように、引数にEnumの型を指定させる関数マクロを考えています。

C言語

1#define SET_ENUM_FROM_NUM(in_integer, out_enum, enum_type) out_enum = (enum_type)in_integer;

こういった実装は、皆さんから見て一般的にあるものでしょうか?
また、危険性や、可読性などの観点からご意見を頂きたいです。

背景、状況

上記の例だと利点が分かりづらいのですが、実際にやりたいことは、「文字列とEnum の相互変換」 になります。

以下のような enum型と文字列の対応がありまして、

C言語

1typedef enum { 2 CMD_TYPE_A_1 = 0, 3 CMD_TYPE_A_2 , 4 CMD_TYPE_A_3 , 5} cmd_type_a_t; 6 7static const char* cmd_type_a_table = 8{ 9 "cmd_type_a_1", 10 "cmd_type_a_2", 11 "cmd_type_a_3", 12} 13 14//※ CMD_TYPE_BやCMD_TYPE_Cもある

これを相互変換する関数マクロを、以下のように一般化したいのです。

C言語

1#define CONVERT_STR_TO_ENUM(str, str_len, convert_table, out_enum, enum_type) \ 2{ 3 // covnert_tableを探索するループ 4 char idx = 0; 5 for(idx = 0 ; convert_tableの長さ ; idx++) 6 { 7 // convert_table[idx]とstrをmemcmpして、一致するものがあれば、 8 // 右のようにキャストする out_enum = (enum_type)idx; 9 // その後 break; 10 } 11} 12 13void main(){ 14 cmd_type_a_t cmd_type_a; 15 CONVERT_STR_TO_ENUM( "cmd_type_a_2", 12, cmd_type_a_table, cmd_type_a, cmd_type_a_t); 16 // cmd_type_a == CMD_TYPE_A_2 17}

配列のインデックスをEnumにキャストする際にどうしてもキャスト演算が必要になるのですが、一連の処理を一般化するために関数マクロの引数に型を入れたいというわけです。

冒頭の、型を関数マクロの引数にすることの是非や、そもそもこういった変換をするにはもっと良い方法があればご教示いただけますと幸いです。

Paalon, SaitoAtsushi👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

回答8

#1

hiroki-o

総合スコア847

投稿2024/06/02 03:11

あらゆる点で、普通の関数のほうがよいと思います。
逆に、普通の関数にしない理由は何ですか?

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

#2

azu1129

総合スコア8

投稿2024/06/02 03:47

#1 普通の関数にできれば、それに越したことはないと思います。

わたしが不勉強なもので教えて頂きたいのですが、C言語で「型そのもの」を引数で受け渡すにはどうしたらよいでしょうか?

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

#3

fana

総合スコア11893

投稿2024/06/02 05:37

単にキャストすればよいのでは.
(キャストをマクロでやることの 意味/理由/etc がわからない感じ.)

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

#4

azu1129

総合スコア8

投稿2024/06/02 05:49

#3 Enumの型が一種類だけなら良い単にキャストするでよいのですが、複数ある場合はマクロの中の処理を cmd_type_a_t、cmd_type_b_t、cmd_type_c_t… それぞれにべた書きすることになると思います。それらを共通化するためにマクロ(もしくは可能なら関数に)したいです。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

#5

jimbe

総合スコア13045

投稿2024/06/02 06:06

あらゆる C の仕様を知ってるわけではありませんので間違っているかも知れませんが、 C で動的に「型そのもの」を扱うことは出来ないと思います。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

#6

退会済みユーザー

退会済みユーザー

総合スコア0

投稿2024/06/02 06:19

一般的かどうかは分かりませんが、昔ながらのCだとこんな感じに書くような気がします。

c

1#include <stdio.h> 2#include <string.h> 3 4typedef enum { 5 CMD_TYPE_A_1 = 0, 6 CMD_TYPE_A_2 , 7 CMD_TYPE_A_3 , 8} cmd_type_a_t; 9 10static const char* cmd_type_a_table[] = 11{ 12 "cmd_type_a_1", 13 "cmd_type_a_2", 14 "cmd_type_a_3", 15 NULL 16}; 17 18#define NOT_FOUND -1 19 20size_t find_index(const char* str, const char* str_table[]) 21{ 22 for (size_t idx = 0; str_table[idx] != NULL; ++idx) { 23 if (strcmp(str, str_table[idx]) == 0) 24 return idx; 25 } 26 return NOT_FOUND; 27} 28 29int main(){ 30 size_t idx = find_index("cmd_type_a_2", cmd_type_a_table); 31 if (idx == NOT_FOUND) { 32 fprintf(stderr, "cannot find index...\n"); 33 return 1; 34 } 35 cmd_type_a_t cmd_type_a = (cmd_type_a_t)idx; 36 fprintf(stdout, "found index(%ld)...\n", idx); 37 return 0; 38}

※安全面は考慮してません

なお文字列からenumの変換は必ず成功するものではないので、失敗時にどうするかを決める必要があります。そこを決めないとマクロにできるのか検討も出来ないように思います。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

#7

azu1129

総合スコア8

投稿2024/06/02 06:39

#5#6
サンプルコードまで頂いて、ありがとうございます。

ループでインデックスを探す処理を共通化して、キャストする部分は共通化しないのですね。
jimbeeさんがおっしゃるように言語として型を動的に扱うことができない以上、
これがC言語として正しく、なおかつ一番スマートな形に思えます。

失敗時にどうするかを決める必要があります。

失敗時はエラーにしようと考えていましたが、たしかにエラーを返すことを考えると関数マクロでは難しいですね。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

#8

jimbe

総合スコア13045

投稿2024/06/02 18:55

編集2024/06/03 05:15

enum の値を何に使うのかによりますが、文字列から対応付けた"何か"を得られるようにするなら、それらを含む構造体を使うとかいうのも可能でしょうか。
以下では CMD に enum に当たる no の他に a/b/c の type も持たせることで、 CMD を使っての処理時に type 別にも何か出来るようにしています。

c

1#include <stdio.h> 2#include <string.h> 3 4typedef struct _cmd { 5 int no, type; 6 char *name; 7} *CMD, _CMD; 8 9static CMD CMD_TYPE_A_TABLE[] = { 10 &(_CMD){ 0, 'a', "cmd_type_a_1" }, 11 &(_CMD){ 1, 'a', "cmd_type_a_2" }, 12 &(_CMD){ 2, 'a', "cmd_type_a_3" }, 13 NULL //EOD 14}; 15 16static CMD CMD_TYPE_B_TABLE[] = { 17 &(_CMD){ 0, 'b', "cmd_type_b_1" }, 18 &(_CMD){ 1, 'b', "cmd_type_b_2" }, 19 NULL //EOD 20}; 21 22typedef struct _cmd_c { 23 _CMD cmd; //最初は必ず _cmd 構造体にすること 24 int type_c_addnl_value; 25} *CMD_C, _CMD_C; 26 27static CMD CMD_TYPE_C_TABLE[] = { 28 (CMD)&(_CMD_C){{ 0, 'c', "cmd_type_c_1"}, 100 }, 29 (CMD)&(_CMD_C){{ 1, 'c', "cmd_type_c_2"}, 200 }, 30 (CMD)&(_CMD_C){{ 2, 'c', "cmd_type_c_3"}, 300 }, 31 (CMD)&(_CMD_C){{ 3, 'c', "cmd_type_c_4"}, 400 }, 32 NULL //EOD 33}; 34 35CMD get_cmd_from_name(char *name, CMD *table) { 36 for(CMD *p=table, cmd; (cmd=*p); p++) { 37 if(strcmp(name,cmd->name) == 0) return cmd; 38 } 39 return NULL; 40} 41 42CMD get_cmd_from_no(int no, CMD *table) { 43 for(CMD *p=table, cmd; (cmd=*p); p++) { 44 if(no == cmd->no) return cmd; 45 } 46 return NULL; 47} 48 49void print_cmd(char *prompt, CMD cmd) { 50 printf("%s: ", prompt); 51 if(cmd != NULL) { 52 printf("no=%d, type=%c name=%s", cmd->no, cmd->type, cmd->name); 53 if(cmd->type == 'c') { 54 CMD_C cmd_c = (CMD_C)cmd; 55 printf(" addnl_value=%d", cmd_c->type_c_addnl_value); 56 } 57 } else { 58 printf("not found"); 59 } 60 printf("\n"); 61} 62 63int main(void) { 64 CMD a = get_cmd_from_name("cmd_type_a_2", CMD_TYPE_A_TABLE); 65 print_cmd("a", a); 66 67 CMD b = get_cmd_from_name("cmd_type_b_3", CMD_TYPE_B_TABLE); 68 print_cmd("b", b); 69 70 CMD c = get_cmd_from_name("cmd_type_c_4", CMD_TYPE_C_TABLE); 71 print_cmd("c", c); 72}

paiza.ioで実行

a: no=1, type=a name=cmd_type_a_2 b: not found c: no=3, type=c name=cmd_type_c_4 addnl_value=400

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

最新の回答から1ヶ月経過したため この意見交換はクローズされました

意見をやりとりしたい話題がある場合は質問してみましょう!

質問する

関連した質問