Skip to content

Support operator overloads implemented by &&op_* class functions #103

Open
@cirras

Description

@cirras

Prerequisites

  • This improvement has not already been suggested.
  • This improvement would be generally useful, not specific to my code or setup.

Engine area

Delphi language support

Improvement description

Background

This is an undocumented implementation detail of the Delphi compiler.

Class operators are compiled to internal class functions, prefixed with a double-ampersand and internal operator name.
It's possible to manually implement these class functions, circumventing the ordinary rules around class overloads.

If you're feeling particularly clever, this lets you implement operator overloads in places where they're not ordinarily allowed (like record helpers).

Identifying internal names

We can find all of the internal operator names by emitting C++ builder headers for a Delphi record with operator overloads defined.

Given Delphi record TFoo:

type
  TFoo = record
  private
    FValue: Integer;

  public
    constructor Create(AValue: Integer);

    class operator Implicit(AValue: Integer): TFoo;
    class operator Explicit(AValue: Integer): TFoo;
    class operator Negative(const A: TFoo): TFoo;
    class operator Positive(const A: TFoo): TFoo;
    class operator LogicalNot(const A: TFoo): TFoo;
    class operator Inc(const A: TFoo): TFoo;
    class operator Dec(const A: TFoo): TFoo;
    class operator Trunc(const A: TFoo): TFoo;
    class operator Round(const A: TFoo): TFoo;
    class operator In(const A: Integer; const B: TFoo): Boolean;
    class operator Equal(const A, B: TFoo): Boolean;
    class operator NotEqual(const A, B: TFoo): Boolean;
    class operator GreaterThan(const A, B: TFoo): Boolean;
    class operator GreaterThanOrEqual(const A, B: TFoo): Boolean;
    class operator LessThan(const A, B: TFoo): Boolean;
    class operator LessThanOrEqual(const A, B: TFoo): Boolean;
    class operator Add(const A, B: TFoo): TFoo;
    class operator Subtract(const A, B: TFoo): TFoo;
    class operator Multiply(const A, B: TFoo): TFoo;
    class operator Divide(const A, B: TFoo): TFoo;
    class operator IntDivide(const A, B: TFoo): TFoo;
    class operator Modulus(const A, B: TFoo): TFoo;
    class operator LeftShift(const A, B: TFoo): TFoo;
    class operator RightShift(const A, B: TFoo): TFoo;
    class operator LogicalAnd(const A, B: TFoo): TFoo;
    class operator LogicalOr(const A, B: TFoo): TFoo;
    class operator LogicalXor(const A, B: TFoo): TFoo;
    class operator BitwiseAnd(const A, B: TFoo): TFoo;
    class operator BitwiseOr(const A, B: TFoo): TFoo;
    class operator BitwiseXor(const A, B: TFoo): TFoo;
    class operator Assign(var Dest: TFoo; var Src: TFoo);
    class operator Include(const A, B: TFoo): TFoo;
    class operator Exclude(const A, B: TFoo): TFoo;
    class operator True(const Src: TFoo): Boolean;
    class operator False(const Src: TFoo): Boolean;
    class operator OnesComplement(const Src: TFoo): TFoo;

    property Value: Integer read FValue;
  end;

The following C++ type is emitted:

struct DECLSPEC_DRECORD TFoo
{
private:
	int FValue;
	
public:
	__fastcall TFoo(int AValue);
	static TFoo __fastcall _op_Implicit(int AValue);
	static TFoo __fastcall _op_Explicit(int AValue);
	static TFoo __fastcall _op_UnaryNegation(const TFoo A);
	static TFoo __fastcall _op_UnaryPlus(const TFoo A);
	static TFoo __fastcall _op_LogicalNot(const TFoo A);
	static TFoo __fastcall _op_Increment(const TFoo A);
	static TFoo __fastcall _op_Decrement(const TFoo A);
	static TFoo __fastcall _op_Trunc(const TFoo A);
	static TFoo __fastcall _op_Round(const TFoo A);
	static bool __fastcall _op_In(const int A, const TFoo B);
	static bool __fastcall _op_Equality(const TFoo A, const TFoo B);
	static bool __fastcall _op_Inequality(const TFoo A, const TFoo B);
	static bool __fastcall _op_GreaterThan(const TFoo A, const TFoo B);
	static bool __fastcall _op_GreaterThanOrEqual(const TFoo A, const TFoo B);
	static bool __fastcall _op_LessThan(const TFoo A, const TFoo B);
	static bool __fastcall _op_LessThanOrEqual(const TFoo A, const TFoo B);
	static TFoo __fastcall _op_Addition(const TFoo A, const TFoo B);
	static TFoo __fastcall _op_Subtraction(const TFoo A, const TFoo B);
	static TFoo __fastcall _op_Multiply(const TFoo A, const TFoo B);
	static TFoo __fastcall _op_Division(const TFoo A, const TFoo B);
	static TFoo __fastcall _op_IntDivide(const TFoo A, const TFoo B);
	static TFoo __fastcall _op_Modulus(const TFoo A, const TFoo B);
	static TFoo __fastcall _op_LeftShift(const TFoo A, const TFoo B);
	static TFoo __fastcall _op_RightShift(const TFoo A, const TFoo B);
	static TFoo __fastcall _op_LogicalAnd(const TFoo A, const TFoo B);
	static TFoo __fastcall _op_LogicalOr(const TFoo A, const TFoo B);
	static TFoo __fastcall _op_ExclusiveOr(const TFoo A, const TFoo B);
	static TFoo __fastcall _op_BitwiseAnd(const TFoo A, const TFoo B);
	static TFoo __fastcall _op_BitwiseOr(const TFoo A, const TFoo B);
	static TFoo __fastcall _op_BitwiseXOR(const TFoo A, const TFoo B);
	static void __fastcall _op_Assign(TFoo &Dest, TFoo &Src);
	static TFoo __fastcall _op_Include(const TFoo A, const TFoo B);
	static TFoo __fastcall _op_Exclude(const TFoo A, const TFoo B);
	static bool __fastcall _op_True(const TFoo Src);
	static bool __fastcall _op_False(const TFoo Src);
	static TFoo __fastcall _op_OnesComplement(const TFoo Src);
	__property int Value = {read=FValue};
	TFoo() {}
	
