2010-10-21-01-文字コードの話 - 3 - project-enigma

2010-10-21-01-文字コードの話 - 3

>> Site top >> weblog >> 月別アーカイブ >> 2010年10月のlog >> 2010-10-21-01-文字コードの話 - 3

最終更新日付:2013/12/31 07:35:54


文字コードの話 - 3

2010 年 10 月 21 日

通常はインライン展開で効率を重視し、複数のエンコーディングを動的に切替えたい場合は仮想関数が使える‥‥‥そんな都合のいい設計をどう実装するか。

まず確認しておかなければならない重要なことは、動的切替えのために仮想関数を使用するということは、クラスユーティリティ(つまりクラススコープのメソッドだけで構成される、インスタンスを作成しないクラス)にはできないということだ。従って、クラスのインスタンスを作成し、そのメソッドをコールする必要がある。

簡単のため、メソッドは1つにしておこう。名前空間 Encoding 配下に複数のクラスを作っておく。Increment は、ポインタを「1文字分進める」ためのメソッドだ。

namespace Encoding {
    class SJIS {
    public:
        inline const char* Increment( const char* p ) const { ... };
    };
    class EUCJP {
    public:
        inline const char* Increment( const char* p ) const { ... };
    };
}

ちなみに、これらのクラスは handler という名前で自クラスのオブジェクトを静的メンバとして持っている。だから typedef 名に対して以下のようにできる。

 typedef Encoding::SJIS  EncodingT;
 p = EncodingT::handler.Increment( p );

また、それとは別にクラス名を小文字にした大域オブジェクトもあるので、以下のようにもできる。

 p = eucjp.Increment( p );

さて、問題は動的解決だ。まず、Dynamic というクラスを用意する。このクラスも同じシグネチャのメソッドを持つが、全て純粋仮想関数となる。そして、そこから派生するオブジェクトを取得するための列挙値とメソッドを提供する。

namespace Encoding {
    class Dynamic {
    public:
        enum Type {
            TYPE_ASCII,
            TYPE_EUCJP,
            TYPE_SJIS,
                :
                :
            TYPE_UTF8
        };
    public:
        static const Dynamic& GetEncoding( Type type );
    public:
        virtual const char* Increment( const char* p ) const = 0;
    };
}

これにより、動的な解決ができるようになる。以下では EUC-JP を実装した Encoding::Dynamic 派生クラスオブジェクトを取得している。

typedef Encoding::Dynamic  EncodingT;
const EncodingT& enc = EncodingT::GetEncoding( EncodingT::TYPE_EUCJP );

GetEncoding メソッドが const 参照を返すことに御注目だ。クラスごとの handler 静的メンバのように、Dynamic では各種の派生クラスオブジェクトが用意されており、GetEncoding を通して取得できるようになっている。

これで、ひとまず前回の要件を満たすだけの話が揃ったことになる。以下のようにtypedef とそのクラスのオブジェクトを取得する部分だけが切替えられれば、それ以外の場所は EncodingT と enc の伝播を受けるだけで済む。Dynamic 以外の場合はコンパイラ判断で可能な限りインライン展開されるし、Dynamic ならば動的な文字コードの切り換えが可能になる。

    typedef Encoding::SJIS     EncodingT;
    const EncodingT& enc = EncodingT::handler;
 
    typedef Encoding::Dynamic  EncodingT;
    const EncodingT& enc = EncodingT::GetEncoding( EncodingT::TYPE_EUCJP );

では、Dynamic 派生クラスはどのように実装されているか。例を見てみよう。

namespace Encoding {
    class DynamicSJIS : public Dynamic {
    public:
        virtual const char* Increment( const char* p ) const {
            return sjis.handler.Increment( p );
        };
    };
}

簡単だ。Dynamic から派生したクラスがあり、メソッドの内部では非 Dynamic 派生クラスのメソッドをコールしているだけ。そして、この Dynamic 派生クラスのオブジェクトも用意されている。

   DynamicSJIS   dynSJIS;

先に書いたとおり、これを返すのは Dynamic::GetEncoding() だ。

const Dynamic& Dynamic::GetEncoding( Type type ) {
    switch( type ) {
    case TYPE_ASCII:    return dynASCII;
    case TYPE_EUCJP:    return dynEUCJP;
    case TYPE_SJIS:        return dynSJIS;
        :
        :
    case TYPE_UTF8:        return dynUTF8;
    }
    return dynASCII;
}

こんなところだろうか。次回は、より細部の面倒で仕方がない部分の話をしよう。

 

コメント

このページにコメントする

 

このページのタグ

Page tag : 開発

 

 


Copyright(C) 2005-2017 project-enigma.
Generated by CL-PREFAB.