DCC警告W1050の回避方法

投稿者:: Yusuke Konno

概要: Delphi 2009で頻繁に出現する警告への対処法を紹介します。

Delphi 2009の使い始めの頃であれば、「W1050 set 式で WideChar がバイト char に縮小されました」という警告によく遭遇すると思います。これはUnicode化による影響が原因で発生しています。本稿ではDCC警告W1050の回避方法について紹介します。

    DCC警告W1050の概要

    文字列とin演算子

Delphiにおいて、ある文字列中に特定の文字があるかどうかを判定する方法は様々なものがありますが、in演算子を用いた判定方法が広く使われています。

var
  S: set of Char;
begin
  S := ['A'..'Z'];
  if ('A' in S) then
  begin
    {何らかの処理}
  end;
end;

上記コードは変数S中にAが含まれているかどうかを判定し、含まれていたら何らかの処理を行うというコードです。Delphi 2007まではこのコードは正常に動作しています。

しかし、上記コードをDelphi 2009でコンパイルすると、「DCC警告W1050 set 式で WideChar がバイト char に縮小されました」 が発生してしまいます。

    なぜこの警告が発生するのか?

Delphi 2009においてはUnicode化によって、デフォルトの文字列型であるStringがUnicodeStringになっています。その影響で、Char型もWideCharへマップされています。これに対しコンパイラはset式の値の範囲を1バイトと解釈します。これは既存のコードとの整合性のためです。これに対しWideCharは16ビット、すなわち2バイトの大きさの変数型です。つまりWideCharはset式のキャパシティを超えてしまっているので、この警告が出てしまいます。

    解決策

    CharInSet関数を使う

そもそもこの警告の全文を略さずに出すと、

[DCC 警告] XXX.pas(XXX): W1050 set 式で WideChar がバイト char に縮小されました。'CharInSet' 関数を 'SysUtils' ユニットで使用することを検討してください。

となっています。つまり、CharInSet関数を用いるのがこの問題の"正しい回避方法"です。

var
  S: set of Char;
begin
  S := ['A'..'Z'];
  if CharInSet('A',S) then
  begin
    {~~~略~~~}
  end;
end;

上記のコードで警告は発生しなくなります。欠点としては若干遅い(とはいっても大して気になることはないはず)のと、このコードを2007以前のDelphiに移植すると動作しない事でしょうか。

通常はこれでOKです。

AnsiChar向けのCharInSet
何故かCharInSet関数は、Char/WideChar向けのものだけではなく、AnsiChar向けのものがオーバーロードされています。AnsiCharであればサイズが1バイトであるため、set式の値の範囲と一致しています。すなわち、従来のin演算子を用いたコードが問題なく動作します。したがって、AnsiCharを用いる場合にはCharInSetは特に使う必要がないでしょう。

    Ord関数を使う

過去のプロダクトとの互換性を維持できる方法です。下記のようにコードを書き換えます。

begin
  if (Ord('A') in [Ord('A').. Ord('Z')]) then
  begin
    {何らかの処理}
  end;
end;

上記コードは2007以前のDelphiでもしっかりと動作します。

    Character.pas内のルーチンを使う

特定の文字が数字であるかどうかを判定したい、アルファベットかどうかを判定したい、といったような場合であれば、Character.pasに収録されているルーチン(IsNumber関数など)を使うのがベターです。以下の例では数字かどうかを判定し、数字であれば処理を行うというものです。

interface

uses
  ..., Character;

  {~~~略~~~}

implementation

  {~~~略~~~}

begin
  if IsNumber('0') then
  begin
    {~~~略~~~}
  end;
end;

Characterをuses節に加える必要があります。この他にもIsLetter関数やIsSymbol関数などを使えば割と簡単に文字の種類を判定できます。ただし、これらのルーチンは2009から登場したので、当然のことながら2007以前では使えません。

    まとめ:互換性維持かどうかを念頭に

Character.pas内のルーチンは便利ですが、互換性を維持できません。互換性を重視する人はOrd関数を使う、そうでなければCharacter.pas内のルーチンあるいはCharInSet関数を用いればよいと思います。

次からのサーバー応答:: ETNASC04