...ing logging 4.0

はてなブログに移行しました。D言語の話とかいろいろ。

typedef invariant(ubyte)[] mbstring; //(2)

http://d.hatena.ne.jp/haru-s/20080405/1207389642 の続き.
詰まっていたところを教えていただいたおかげでフォーマット指定ができるようになった.

main.d
import std.stdio;
import stdex.windows.mbstring;

void main()
{
    mbstring m = toMBS("abあい");
    string c = toUTF8(m);
    wstring w = toUTF16(m);
    dstring d = toUTF32(m);
    
    with (stdex.windows.mbstring)
    {
        writefln("----> stdex.windows.mbstring.*");
        writefln("%s:%s:", m, 'X', w, 3.14, "/%s/%s/%s/", c, 123+456i, d, ["a","b"], true);
        
        assert(m.length == 7);
        
        assert(toMBS("abあいabあい") != m ~ m); // !!!!!!!!
        assert(toMBS("abあいabあい") == join(m, m));
        
        // クラスにまとめてみた
        MBString s = new MBString(m);
        MBString t = new MBString(c);
        assert(s.length == m.length);
        assert(s == t);
        assert(s.toString() == c);
        assert(s.toMBString() == toMBS("abあい"));
        assert(s ~ t == toMBS("abあいabあい"));
        assert(s ~ m == toMBS("abあいabあい"));
        t ~= s;
        assert(t.toMBString() == toMBS("abあいabあい"));
        s ~= "cdかき";
        assert(s.toMBString() == toMBS("abあいcdかき"));
    }
    
    with (std.stdio)
    {
        writefln("----> std.stdio.*");
        writefln("%s:%s:", m, 'X', w, 3.14, "/%s/%s/%s/", c, 123+456i, d, ["a","b"], true);
    }
}
stdex.windows.mbstring.d は最後に添付
出力結果
----> stdex.windows.mbstring.*
abあい:X:abあい3.14/abあい/123+456i/abあい/[a,b]true
----> std.stdio.*
[97 98 130 160 130 162 0]:X:ab縺ゅ> 3.14/%s/%s/%s/ab縺ゅ> 123+456iab縺ゅ> [a b]true
特徴
  • string/wstring/dstring と mbstring が混在していても文字化けしない.
  • ["a","b"] の出力結果が [a b] ではなく [a,b] になって std.stdio.* と異なる.意図しない挙動だがまあいいか?
  • stdex.windows.mbstring.writef*() のフォーマット指定が昔の std.stdio.writef*() と同様に振る舞い,フォーマット指定子の数の分だけそれ以降に並ぶ引数が評価される.何かの副作用で気が付いたらそうなってたw
  • mbstring 同士の ~ による結合がうまくいかない.std.windows.mbstring.join() を使う必要あり.これはどうにもならんのだろうか.
assert(toMBS("abあいabあい") != m ~ m); // !!!!!!!!
  • mbstring を内包するクラス MBStinrg を使えば,上記の問題は見えなくすることができる.
感想

writef*() がマルチバイト文字列を認識してくれるので楽なんじゃないだろうか.
ただ,mbstring 同士の ~ による結合がうまくいかないのが辛い.これを回避するために MBString クラスを作ってみたが,それだったら C++ の std::basic_string みたいに文字と文字列を別々に扱う仕組みの方がいいのかもしれない.
mbstring を idup すると,aliastypedef する前の invariant(ubyte)[] が返されてしまって明示的に cast(mbstring) しなければならないことに気が付いた.これ暗黙的にできるんじゃないの?

class MBString {
    private mbstring s_;
    mbstring toMBString() {
        return cast(mbstring)s_.idup;
    }

以上,aliastypedef invariant(ubyte)[] mbstring; をマルチバイト文字列として使ってみた感じでした.
どうでしょう?

追記(2008/04/13)

ん・・・ MBString 内の .idup はすべていらないような気がしてきた.

追記(2008/04/14)

やっぱり .idup はいらないよね.消しておこう.
メンバ関数から返すときだって invariant なデータなんだから共有しても問題ないだろうし.

stdex.windows.mbstring.d
module stdex.windows.mbstring;

import std.string;
import std.format;
import std.stdio : p = writef, pln = writefln;
import std.cstream;
import std.typetuple;
import std.traits;
import std.c.windows.windows;

// multi byte string
typedef invariant(ubyte)[] mbstring;

class MBString
{
    private mbstring s_;
    
