要点
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ユーザーはとても助かる。