Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
2 tasks done
Cirras opened this issue Nov 24, 2023 · 0 comments
Open
2 tasks done

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

Cirras opened this issue Nov 24, 2023 · 0 comments
Labels
engine Improvements to the core engine enhancement Improvements to an existing feature

Comments

@Cirras
Copy link
Collaborator

Cirras commented Nov 24, 2023

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}
@Cirras Cirras added the enhancement Improvements to an existing feature label Nov 24, 2023
@fourls fourls added the engine Improvements to the core engine label Nov 27, 2023
@fourls fourls changed the title [Engine Improvement]: Support operator overloads implemented by &&op_* class functions Support operator overloads implemented by &&op_* class functions Nov 27, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
engine Improvements to the core engine enhancement Improvements to an existing feature
Projects
None yet
Development

No branches or pull requests

2 participants