Programming」カテゴリーアーカイブ

GOptionContext

コマンドラインオプションは Linux では重要。
とりあえずよく見かける実装手段を自分で整理、したけど…

#include <stdio.h>
#include <getopt.h>

/* 
 * optarg, optind については unistd.h で定義されている 
 * getopt.h によってインクルードされる
**/

static struct
option options[] = {
    {"help",   no_argument, 0, 'h'},
    {"list",   no_argument, 0, 'l'},
    {"num", required_argument, 0, 'n'},
    {0, 0, 0, 0}
};

int
main (int argc, char *argv[]) {

    int opt, i;
    int idx = 0; /* 使い道が無いので気にしないほうがいい */

    /* 第三引数にアルファベットを指定 */
    while ((opt = getopt_long(argc, argv, "hln:", options, &idx)) != -1) {
        switch (opt) {
        case 'l':
            /* こうやって捕まえる */
            printf("-l or --list オプションです\n");
            break;
        case 'n':
            /* コロンを付けると値を受け取れる */
            printf("%s 行表示します\n", optarg);
            break;
        case 'h':
            /* Usage は自分で用意する */
            printf("ヘルプが表示されるかも...\n");
            return 0;
        case '?':
            return 1;
        }
    }
    /* オプションを省いた部分を処理 */
    for (i=optind; i<argc; i++) {
        printf("%d %s\n", i, argv[i]);
    }
    return 0;
}

option 構造体の中、getopt_long の引数、そして case 文。
いったい何度同じアルファベット指定を書かせるのよ。
間違いの元というか、整理していて自分が間違いまくったYO!

それじゃイカンと思ったのかどうかは知らないが。
GLib の GOptionContext は一箇所のみの指定になっていた。

#include <glib.h>
#include <stdio.h> /* stderr */

static gboolean _bool = 0;     /* 値を受ける必要が無いとき */
static gint     _num  = 0;     /* getopt と違い数値として受け取り */
static gchar*   _str  = NULL;  /* 文字列は破棄が必要、日本語はダメだった */

static GOptionEntry entries[] = {
    {"bool", 'b', 0, G_OPTION_ARG_NONE,   &_bool, "Show", NULL},
    {"num",  'n', 0, G_OPTION_ARG_INT,    &_num,  "Size", "<num>"},
    {"str",  's', 0, G_OPTION_ARG_STRING, &_str,  "Text", "<string>"},
    {NULL}
};

int
main (int argc, char *argv[]) {

    GError* error = NULL;
    GOptionContext* context;

    context = g_option_context_new ("After GApplication ?");
    g_option_context_add_main_entries (context, entries, NULL);
    if (!g_option_context_parse (context, &argc, &argv, &error)) {
        g_fprintf(stderr, "Error parsing options, try --help.\n");
        return 1;
    }
    /* 値を確認 */
    g_printf("boolean=%d, int=%d, string=%s\n", _bool, _num, _str);

    /* 後は GApplication ? */

    g_free(_str);
    g_option_context_free(context);
    return 0;
}
/* output

$ ./a.out
boolean=0, int=0, string=(null)

$ ./a.out -b --num 675 -s Daytona
boolean=1, int=675, string=Daytona

$ ./a.out -h
Usage:
  a.out [OPTION...] After GApplication ?

Help Options:
  -h, --help             Show help options

Application Options:
  -b, --bool             Show
  -n, --num=<num>        Size
  -s, --str=<string>     Text

*/

GOptionEntry が少し面倒、と思ったら大間違いだった。
まさかの Usage 全自動生成、しかも細かい。
Python の optparse 同様に -h –help は勝手に作ってくれる。

変更したくなったら一箇所だけ書き換えればいいのは本当に嬉しい。
Usage にさえ反映されるので「やっちまった…」は確実に減る。

GOptionContext ってこんなに凄かったのか。
C 言語標準ライブラリでゴリゴリ書くなんてアホみたい。
せっかく整理したけどもう getopt を使うことは無いがや。

しかし別途というかオプション以外の引数や数を得る関数が何も無い。
そういうのは GApplication でヤレということでいいのかな?

Numeric sort in GLib

ファイル名の数値優先ソート C 言語版を書いてみた。

というより、手元に溜まっている古い覚書ファイルを整理している。
昔書いたのって posix と glib が混在で自分がゲンナリするもので。
とにかく今の知識で限界まで整理して Web に書き出す。

Python の覚書でディレクトリ内容列挙とソートを別々に書いているけど
どうせ同時に使うのだからまとめたほうがよさそう。
ということでこんなコードになった。

#include <glib.h>
#include <gio/gio.h>

