C++BuilderでDelphiのオーバーロードされた演算子を使うための工夫

要点

Delphiでオーバーロードした演算子は、C++Builderでは普通の関数に変換されます。
Delphiの「HPPEMIT」コンパイラ指令でC++Builder用の演算子のコードをDelphiのユニットに埋め込んでおくと、C++BuilderでもDelphiと同じように使用できるようになります。

本題

Delphi XE2以降ならBCDの演算子が使えるようになった。
ソースコードを簡単でわかりやすく記述できる。
参考:「» Delphi » 入門者のための言語の文法と IDE » BCD 演算 (XE2 以降)

Delphi XEまでは関数を使用していた。

var
  Value1, Value2, Value3: TBcd;
begin
  Value1 := 123;
  Value2 := 456;
  BcdAdd(Value1, Value2, Value3);

Delphi XE2以降は演算子で計算できるようになった。

var
  Value1, Value2, Value3: TBcd;
begin
  Value1 := 123;
  Value2 := 456;
  Value3 := Value1 + Value2;

Delphiでオーバーロードされた演算子は、C++Builderでは演算子ではなく関数になる。

たとえばTBcdのAdd演算子は、C++Builderでは次のようになる。

struct DECLSPEC_DRECORD TBcd
{
public:
    static TBcd __fastcall _op_Addition(const TBcd &A, const TBcd &B);
}

したがって、演算子を使うならC++Builderではこのように書くことになる。

TBcd Value1 = IntegerToBcd(123);
TBcd Value2 = IntegerToBcd(456);
TBcd Value3 = TBcd::_op_Addition(Value1, Value2);

Delphiでレコード型のオーバーロードされた演算子がC++Builderでは_op_XXXという関数になるのは、TBcdに限らない。
すべてのレコード型のオーバーロードされた演算子で、このように置き換えられる。

たとえばDelphiで次のようなレコード型を作る。

type
  TEmployee = record
  private
    FId: Integer;
    FName: string;
  public
    property Id:Integer read FId write FId;
    property Name:string read FName write FName;
    class operator Equal(const A, B: TEmployee): Boolean;
  end;

{ TEmployee }

class operator TEmployee.Equal(const A, B: TEmployee): Boolean;
begin
  Result := A.FId = B.FId;
end;

これをC++Builderに持って行くと、次のように変換される。

struct DECLSPEC_DRECORD TEmployee
{
private:
    int FId;
    System::UnicodeString FName;

public:
    __property int Id = {read=FId, write=FId};
    __property System::UnicodeString Name = {read=FName, write=FName};
    static bool __fastcall _op_Equality(const TEmployee &A, const TEmployee &B);
};

Delphiのoperator Equal関数がC++Builderでは_op_Equality関数になっている。

よって、Delphiの次のコードはコンパイルできるが、

procedure TestTEmployee.TestEqual;
var
  A, B: TEmployee;
begin
  Check(A = B);
end;

C++Builderの次のコードはコンパイルエラーになる。

void __fastcall TestEqual() {
  TEmployee A, B;
  Check(A == B);
}

[bcc32 エラー] TestTEmployee.cpp(17): E2093 == 演算子が使われたがクラス TEmployee には定義が存在しない

そこで、Delphiのコンパイラ指令「HPPEMIT指令」を使ってみる。
「HPPEMIT指令」を使うと、DelphiのユニットにC++Builder用のコードを埋め込むことができる。

type
  TEmployee = record
  private
    FId: Integer;
    FName: string;
  public
    property Id:Integer read FId write FId;
    property Name:string read FName write FName;
    class operator Equal(const A, B: TEmployee): Boolean;
  end;

(*$HppEmit END 'bool operator==(const TEmployee& A, const TEmployee& B) {' *)
(*$HppEmit END '  return TEmployee::_op_Equality(A, B);' *)
(*$HppEmit END '}' *)

こうすると、C++Builderでは次のようなコードが作成される。

struct DECLSPEC_DRECORD TEmployee
{
private:
    int FId;
    System::UnicodeString FName;

public:
    __property int Id = {read=FId, write=FId};
    __property System::UnicodeString Name = {read=FName, write=FName};
    static bool __fastcall _op_Equality(const TEmployee &A, const TEmployee &B);
};
//-- user supplied -----------------------------------------------------------
bool operator==(const TEmployee& A, const TEmployee& B) {
  return TEmployee::_op_Equality(A, B);
}

これで望み通り、C++BuilderでもDelphiと同じようにコードを書けるようになった。

void __fastcall TestEqual() {
  TEmployee A, B;
  A.Id = 1;
  A.Name = "Alice";
  B.Id = 1;
  B.Name = "Bob";

  Check(A == B);
}

DelphiとC++Builderの両方で使うレコード型を作成するときには、C++Builder用の演算子のコードをDelphiのユニットに埋め込んでおくと、C++Builderユーザーはとても助かる。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください