C/C++」タグアーカイブ

g_strdup_printf

GLib には g_strdup_printf という関数があることを今頃知った。
strdup にに文字列成形機能を追加したということみたい。
ライブラリ関数として用意されているのだから利用しない手はない。

_strdup、_wcsdup、_mbsdup

strdup は _strdup とう名前で VisualStudio でも使えるようだ。
malloc して内容をコピーするだけなのでアッサリ自作できるとはいえ。

更に devhelp をよく見ると文字列は GPtrArray で配列にしている。
GArray で文字列でも問題ないみたいだけど。
とにかく前回のコードを上記で書き換えてみる。

#include <glib.h>

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

    GPtrArray* array;
    gchar*  str;
    gint    count, i;

    count = 3;
    array = g_ptr_array_new();
    i = 0;
    for (i; i<count; i++) {
        str = g_strdup_printf("GArray %03d", i);
        g_ptr_array_add(array, str);
    }
    /* printf */
    i = 0;
    for (i; i<array->len; i++) {
        g_print("%s\n", g_ptr_array_index(array, i));
    }
    /* free */
    g_ptr_array_free(array, TRUE);
    return 0;
}

g_strdup_printf ならこんなにスッキリ!
これは g_ptr_array_free が破棄してくれるけど通常は破棄を忘れずに。

GPtrArray なら一揆に簡単になった。
オブジェクト作成に引数は不要だし配列へのポインタ追加も代入不要。
中身の取り出しも型名指定する必要が無い、文字列なら絶対にコッチ。

ただ C 言語なので演算子のオーバーロードがない。
list[0] みたいにはできないんですね。

Arrays of Arrays

動的配列みたいなワード検索するとよく見かける C 言語コード

#include <stdio.h>

#define MAX_LENGTH 1024

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

    int count,i;
    char** arg;
    count = 3;
    /* 開始 */
    arg = (char**)malloc(sizeof(char*) * count);
    i = 0;
	for (i; i<count; i++) {
        arg[i] = (char*)malloc(sizeof(char) * MAX_LENGTH);
        sprintf(arg[i], "malloc %03d", i);
    }
    /* printf と破棄 */
    i = 0;
    for (i; i<count; i++) {
        printf("%s\n", arg[i]);
        free(arg[i]);
    }
    free(arg);
    return 0;
}

c_aoa

これだと gcc は盛大に Warning を吐いてくれるのね。
ポインタのポインタが配列の配列だと認識できないのかな?
先頭要素のポインタを一つ分オフセットすれば次の要素のポインタ…
って今なら普通に解るけどスゲェ理屈だよな。

minipoli で使っている C++ 式というか new でなら問題無い。
TCHAR を使っているし解りづらいので少し書き換えて。

#include <stdio.h>

const int MAX_LENGTH = 1024;

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

    int count = 3;
    // 開始
    char** arg = new char* [count];
	for (int i=0; i<count; i++) {
        arg[i] = new char[MAX_LENGTH];
        sprintf(arg[i], "new %03d", i);
    }
    // cout と破棄
    for (int i=0; i<count; i++) {
        printf("%s\n", arg[i]);
        delete[] arg[i];
    }
    delete[] arg;
    return 0;
}

C++ なのに何故 stdio なんだよとあの頃の自分にツッコミしたい。
VisualStudio でプログラミングを始めるとそうなっちゃうんだよなぁ。
Linux で C++ は実用面では避けたいんだが、gtkmm ワカンネエし。

GLib に GArray というものがあるので使ってみる。

#include <glib.h>

#define MAX_LENGTH 1024

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

    GArray* array;
    gchar   c[MAX_LENGTH];
    gint    count, i;

    count = 3;
    /* 開始 */
    array = g_array_new(TRUE, TRUE, sizeof (gchar*));
    i = 0;
    for (i; i<count; i++) {
        g_sprintf(c, "GArray %03d", i);
        gchar* temp;
        temp = g_strdup(c);
        g_array_append_val(array, temp);
    }
    /* printf 破棄は不要 */
    i = 0;
    for (i; i<array->len; i++) {
        g_print("%s\n", g_array_index(array, gchar*, i));
    }
    /* 第二引数 TRUE で中身も破棄 */
    g_array_free(array, TRUE);
    return 0;
}

そうか、あらかじめポインタの領域が確保されているわけじゃないもんな。
一度新な領域を確保してポインタを入れる必要があるわけだ。
公式サンプルコードなら簡単だけど成形するとなると一手間掛かる。
GLib のほうが面倒臭いなんて初めて見た。

うーん、どれもイマイチだ。
次は GList でも調べるか。

g_type_init

Vala の覚書ページを書き換えようとテスト。

init_error

なんだそりゃwww
警告が出るだけでビルドは問題なく行われるようだけど。

調べてみると GLib 2.36 からマジで g_type_init を廃止したようだ。
Fedora 19 の GLib はたしかに 2.36 だった。
Python ばかり使っているとこういうの気が付かないよね。

GIO入門 – ふとしの日記

#include <gio/gio.h>

/* gcc no_init.c `pkg-config --cflags --libs gio-2.0` */

gint
main (gint argc, gchar* argv[])
{
	GFile *gf;
	/* GLib >= 2.36 
	g_type_init ();*/
	gf = g_file_new_for_path ("sample.txt");
	return 0;
}

g_type_init は本当に不要になっているよふとしさん。
しかし Vala の場合はジェネレータが勝手に書き足すのだが…

[vala] codegen: Do not call g_type_init when targeting GLib >= 2.36

対策版は出ているのか、って Oct 2012 だから一年前かよ。
Fedora 19 パッケージの 0.20.1 は April 2013 のはずなのだが。

Lubuntu 13.10 の GLib は 2.38。
valac のパッケージを Synaptic で調べると同じ 0.20.1 か。
インストールしてみたけど、やはり症状は同じであった。

本家からソースを落として自力 make するか修正を待つか、うーん。
Vala のバージョンと GLib, GTK+ のバージョンは関係ってあるのか?
そのあたりがよく解らないから自力ビルドは避けたいのだが。

ビルドは可能なんだからこのままでもいいか、一番安全だし。

しかし `g_type_init vala` で検索しても日本語がまったく出てこない。
つまり日本人で Vala を使っている人はいないってことなのかも…

gunichar

C 言語で文字列メンドクサイ | PaePoi

以前こんなことを書いたけど

Eye of GNOME 3 plugin Create | PaePoi

上記をやった時に早く気がつけよ。
g_utf8_strlen なんて解りやすい関数があるじゃないの。
一文字ずつ書き出すための変換関数もあるし。

ついでに、Linux の wchar_t は 4 byte だから多分 UCS-4 だよね。
まあ GLib を使うなら gunichar になるわけだが。
typedef guint32 gunichar;
になっているけど、つまりは 4 byte ということだし。
いわゆる普通に Unicode と呼ばれているものは gunichar2 でいいみたい。

変換は UTF-16 ではなく UCS-4 にしたほうがよさげ。
g_unichar_to_utf8 なんて楽できそうな関数もあるわけで。
Unicode Manipulation

UTF-16 に変換しようと考えてしまう所はやはり Windows でしかプログラミングできなかった頃のバカな頭がまだ抜けきらないということか。
でも UCS-4 でも str[int] の添字でイケるのかな、実験だ。

test.c

#include <glib.h>
#include <string.h>

int
main (int argc, char *argv[])
{
    /* Including Japanese characters */
    gchar c[] = "Linux 勉強中";
    /* UTF-8 Length */
    g_printf ("%s is %d characters\n", c, g_utf8_strlen(c, sizeof(c)));
    /* Converted to UCS4 */
    GError * error = NULL;
    gunichar * ucs;
    glong len = sizeof(c);
    ucs = g_utf8_to_ucs4 (c, -1, NULL, &len, &error);
    if (!ucs) {
        g_print (error->message);
        int retval = error->code;
        g_error_free (error);
        return retval;
    }
    /* Export single character */
    int i = 0;
    for (i; i<len; i++) {
        gchar cc[4];
        memset(&cc, '\0', sizeof(cc));
        g_unichar_to_utf8 (ucs[i], cc);
        g_printf ("%s\n", cc);
    }
    g_free(ucs);
    return 0;
}

Makefile

ucs: test.c
	gcc -o ucs test.c `pkg-config --cflags --libs glib-2.0`

ucs.tar.gz

添字で普通に一文字づつ取り出せるみたいね。
変換エラーチェックを入れてもコレだけだ、簡単。

しかし memset しないと ‘\0’ を入れてくれない。
まあそりゃ普通に char の %c 指定ならンナモンいらないわけだし。
どうでもいいけど ZeroMemory は Windows しか使えないと今頃知った。
やっぱり Windows でしかプログラミングをやらないのではバカになる。

端末入力なら下記でイケるみたい。
\n が入ってしまうので強引に \0 を挿入とかしているけど。

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

int
main (int argc, char *argv[])
{
    /* Including Japanese characters */
    gchar c[256];
    fgets(c, sizeof(c), stdin);
    /* Remove '\n' */
    c[strlen(c)-1] = '\0'; 
    /* Length */
    glong len = g_utf8_strlen(c, sizeof(c));
    g_printf ("%s is %d characters\n", c, len);
    /* Converted to UCS4 */
    GError * error = NULL;
    gunichar * ucs;
    ucs = g_utf8_to_ucs4 (c, -1, NULL, &len, &error);
    /* Export single character */
    int i = 0;
    for (i; i<len; i++) {
        gchar cc[4];
        memset(&cc, '\0', sizeof(cc));
        g_unichar_to_utf8 (ucs[i], cc);
        g_printf ("%s\n", cc);
    }
    g_free(ucs);
    return 0;
}

なんかもっとスマートな方法がありそうな気がするけど。

Gedit C lang

プログラミングへの意欲がすっかり無くなってきた君達(俺のこと…
ここいらで基本の基本をやり直ししたいところだ。

ということで gcc による C 言語コンパイルの基本を。
コンパイラやヘッダ類は導入済みという前提で。

Gedit を利用します。
プラグインの「コードスニペット」を有効にします。
プラグインの「外部ツール」を有効にします。

Manage External Tools… を開き以下を登録します。

#!/bin/sh
gcc $GEDIT_CURRENT_DOCUMENT_NAME

準備はコレだけです。

test.c という名前で空のファイルを作成し Gedit で開きます。
Inc と打ち込んでキーボードの Tab キーを叩きます、i だけ大文字。

上記のようなテキストが流し込まれたはず。
inc と i を小文字にすると自作ヘッダ用のダブルコーテーションになる。
Delphi(pascal) 屋の人は「それインクリメントじゃないの?」と勘違いしない。

後はお約束の stdio と打ち込み Tab キーで下方にカーソルが移動する。
スタンダード In/Out ヘッダと通ぶらずとも「スタジオえっち」で誰にでも通…(以下略
これで printf 関数が使えるようになる。

Enter キーで一段下げて main と打ち込み再び Tab キー。
main 関数が流し込まれ関数内にカーソルが移動するので printf() を書く。

コンパイルは最初に作成した外部ツールを実行するだけ。
Gedit は実はこんなに凄い!

、、、、、、、、、、

では Gedit の宣伝なので分割コンパイル方法も少し。
extern 宣言を使う方法もあるけど事実上ヘッダを利用する人しかいない。

cbr.h cbr.c という2つのファイルを同一ディレクトリに作成。

cbr.h

#include <stdio.h>

void
print_cbr(void);

stdio.h をインクルードし、関数プロトタイプのみを書く。

cbr.c

#include "cbr.h"

void
print_cbr(void)
{
    printf("でも CBR250R 買ってしまったし...\n");
}

cbr.h をインクルードし、プロトタイプの実体を作成する。

test.c も書き換え。

#include "cbr.h"

int
main (int argc, char *argv[])
{
    printf("新型 Ninja250 カッケェ!\n");
    print_cbr();
    return 0;
}

stdio.h は cbr.h で宣言されているので書く必要は無い。
プロトタイプがヘッダで宣言されているので print_cbr 関数が使える。

先ほどの外部ツールは残念ながら使えない。
$GEDIT_DOCUMENTS_PATH を使えば複数ファイルのリンクもできるけど *.c のみを全部開いている状態をビルド時に作る必要があるので逆に面倒くさい。
普通に Makefile を作って Ctrl+F8 のほうがいい。

Makefile

ninja: test.c cbr.c
	gcc -o ninja test.c cbr.c

cbr.tar.gz

以上 Gedit はこんなに凄い、ってだからぁ…
すっかり月一更新臭くなっているこのブログ、なんとかせねば。