83554


C言語におけるコマンドライン引数の処理と  
<errno.h>を用いたエラー対処について聞きたいことがあります。  
想定している仕様と実行環境はC99, C11あたりとPOSIX互換OSです。

コマンドライン引数を検査して、それがプログラムの仕様を満たしていない場合、  
エラーを表示してプログラムを停止させようとしています。

こういう場合、たいていの教科書  
（一例: 渡辺知恵美『システムプログラミング入門』）では、  
printf関数などを用いてエラーメッセージを出力するという実装を行っています。  
また、実用されるソフトウェアでは、  
（一例: [musl libcのgetopt関数の（現時点での）実装](https://git.musl-
libc.org/cgit/musl/tree/src/misc/getopt.c?id=b76f37fd5625d038141b52184956fb4b7838e9a5#n16)）では、  
自前の関数のなかでfwrite関数などを用いてエラーメッセージを出力しています。

そこで、  
標準（例えば[The Open Group Base Specifications Issue 7, 2018
edition](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/errno.h.html)）で用意されているエラーコード  
（コンパイル中に実行環境に応じたエラー番号に展開されるマクロ文字列）  
の中から、  
今のエラー状況を説明するのに適したものを選び、  
それをその場でerrno変数に設定してから、  
perror関数でメッセージを出してもいいですか。  
「いいですか」というのはかなり微妙な聞き方で申し訳ないのですが、  
「実装もできてちゃんと動いている（ように見える）けど、  
このやり方ではマズいことが起こる」  
あるいは  
「この設計はある理由から悪手である」  
といったことを知りたいです。

※「自分で考えた独自のエラーコードを設定する」ことではなく、  
「（標準で定義されている）エラーコードから自分が適当であると判断した  
ものを選び、それをその場でerrno変数に設定する」  
ことを想定しています。  
「自分で設定する」のはerrno変数であり、  
エラーコードは既存のものを使います。

実装の例::

[code]

    #include <stdio.h>
    #include <stdlib.h>
    #include <errno.h>
    
    /*
     * $ prog
     * → 引数がないので失敗
     * $ prog a
     * →　引数があるので成功（実際はこの後色々処理）
     */
    
    int main(
        int argc,
        char* argv[]
    ) {
        if (argc != 2) {
         /*
          * 問題の箇所。
          * この場所における「エラーの状況」は、
          * 「引数の個数が違う」という状況なので、
          * https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/errno.h.html
          * などを参考に、
          * 「引数が不正である」ことを意味する
          * EINVALというエラーコードをerrno変数に設定する。
          */
         errno = EINVAL;
    
         perror("Needs command line argument");
         exit(EXIT_FAILURE);
         }
    
        printf("Argument is: %s.\n", argv[1]);
        
        exit(EXIT_SUCCESS);
    }
    
[/code]

実演::  
<https://wandbox.org/permlink/pH32RuTr0K4DLs3j>

そもそも、どうしてerrno変数とperror関数とを使いたいかというと、  
エラー対処の方法を「統一」したいなと思ったからです。  
例示したプログラムは引数の個数のみを調べて終了していますが、  
実際には、例えばstrtol関数によって引数文字列を数値として読むなど、  
より複雑な引数処理が行われますよね。  
そういう場面におけるエラー対処は、  
（少なくともある教科書では）ほとんどがperror関数を用いて行われています。  
これは、そこでの処理に用いる関数が  
エラーコードをerrno変数に設定することによって  
エラーおよびその種別を報告するからなのですが、  
逆に「実行する処理がerrno変数を設定しないという理由で、  
perror関数を用いた統一的なエラー対処を行わないのはちょっと変じゃないか」  
と感じたのです。  
そこで、errno変数にエラーコードをその場で設定することで、  
エラー対処方法を  
「errno変数とperror関数とを用いる」ことに統一しようとした次第です。

エラーコードをerrno変数を設定しない処理でのエラー対処において、  
自分でerrno変数を設定するという行為の危険性や、  
反対に、業務プログラムやOSSなど実用プログラムにかかわるなかで、  
そういった実装をした、あるいは出会った経験がありましたら  
教えていただきたいです。  
また、そもそも実用のプログラムではperror関数は使わないほうがいい、  
というような暗黙の常識などがあれば、ぜひ知りたいです。

よろしくお願いします。


CC BY-SA 4.0
2021-11-11T03:12:00.957
83553
2021-11-11T04:57:13.663
2021-11-11T04:29:24.067
19601
19601
question
c command-line
C言語において、自分で適当だと判断した標準のエラーコードを選んでerrno変数に設定し、コマンドライン引数のエラー対処を行っても問題ないですか


[man errno](https://linuxjm.osdn.jp/html/LDP_man-pages/man3/errno.3.html)
にも書かれていますが `errno` は「変更可能な左辺値」ですので「変更することは問題ない」です。マニュアルには `0`
にするとよい状況が書かれています。かつ、エラーコード値は正の整数値であるとされています。説明がそれだけということはつまり、具体的なエラーコード値が何であるかは
POSIX では規定していません。実際、ウチの hpux11.11 と cygwin でエラー値は異なります。

cygwin では `#define ENOTEMPTY 90`  
hpux11.11 では `#define ENOTEMPTY 247`

ということで、あなたが新しい `EHOGEPIYO`
なるエラーコードを採用しようとしたとして、それに割り振るエラー値は既にシステムが用意している値とカブルかもしれません。かぶらせてしまうと誤動作します。

また当然ながら `perror()` もあなたの追加した `EHOGEPIYO` に対してエラーメッセージを持っていませんから、使い道がないです。

なので `EHOGEPIYO` なるエラーコードをユーザーの一人が勝手に使おうとしても、それだけではシステム全体に追加することにはならない上に POSIX
システム間での互換性がなくなるので意味がない＝誰もやらない、ということです。

* * *

質問が編集された結果内容が変わったので追記

Q. `errno` に、標準のエラー値（ `EINVAL` など）を自分で設定することは是か非か  
A. [man errno](https://linuxjm.osdn.jp/html/LDP_man-pages/man3/errno.3.html)
には末端プログラマが `errno=0;` とすべき状況が書かれていますが非０値を代入することに関して良いとも悪いとも書かれていません。 `errno`
が「変更可能な左辺値」である以上は非０値を代入することができます。あなたが POSIX ライブラリ関数（の拡張）の作者であるなら必要な時に `errno`
に `EINVAL` を代入することは問題ないでしょう。ライブラリ関数が `errno=0;`
にすることはないと解説されていますので、ライブラリ関数（の拡張）の作者としては `errno=0;`
とすることはまずそうです。ライブラリ関数の作者でない一般プログラマがあっても「代入すること」自体は問題ないです。

通常のプログラムで `errno=EINVAL;` のようなコードをあまり見ない理由は単純です。

  * `errno` を変更するのはライブラリ関数であるというコンセンサスが既にある
  * 関数の引数とプログラムの引数は使用者にとって違うものなので、「関数の引数が間違っている」をもって「プログラムの使い方が間違っている」の意味にしてしまうと混乱が生じるであろう
  * POSIX 仕様が決めているエラーコードとエラーメッセージでは「あなたのプログラム固有のエラー」を表現しきれないであろう（末端使用者にとってはエラーメッセージなど不要で usage の解説が出るほうが嬉しいかもしれない）
  * POSIX 仕様が定めていない各システム固有の拡張エラーコードとエラーメッセージを使うと移植性がなくなる

使い手のことを考えない、プログラマだけの論理でコード書いても喜ばれないです。将来にわたって自分以外の人物が使うことは絶対ないと断言できるならお好きにどうぞ。


CC BY-SA 4.0
2021-11-11T03:41:33.507
83554
2021-11-11T04:57:13.663
2021-11-11T04:57:13.663
8589
8589
83553
answer