	TFoo& operator =(int AValue) { *this = TFoo::_op_Implicit(AValue); return *this; }
	TFoo operator -() { return TFoo::_op_UnaryNegation(*this); }
	TFoo operator +() { return TFoo::_op_UnaryPlus(*this); }
	TFoo operator !() { return TFoo::_op_LogicalNot(*this); }
	TFoo& operator ++() { *this = TFoo::_op_Increment(*this); return *this; }
	TFoo operator ++(int) { TFoo tmp = *this; *this = TFoo::_op_Increment(*this); return tmp; }
	TFoo& operator --() { *this = TFoo::_op_Decrement(*this); return *this; }
	TFoo operator --(int) { TFoo tmp = *this; *this = TFoo::_op_Decrement(*this); return tmp; }
	friend bool operator ==(const TFoo A, const TFoo B) { return TFoo::_op_Equality(A, B); }
	friend bool operator !=(const TFoo A, const TFoo B) { return TFoo::_op_Inequality(A, B); }
	friend bool operator >(const TFoo A, const TFoo B) { return TFoo::_op_GreaterThan(A, B); }
	friend bool operator >=(const TFoo A, const TFoo B) { return TFoo::_op_GreaterThanOrEqual(A, B); }
	friend bool operator <(const TFoo A, const TFoo B) { return TFoo::_op_LessThan(A, B); }
	friend bool operator <=(const TFoo A, const TFoo B) { return TFoo::_op_LessThanOrEqual(A, B); }
	friend TFoo operator +(const TFoo A, const TFoo B) { return TFoo::_op_Addition(A, B); }
	friend TFoo operator -(const TFoo A, const TFoo B) { return TFoo::_op_Subtraction(A, B); }
	friend TFoo operator *(const TFoo A, const TFoo B) { return TFoo::_op_Multiply(A, B); }
	friend TFoo operator /(const TFoo A, const TFoo B) { return TFoo::_op_Division(A, B); }
	friend TFoo operator %(const TFoo A, const TFoo B) { return TFoo::_op_Modulus(A, B); }
	friend TFoo operator <<(const TFoo A, const TFoo B) { return TFoo::_op_LeftShift(A, B); }
	friend TFoo operator >>(const TFoo A, const TFoo B) { return TFoo::_op_RightShift(A, B); }
	friend TFoo operator &&(const TFoo A, const TFoo B) { return TFoo::_op_LogicalAnd(A, B); }
	friend TFoo operator ||(const TFoo A, const TFoo B) { return TFoo::_op_LogicalOr(A, B); }
	friend TFoo operator &(const TFoo A, const TFoo B) { return TFoo::_op_BitwiseAnd(A, B); }
	friend TFoo operator |(const TFoo A, const TFoo B) { return TFoo::_op_BitwiseOr(A, B); }
	friend TFoo operator ^(const TFoo A, const TFoo B) { return TFoo::_op_BitwiseXOR(A, B); }
	TFoo operator ~() { return TFoo::_op_OnesComplement(*this); }
};

Name data

Public operator name Internal class function name
Implicit &&op_Implicit
Explicit &&op_Explicit
Negative &&op_UnaryNegation
Positive &&op_UnaryPlus
LogicalNot &&op_LogicalNot
Inc &&op_Increment
Dec &&op_Decrement
Trunc &&op_Trunc
Round &&op_Round
In &&op_In
Equal &&op_Equality
NotEqual &&op_Inequality
GreaterThan &&op_GreaterThan
GreaterThanOrEqual &&op_GreaterThanOrEqual
LessThan &&op_LessThan
LessThanOrEqual &&op_LessThanOrEqual
Add &&op_Addition
Subtract &&op_Subtraction
Multiply &&op_Multiply
Divide &&op_Division
IntDivide &&op_IntDivide
Modulus &&op_Modulus
LeftShift &&op_LeftShift
RightShift &&op_RightShift
LogicalAnd &&op_LogicalAnd
LogicalOr &&op_LogicalOr
LogicalXor &&op_ExclusiveOr
BitwiseAnd &&op_BitwiseAnd
BitwiseOr &&op_BitwiseOr
BitwiseXor &&op_BitwiseXOR
Assign &&op_Assign
Include &&op_Include
Exclude &&op_Exclude
True &&op_True
False &&op_False
OnesComplement &&op_OnesComplement

Rationale

Some Delphi libraries actually make use of this undocumented language "feature" to provide operator overloads in record helpers.

Spring4D is the most prominent example, using this approach to polyfill older versions of Delphi:

{$IFNDEF DELPHIXE3_UP}
  TMethodHelper = record helper for TMethod
  public
    class function &&op_Equality(const left, right: TMethod): Boolean; static; inline;
    class function &&op_Inequality(const left, right: TMethod): Boolean; static; inline;
    class function &&op_GreaterThan(const left, right: TMethod): Boolean; static; inline;
    class function &&op_LessThan(const left, right: TMethod): Boolean; static; inline;
  end;
{$ENDIF}

Metadata

Metadata

Assignees

No one assigned

    Labels

    engineImprovements to the core engineenhancementImprovements to an existing feature

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions