
へにかさんのコメント:
> ある程度枯れたソースを流用したほうが、トラブルが少ない、という理由
> かも知れません。
ごもっともです。
- コピペによる短期的な修正量削減・リスク軽減をとるか
- コピペをせずに中・長期的な修正量削減tと、美しい設計による保守コスト削減をとるか
という話なわけで、どちらにするかはそのときの状況判断次第です。
しかし「コピペをせずにすむ技術力を持ちつつ、状況次第ではコピペする」
ならば理解できますが、わたしの見る限り技術力がないのでコピペしかできない
人がほとんどでした。
一般論で終わってしまうのも嫌なので具体的な例を出します。DB からデータを
取得して、CSV ファイル 10個に出力するプログラムがあるとします。10 ファイルを
まとめてオープンする処理を書かせるとします。すると、
fp1 = fopen(CSV_OUT_1, "w");
if ( fp1 == NULL ){
char errmsg[ERRMSG_LEN];
sprintf(errmsg, "CSVファイルオープンエラー");
err_fp = fopen(LOGFILE, "a");
fprintf(err_fp, errmsg);
fclose(err_fp);
EXEC SQL ROLLBACK;
EXEC SQL DISCONNECT;
exit(1);
}
fp2 = fopen(CSV_OUT_2, "w");
if ( fp2 == NULL ){
char errmsg[ERRMSG_LEN];
sprintf(errmsg, "CSVファイルオープンエラー");
err_fp = fopen(LOGFILE, "a");
fprintf(err_fp, errmsg);
fclose(err_fp);
EXEC SQL ROLLBACK;
EXEC SQL DISCONNECT;
fclose(fp1);
exit(1);
}
fp3 = fopen(CSV_OUT_3, "w");
if ( fp3 == NULL ){
char errmsg[ERRMSG_LEN];
sprintf(errmsg, "CSVファイルオープンエラー");
err_fp = fopen(LOGFILE, "a");
fprintf(err_fp, errmsg);
fclose(err_fp);
EXEC SQL ROLLBACK;
EXEC SQL DISCONNECT;
fclose(fp1);
fclose(fp2); ← ★ここがどんどん増えていくことに注意
exit(1);
}
… 以下 fp4~fp10 略。
というソースを平気で書いたりする。
おいおい、って感じですよね (ちなみに、どのファイルをオープンしようとして
エラーになったのかわからないし、errno も記録していない。ひどいのになると
いきなり exit しておしまいってのもよく見ました)。
わたしなら、
char *csv_outfile[] = {CSV_OUT_1, CSV_OUT_2, CSV_OUT_3 ... };
FILE *csv_fp[(csv_outfile)/sizeof(csv_outfile[0])];
for ( i=0 ; i<sizeof(csv_outfile)/sizeof(csv_outfile[0]) ; i++ ){
csv_fp[i] = fopen(csv_outfile[i], "w");
if ( csv_fp[i] == NULL ){
/* 可変長引数なログ出力関数 logging を作る */
logging("CSVファイル [%s] オープンエラー。errno[%s]",
csv_outfile[i], strerror(errno));
/* わたしはこういう場合は fclose せず OS にまかせますが、まぁ一応 */
for ( ; i>=0 ; i-- ){
fclose(csv_fp[i]);
}
EXEC SQL ROLLBACK;
EXEC SQL DISCONNECT;
exit(1);
}
}
と書きます。ログファイルに
CSVファイル[/foo/bar/hoge.csv](ほげシステム出力用CSV)] オープンエラー。
errno[Permission Denied]
などとファイルの種類 (ほげシステム~ってところ) を出力したいがために、
typedef struct {
char filename[FILE_MAX]; /* ファイル名 */
char description[256]; /* 説明用文章。エラー発生時などに使用 */
FILE *fp;
} csv_info_t;
csv_into_t csv_info[] = {
{CSV_OUT_1, "ほげシステム出力用CSV"},
{CSV_OUT_2, "ほげ料金請求額CSV"},
{CSV_OUT_3, "ふが料金請求額CSV"},
...
};
for ( i=0 ; i<sizeof(csv_info)/sizeof(csv_info[0]) ; i++ ){
csv_info_t *csv_p = &csv_info[i];
csv_p->fp = fopen(csv_p->filename, "w");
if ( csv_p->fp == NULL ){
logging("CSVファイル [%s](%s)オープンエラー。errno[%s]",
csv_p->filename, csv_p->description, strerror(errno));
..
}
}
とさらにもう一段抽象化するかもしれません。
もし何かの事情があってファイルオープン処理は 10回書かざるをえないとしても、
char *csv_outfile[] = {CSV_OUT_1, CSV_OUT_2, CSV_OUT_3 ... };
FILE *csv_fp[(csv_outfile)/sizeof(csv_outfile[0])];
memset(csv_fp, NULL, sizeof(csv_fp));
csv_fp[0] = fopen(CSV_OUT_1, "w");
if ( csv_fp[0] == NULL ){
sprintf(errmsg, "CSVファイル [%s] オープンエラー。errno[%s]",
CSV_OUT_1, strerror(errno));
goto err;
}
csv_fp[1] = fopen(CSV_OUT_2, "w");
if ( csv_fp[1] == NULL ){
sprintf(errmsg, "CSVファイル [%s] オープンエラー。errno[%s]",
CSV_OUT_2, strerror(errno));
goto err;
}
(略)
return RET_OK;
err:
err_fp = fopen(LOGFILE, "a"); /* ログオープンエラー処理略 */
fprintf(err_fp, "%s\n", errmsg);
fclose(err_fp);
for ( i=0 ; i<(sizeof(csv_fp)/sizeof(csv_fp[0]) ; i++ ){
if ( csv_fp[i] != NULL ){
fclose(csv_fp[i]);
}
}
EXEC SQL ROLLBACK;
EXEC SQL DISCONNECT;
return RET_NG;
などとエラー処理は一箇所にまとめてほしいものです。でもそれすらできません。
新人がしょーもないソースを書くのは仕方がないですが、それを指導するのは
先輩のはず。でも、その先輩にも技術力がないので指摘することができない。
かなりレベルが低い例で恐縮ですが、わたしの言うコピペというのはこういう
情けないレベルの話です。「ある程度枯れたソースを流用」云々以前の問題です。
こういう低レベルな悩みはないのであれば、へにかさんの会社 or 部署のレベルが
高いか、組み込み系全体のレベルが業務系よりは高いのかなぁと思ったりします。
まだまだ続きます。
P.S.
へにかさん、いろいろご心配頂いて申し訳ありません。とりあえず今はとても
楽しくやっております。上記のような汚いソースを書く輩がいたら、
「こんな汚いソースは初めて見ました」
と言って (嫌な奴)、何度でも書き直させます。


