質問
特定のものをテキストファイルにダンプする必要があり、画面に表示する必要があります。 (私はCプログラムユーティリティについて語っている) メニューオプションは次のようになります。
1. display AA parameters 2. display BB parameters 3. display CC parameters 4. dump all 5. Exit Select option >
1/2/3を選択した場合、画面のみに表示する必要があるか、オプション#4を選択した場合、すべてのパラメーターを1つずつ表示する必要があり、同じ必要があります。.txtファイルにダンプします。
わかっているのは、printf関数とfprintf関数を使用して、画面に表示し、テキストファイルに書き込むことができることです。問題は、20個を超えるパラメーターを表示し、それぞれに少なくとも20個のサブパラメーターがあることです。
現在、以下のように実装されています
printf ( "Starting serial number [%ld]\n",
serial_info_p->start_int_idx);
fprintf(file_p, "Starting serial number [%ld]\n",
serial_info_p->start_int_idx)
printf ( "Current Serial number [%d]\n",
serial_info_p->current_int_idx);
fprintf(file_p, "Current Serial number [%d]\n",
serial_info_p->current_int_idx);
コードの行数を削減するためにこれを実装する最も簡単な方法はありますか?
解決
これまでに人々が提案してきたことよりももっと過激になりますが、多すぎるかもしれません。 (「インライン」キーワードはC99です。C89にコーディングする場合は、あまり重要なことなく省略できます。)
/*
** These could be omitted - unless you get still more radical and create
** the format strings at run-time, so you can adapt the %-24s to the
** longest tag you actually have. Plus, with the strings all here, when
** you change the length from 24 to 30, you are less likely to overlook one!
*/
static const char fmt_int[] = "%-24s [%d]\n";
static const char fmt_long[] = "%-24s [%ld]\n";
static const char fmt_str[] = "%-24s [%s]\n"; /* Plausible extra ... */
static inline void print_long(FILE *fp, const char *tag, long value)
{
fprintf(fp, fmt_long, tag, value);
}
static inline void print_int(FILE *fp, const char *tag, int value)
{
fprintf(fp, fmt_int, tag, value);
}
static inline void print_str(FILE *fp, const char *tag, const char *value)
{
fprintf(fp, fmt_str, tag, value);
}
static void dump_data(FILE *fp, const serial_info_t *info)
{
dump_long("Starting serial number", info->start_int_idx);
dump_int( "Current Serial number", info->current_int_idx);
/* ... and similar ... */
}
次に、呼び出しコードは、オプション1、2、3、および2に対してdump_data()
を1回(引数stdout
で)呼び出し、オプション4に対して2回(offsetof
で1回、出力ファイルのファイルポインターで1回)を呼び出します。
パラメータの数が本当に膨大になったら(数百に)、タイプとオフセット情報(<stddef.h>
からlong
)と関数へのポインタをエンコードしたデータ構造を検討することさえできますなどのように、すべての必要な情報をエンコードする構造を反復処理する<=>でループするだけです。
また、データ構造のすべての整数メンバーに同じ基本整数型(例では<=>)を使用することで、生活を簡素化できます。
Fred Brooks in <!> quot; Mythical Man Month <!> quot; -まだ読んでいないなら読む価値のある本ですが、Twentieth Anniversaryエディションを必ず読んでください-第9章で述べています:
フローチャート[コード]を表示し、テーブル[データ構造]を隠してください。テーブルを見せてください。通常、フローチャートは必要ありません。明らかです。
このコードのテーブルドリブンバージョンは、スペースを節約するだけでなく、同様の方法で100個の関連する関数を変更しなければならない場合のフラストレーションと、テーブルデータの単純な変更で多くの問題を解決できます。
他のヒント
編集: C ++タグは誤解を招くように見えますが、誰か削除してください。ありがとう:)
可変長マクロを使用して、printfとフレンドをカスタマイズします。
次のように書きます:
#define tee(fp,fmt, ...) \
{ \
printf (fmt, __VA_ARGS__); \
fprintf (fp, fmt, __VA_ARGS__); \
}
(名前はtee(1)ユーティリティに由来します)
このようなものを使用すると、任意の数の出力ストリームを追加でき、PrintTargetリンクリストを変更するだけで、実行時にそれらを変更できます。
/** gcc -Wall -o print_target print_target.c && ./print_target */
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
typedef struct PrintTarget* PrintTargetp;
void* xmalloc (size_t size);
PrintTargetp pntCreate (PrintTargetp head, FILE* target);
void pntDestroy (PrintTargetp list);
typedef struct PrintTarget
{
FILE* target;
PrintTargetp next;
} PrintTarget;
void myPrintf (PrintTargetp streams, char* format, ...)
{
va_list args;
va_start(args, format);
while (streams)
{
vfprintf(streams->target, format, args);
streams = streams->next;
}
va_end(args);
}
int main(void)
{
PrintTargetp streams = pntCreate(NULL, stdout);
streams = pntCreate(streams, fopen("somefile.txt", "a+")); //XXX IO errors?
myPrintf(streams, "blah blah blah...\n");
pntDestroy(streams);
return 0;
}
補助関数の定義は次のとおりです。
PrintTargetp pntCreate (PrintTargetp head, FILE* target)
{
PrintTargetp node = xmalloc(sizeof(PrintTarget));
node->target = target;
node->next = head;
return node;
}
void pntDestroy (PrintTargetp list)
{
while (list)
{
PrintTargetp next = list->next;
free(list);
list = next;
//XXX cycles?
//XXX close files?
}
}
void* xmalloc (size_t size)
{
void* p = malloc(size);
if (p == NULL)
{
fputs("malloc error\n", stderr);
abort();
}
return p;
}
プログラムの出力を tee(1)
コマンド。
コンソールアプリケーションを作成している場合は、次のようなものを使用して画面(標準出力)に出力できる必要があります。
fprintf(stdout, "Hello World\n");
これにより、データを印刷するコードを独自の関数に移動し、FILE *を渡して印刷できるようになります。次に、<!> quot; stdout <!> quot;を渡すと画面に出力できます。別のFILE *を渡すとファイルに出力できます。例:
void print_my_stuff(FILE* file) {
fprintf( file,"Starting serial number [%ld]\n", serial_info_p->start_int_idx);
fprintf(file, "Current Serial number [%d]\n", serial_info_p->current_int_idx);
.
.
.
}
編集: Cソリューションが必要であることに気付きませんでした。参照用にこの回答を残しますが、明らかにC ++が必要です。
出力を2つのストリームに送信する新しいストリームクラスを作成できます。 http://www.csでこの実装を見つけました。 technion.ac.il/~imaman/programs/teestream.html 。試したことはありませんが、動作するはずです。
リンクのコードは次のとおりです。
#include <iostream>
#include <fstream>
template<typename Elem, typename Traits = std::char_traits<Elem> >
struct basic_TeeStream : std::basic_ostream<Elem,Traits>
{
typedef std::basic_ostream<Elem,Traits> SuperType;
basic_TeeStream(std::ostream& o1, std::ostream& o2)
: SuperType(o1.rdbuf()), o1_(o1), o2_(o2) { }
basic_TeeStream& operator<<(SuperType& (__cdecl *manip)(SuperType& ))
{
o1_ << manip;
o2_ << manip;
return *this;
}
template<typename T>
basic_TeeStream& operator<<(const T& t)
{
o1_ << t;
o2_ << t;
return *this;
}
private:
std::ostream& o1_;
std::ostream& o2_;
};
typedef basic_TeeStream<char> TeeStream;
次のように使用します:
ofstream f("stackoverflow.txt");
TeeStream ts(std::cout, f);
ts << "Jon Skeet" << std::endl; // "Jon Skeet" is sent to TWO streams
#define ARRAY_LEN(x) (sizeof(x) / sizeof(x[0]))
FILE *f = fopen("somefile.txt", "a+");
FILE *fp[] = { stdout, f };
int i = 0;
for (i = 0; i < ARRAY_LEN(fp); i++) {
fprintf(fp[i], "Starting serial number [%ld]\n", serial_info_p->start_int_idx);
fprintf(fp[i], "Current serial number [%ld]\n", serial_info_p->start_int_idx);
}
fclose(f);