    this(mbstring s)
    {
        s_ = s;
    }
    this(MBString other)
    {
        s_ = other.s_;
    }
    this(string s)
    {
        s_ = toMBS(s);
    }
    
    uint length()
    {
        return s_.length;
    }
    
    mbstring opCat(mbstring other)
    {
        return .join(s_ , other);
    }
    mbstring opCat(MBString other)
    {
        return this.opCat(other.s_);
    }
    mbstring opCat(string other)
    {
        return this.opCat(toMBS(other));
    }
    
    mbstring opCatAssign(mbstring other)
    {
        return s_ = .join(s_, other);
    }
    mbstring opCatAssign(MBString other)
    {
        return this.opCatAssign(other.s_);
    }
    mbstring opCatAssign(string other)
    {
        return this.opCatAssign(toMBS(other));
    }
    
    override int opEquals(Object other)
    {
        return s_ == (cast(MBString)other).s_;
    }
    
    override string toString()
    {
        return toUTF8(s_);
    }
    
    mbstring toMBString()
    {
        return s_;
    }
}

/+  to UTF8,16,32 from MBS
 +
 + codePage = is the number of the target codepage, or
 +   0 - ANSI,
 +   1 - OEM,
 +   2 - Mac
 +/
wstring toUTF16(mbstring s, uint codePage=0)
{
    wstring result;
    result.length = MultiByteToWideChar(codePage, 0, cast(char*)s, s.length, null, 0);
    MultiByteToWideChar(codePage, 0, cast(char*)s, s.length, cast(wchar*)result, result.length);
    return result;
}
string toUTF8(mbstring s)
{
    return std.utf.toUTF8(.toUTF16(s));
}
dstring toUTF32(mbstring s)
{
    return std.utf.toUTF32(.toUTF16(s));
}

/+ to MBS from UTF8,16,32 and others
 +
 + codePage = is the number of the target codepage, or
 +   0 - ANSI,
 +   1 - OEM,
 +   2 - Mac
 +/
mbstring toMBS(T)(T arg, uint codePage=0)
{
    static if(is(T : string))
    {
        ubyte[] result;
        const(wchar*) ws = std.utf.toUTF16z(arg);
        result.length = WideCharToMultiByte(codePage, 0, ws, -1, null, 0, null, null);
        WideCharToMultiByte(codePage, 0, ws, -1, cast(char*)result, result.length, null, null);
        return cast(mbstring)result.idup;
    }
    else
        return toMBS(std.string.format(arg));
}

void mbswritef(...)
{
    doFormat(
        (dchar c) {
            mbstring s = toMBS([c]);
            dout.writeBlock(s.ptr, s.length - 1);
            //p("(",s.length - 1,")");
        },
        _arguments, _argptr);
}
void mbswritefln(TL...)(TL t)
{
    mbswritef(t, "\n");
}

mbstring join(mbstring lhs, mbstring rhs)
{
    return cast(mbstring)(lhs[0 .. $-1] ~ rhs);
}

private template toDynamicArray(T)
{
    static if(!isStaticArray!(T))
        alias T toDynamicArray;
    else
        alias typeof((*cast(T*)null)[]) toDynamicArray;
}

private template tmap(alias F)
{
    alias TypeTuple!() tmap;
}
private template tmap(alias F, T, U...)
{
    alias TypeTuple!(F!(T), tmap!(F, U)) tmap;
}

void writef(TL...)(TL args)
{
    alias ReplaceAll!(mbstring, string, tmap!(toDynamicArray, TL)) U;
    U arg;
    foreach (i, v; args)
        static if (is(typeof(args[i]) : mbstring))
            arg[i] = toUTF8(cast(mbstring)args[i].idup);
        else
            arg[i] = args[i];
    mbswritef(arg);
}
void writefln(TL...)(TL t)
{
    .writef(t, "\n");
}