/*
 * Numeric sort in GLib (like a Nautilus)
 * gcc nautilus_sort.c `pkg-config --cflags --libs gio-2.0`
**/

gint
compare_main_func(gchar** a, gchar** b) {

    gint n;

    gchar* aaa = g_utf8_collate_key_for_filename (*a, -1);
    gchar* bbb = g_utf8_collate_key_for_filename (*b, -1);
    n = g_strcmp0(aaa, bbb);
    g_free(aaa);
    g_free(bbb);
    return n;
}

gint
compare_data_func(gconstpointer a, gconstpointer b) {
    return compare_main_func((gchar**)a, (gchar**)b);
}

void 
print_data(gpointer data, gpointer user_data) {
    g_printf("%s\n", data);
}

int
main (int argc, char *argv[]) {

    gchar* dir;
    GFile* file;
    GFileEnumerator* file_enum;
    GFileInfo* info;
    GPtrArray* array;

    dir = g_get_current_dir();
    file = g_file_new_for_path(dir);
    g_free(dir);
    /* Get the directory contents */
    array = g_ptr_array_new();
    file_enum = g_file_enumerate_children(file,
                                          G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME,
                                          G_FILE_QUERY_INFO_NONE,
                                          NULL,
                                          NULL);
    for (;;) {
        info = g_file_enumerator_next_file(file_enum, NULL, NULL);
        if (info == NULL) break;
        g_ptr_array_add(array, g_strdup(g_file_info_get_display_name(info)));
        g_object_unref(info);
    }
    g_object_unref(file_enum);
    /* Sort */
    g_ptr_array_sort(array, compare_data_func);
    /* print */
    g_ptr_array_foreach(array, print_data, NULL);
    /* free */
    g_ptr_array_free(array, TRUE);
    return 0;
}

numeric_sort

pkg-config は gio ですのでお間違いなく。

Python では使わないので気が付かなかったけど
GCompareFunc の引数が gconstpointer になっている。
実際には const gchar** だ、コレにはまいった。

gchar* aaa = g_utf8_collate_key_for_filename (*((gchar**)a), -1);

とやれば一応関数に分離せずにビルドできる。
けど見た目が酷過ぎるので分離した、ドンだけキャストすればいいのよ。
ポインタが今一。。。な初心者がこんなの見たらブン投げるわ。

しかし GLib はループ用の int をまったく使わないで書けるんだな。
筆者はもう慣れたけどローカル変数も malloc も使わないって違和感スゲェ。
C より Python の経験があるほうが覚えるの早そう、破棄が必要なだけだし。

ついでに、今日電源を入れたら。

mouse

GNOME はこんなに親切だったのか、初めて見た。
いやマウスの LED がチカチカするから解るんですけど。

Nemiver

gcc で C/C++ プログラミングをする際のデバッガに gdb がある。
コマンドラインツールなので vi, emacs 愛用者は普通に使えると思う。
しかし筆者のような Gedit 愛用者には少々使い辛い。

何かないかと探すと Nemiver という GUI アプリケーションがあった。
ようするに gdb のフロントエンド。
GNOME Wiki! に登録されているし突然亡くなる心配はなさそう。

Apps/Nemiver – GNOME Wiki!

書くまでもなく日本語情報は無かった。
ということで自分で使ってみる。

sudo dnf install nemiber

で Fedora は導入できる、ubuntu は apt-get でいいはず。

設定の[デバッガ]タブにて gdb の位置を指定。
/usr/bin/gdb でいいはず、この設定は必要あるのかな?
レイアウト等はお好みで。

このアプリは起動時の引数に実行ファイルを渡すので終了させる。
ビルド時に gcc への引数 -g (デバッグ情報を含める)を追加。

gcc -g hoge.c
nemiver a.out

nemiver01

使い方は Visual Studio とだいたい同じですね。
F6 キーで次行へ進む、F7 キーで関数の内部へ入る。
F11 キーでカーソル行まで一気に進む、等々。

ブレークポイントは行番号横をクリック。
F5 を押していくと指定した位置で止まるのでその時の変数値なんかを。
下ペインのブレークポイントタブで通過数を確認等もできる。

下ペインのコンテキストタブで変数の現在値を確認できる。
日本語の文字列変数もそのまま確認できるけど静的配列だとダメです。

pchar

これ以外は各自で目的が違うと思うので勝手に調べてね。
Visual Studio を使っていた人ならなんとなく解ると思う。

っっって、つまり結局は毎回コマンドを打たなければいけないのか。
では意味がないので Gedit の外部ツールに登録してしまおう。

#!/bin/sh
gcc -g -o debug $GEDIT_CURRENT_DOCUMENT_PATH `pkg-config --cflags --libs glib-2.0`
if test $? -eq 0; then
    nemiver debug
fi

external_tools

筆者は GLib を使うのでこんな感じにしてみました。
デバッグの為だけに糞重い開発環境を使う必要はないです。

しかし Gedit は便利だ、コマンドが充実した Linux が便利なだけですが。
問題は外部ツールが増えすぎると指定したキーを忘れてしまうことだ。

Stack area

最近スタック領域とかエラそうなことを書いているけど。
よく考えたら実は何故スタック領域という名前なのかを知らない。
スタックってデータを後入れ先出しすることだよね。

#include <stdio.h>
#include <stdlib.h>

int
main (int argc, char* argv[]) {

    /* 自動変数はスタック領域 */
    int   i;        /* 4 byte 確保 */
    char  str1[6];  /* 6 byte 確保 */
    char* str2;     /* 8 byte (64bit の場合)確保 */

    /* NULL文字'\0'が入るので 5 byte 以下 */
    sprintf(str1, "%s", "Ninja");

    /* 256 byte をヒープ領域に確保 */
    str2 = malloc(256);
    sprintf(str2, "%s", "Kawasaki");

    for (i=0; i<2; i++)
        printf("%s %s\n", str2, str1);

    /* ヒープ領域確保分は解放する(確保を手放す)必要あり */
    free(str2);

    /* 解放されても別スレッドに上書きされるまでは残っている */
    printf("%s\n", str2);

    return 0;
}

ということさえ解っていれば別に意味は知らずとも…
ではイカンよな、もう少し理解しなきゃ。

学校では教えてくれないこと | 技術コラム集(組込みの門) | ユークエスト

そういう意味だったのか、宣言順に積み上がっていくのね。
ただ x86_64 だと 8 バイトでは上手くいかなかったので 16 に。

#include <stdio.h>
#include <string.h>

int
main (int argc, char* argv[]) {

    char a[16] = "元の文字列", b[16];

    printf("before a[]=<%s>\n", a);
    strcpy(b, "0123456789abcdef破壊");
    /*
     * ちなみにコチラだと Segmentation fault
     * sprintf("%s\n", b, "0123456789abcdef破壊");
    **/
    printf("after  a[]=<%s>\n", a);

    return 0;
}

buffer_over_run

なるほど、これは完全に後入れ先出しだ。

関数内の自動変数もスタックに入るから確かにコレが一番効率いいな。
スタックってそういう処理がしたい場合に利用するのか、うんうん。
それも解って得した気分。

しかしこういう gcc で解説してくれるサイトなんですが。
皆 stdio.h だけインクルードしているけど、筆者の環境では

strcpy を使う時は string.h
malloc を使う時には stdlib.h

と関数宣言されているヘッダを直インクルードしないと警告になる。
ビルドはできるけどね、警告が出るのって嫌じゃないですか。

GIOChannel

glib での読み書きは g_open ではなく GIOChannel で行うらしい。
もう情報が山ほどありすぎてワケワカだよ、独学の限界を感じる。
まだよく解っていないけどとりあえずテキストで試してみた。

#include <glib.h>

int
main(int argc, char* argv[]) {

    GIOChannel* channel;
    GError*     error = NULL;
    gchar*      str;
    gsize       length;
#if !GLIB_CHECK_VERSION (2,35,0)
    g_type_init ();
#endif
    channel = g_io_channel_new_file("watch.txt", "r", &error);
    if (error)
    {
        g_warning (error->message);
        g_error_free (error);
        return 1;
    }
    g_io_channel_read_to_end(channel, &str, &length, &error);
    if (error)
    {
        g_warning (error->message);
        g_error_free (error);
        g_io_channel_unref(channel);
        return 1;
    }
    g_printf(str);

    g_free(str);
    g_io_channel_unref(channel);
    return 0;
}

iochannel

こういうことでいいのかな?
char 配列ならバイト単位だからバイナリにも応用できるな。

んで、いつものように vala のサンプルコードを見てみる。
GLib.IOChannel ? glib-2.0
g_io_add_watch でハンドラを用意してメインループを回している。
このコードだとこうするメリットが特に見当たらないんですけど。

GMainLoop, GMainContext和GSource学?笔? – ?匠Smith先生的?? – 博客?道 – CSDN.NET

こんなこともできるのか、端末からの読み書きに GMainLoop とは。
つまりメインループを回すことに意義があるってことかな。

ただバックスラッシュ(\)がスラッシュ{/}になっているのは何故?
そこを書き換えて g_type_init を消せば普通にビルドできます。
このコードを参考に色々弄ってみると面白いかも。

中国にも glib を使っている人がいるんだな、つか日本人は…