Jump to content
RadStudio 10.3.1 was released today Read more... ×
Eugine Savin

operator overloding Equal vs Multiply

Recommended Posts

Simple question, why "Multiply" operator works, and "Equal" does not. compiler give error  "[dcc32 Error] Project1.dpr(33): E2015 Operator not applicable to this operand type"

 

program Project1;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils;

type
  TMyRecord2 = record
  public
    class operator Equal(const a: TMyRecord2; const b: array of Integer): boolean;
    class operator Multiply(const a: TMyRecord2; const b: array of Integer): TMyRecord2;
  end;

class operator TMyRecord2.Equal(const a: TMyRecord2; const b: array of Integer): boolean;
begin
  Result := True;
end;

class operator TMyRecord2.Multiply(const a: TMyRecord2; const b: array of Integer): TMyRecord2;
begin
  Result := a;
end;

var
  b: boolean;
  r: TMyRecord2;
begin
  try
    r := r * [10]; // this line is compiled
    b := r = [10]; // this line is not compiled
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

 

Edited by Eugine Savin
  • Like 1

Share this post


Link to post

Probably because in the second case "[10]" is seen as a set constant by the compiler. It works if you declare a variable with a proper type:

var
  b: boolean;
  a: array of Integer;
  r: TMyRecord2;
begin
  try
    a := [10];
    r := r * a; // this line is compiled
    b := r = a; // this line is not compiled
  except
    on E: Exception do
      Writeln(E.ClassName, ': ', E.Message);
  end;
end.

 

 

  • Like 1

Share this post


Link to post
44 minutes ago, Uwe Raabe said:

because in the second case "[10]" is seen as a set constant by the compiler

Well, where is not "set of Integer", but code

var i: Integer;
begin
  i := 10;
  b := r = [i];
end

is not compiled

Edited by Eugine Savin
typo

Share this post


Link to post

And main question, why 

r := r * [10] // here [10] is array

and

b := r = [10] // here [10] is set

 

what's difference ??

Edited by Eugine Savin

Share this post


Link to post

In Delphi a set is denoted with square brackets. Unfortunately a constant array is also denoted with square brackets. I have to admit that I am not happy with this language design decision either.

 

The term [10] as well as (i being an integer) can be both a set or array constant. There is no rule for the compiler to choose one or another. There are scenarios where the compiler chooses different than the developer intended.

 

If you feel that the compiler is doing wrong here, please open a bug report with this example.

 

Share this post


Link to post
3 hours ago, Uwe Raabe said:

In Delphi a set is denoted with square brackets. Unfortunately a constant array is also denoted with square brackets. I have to admit that I am not happy with this language design decision either.

 

The term [10] as well as (i being an integer) can be both a set or array constant. There is no rule for the compiler to choose one or another. There are scenarios where the compiler chooses different than the developer intended.

 

If you feel that the compiler is doing wrong here, please open a bug report with this example.

 

I fully agree.

 

It is never a good idea to give overridden operators variant open array parameters. You should have two (or sometimes one) single operands, not arrays. If you want to pass arrays, use functions.

Edited by Rudy Velthuis

Share this post


Link to post
10 hours ago, Rudy Velthuis said:

I fully agree.

 

It is never a good idea to give overridden operators variant open array parameters. You should have two (or sometimes one) single operands, not arrays. If you want to pass arrays, use functions.

Operators are functions. There are certainly times when it might be useful to use arrays as operator arguments. 

 

I regret that the language designers have allowed literals to have meaning determined by context in various places. When there is insufficient context, as is inevitably the case on occasion, these kind of problems arise. 

  • Like 1

Share this post


Link to post

To tell the compiler the type of operand, I can advise the following:

  1. Replace array of Integer with TArray<Integer>. (In 10.3 Rio it is the same type.)
  2. Create a helper function:
function AsArray(const [ref] Arr: TArray<Integer>): TArray<Integer>; inline;
begin
  Result := Arr;
end;
...
    b := r = AsArray([10]); // It compiles

 

Edited by Kryvich

Share this post


Link to post
2 hours ago, David Heffernan said:

Operators are functions. There are certainly times when it might be useful to use arrays as operator arguments.  

But not open array arguments, IMO. Somehow that is against the idea of operators. It feels wrong.

 

But that is my personal opinion.

Share this post


Link to post
13 minutes ago, Eugine Savin said:

What's problems with open arrays ? Anyway if I replace "array of Integer" by "TArray<Double>"  r * [10.0] - compiled, but r = [10.0] is not ( E2001 Ordinal type required)

 

// qc link https://quality.embarcadero.com/browse/RSP-23498

I'm not quite sure if you are confusing open array parameters and dynamic arrays. They look alike, but are not the same thing. Your TArray<Double> is not an "open array", it is a dynamic array.

 

I meant open array parameters. IMO, it doesn't make sense to give operators open array parameters. That would be like giving them three operands, e.g.: class operator Divide(const a, b: MyFloat; rounding: MyRoundingMode); It is obvious that that doesn't fit in the scheme x := operand1 / operand2;  In the same way, open array parameters do not fit in there, IMO.

 

Dynamic arrays do, of course, but I didn't mean those. Take a look at the concatenation of dynarrays: b := b + [4, 5, 6]; but those are not open array parameters.

 

FWIW, it is QP, not QC anymore.

Edited by Rudy Velthuis

Share this post


Link to post
21 minutes ago, Eugine Savin said:

r = [10.0]

Bad syntax, unless you mean to use them as default parameter. But default parameters don't match the notion of a dyadic operator either. It doesn't make a lot of sense to write: x := a +; where the second operand is a default value.

 

In other words: there are, IMO, some things that should not be used with operators that are fine with normal functions: open array parameters and default parameters are two of them. Operators may be functions, but they have special semantics and not everything is allowed. IMO, open array parameters and default parameters should be among the features not allowed for operators either.

Edited by Rudy Velthuis

Share this post


Link to post
1 minute ago, Rudy Velthuis said:

it doesn't make sense to give operators open array parameters.

 

TMyArray = record
..
end;

const Arr1: array[0..1] of Integer = (..)
const Arr2: array[0..2] of Integer = (..)
var MyArray: TMyArray ..

 

I wanna have ability to compare if (MyArray == Arr1) or (MyArray = Arr2)..   How can I get it without open arrays as operator parameters ?

Share this post


Link to post
15 minutes ago, Eugine Savin said:

 


TMyArray = record
..
end;

const Arr1: array[0..1] of Integer = (..)
const Arr2: array[0..2] of Integer = (..)
var MyArray: TMyArray ..

 

I wanna have ability to compare if (MyArray == Arr1) or (MyArray = Arr2)..   How can I get it without open arrays as operator parameters ?

Having dynamic array parameters in an operator declaration is fine. Having open array parameters is not, IMO. In your case, simply do:

function EqualArrays(const A: TMyArray; B: array of Integer): Boolean;

And don't use operators.

 

Hmmm... I see you use == C-like syntax. How would you do this in, say, C++? In C++, for an open array, you would have to pass a third parameter for the length of the static array, but how would you pass that? I doubt that the syntax allows it. I am pretty sure you would have to use... a plain function (or perhaps some template magic, but we don't have that in Delphi).

 

Edited by Rudy Velthuis

Share this post


Link to post
1 minute ago, Rudy Velthuis said:

How would you do this in, say, C++?

In C++ there is not differences between functions and overloaded operators.

struct MyArray {
public:
    template <std::size_t N>
    bool operator==(int (&rhs)[N]) { std::cout << N << '\n'; return true; }
};

int main() {
    // your code goes here
    int arr1 [1] = {0};
    int arr2 [2] = {0, 0};
    MyArray myArr;
    if (myArr == arr1 && myArr == arr2) {}
    return 0;
}

 

20 minutes ago, Rudy Velthuis said:

And don't use operators.

Can you explain why you allow operators with dyn arrays, and do not allow ones with open arrays ?

Share this post


Link to post
38 minutes ago, Eugine Savin said:

In C++ there is not differences between functions and overloaded operators.


struct MyArray {
public:
    template <std::size_t N>
    bool operator==(int (&rhs)[N]) { std::cout << N << '\n'; return true; }
};

int main() {
    // your code goes here
    int arr1 [1] = {0};
    int arr2 [2] = {0, 0};
    MyArray myArr;
    if (myArr == arr1 && myArr == arr2) {}
    return 0;
}

 

Can you explain why you allow operators with dyn arrays, and do not allow ones with open arrays ?

First: I don't "allow" anything. But I think it doesn't make sense, in Delphi.

 

As I said, it is possible with templates, because these can have value parameters like std::size_t N too. But that would still preclude dynamic arrays too, or their C++ equivalents.

 

I do notice that that creates TWO overloaded operators: one for single-element static arrays, and one for two-element static arrays. In other words, it has two types. In Delphi, you can do that too, with overloading, but then you would have to create a type and an overload for every size.

 

In other words: in Delphi, that doesn't make sense. Yes, it would be nice in your simple example, but if you have an open array parameter, it must accept dynarrays as well as static arrays of any size, and it must accept open array constructors (like a function Func([7, 8 9])). How would you do that, syntactically? Just like it doesn't make sense to give a dyadic operator like = or / a third operand, an open array constructor doesn't make sense either.

 

A dynamic array is different. It is a single item, a single type. You could also have a single statci array type. Or several. See above.

 

Can you explain why you can't use a function?

Edited by Rudy Velthuis

Share this post


Link to post
2 hours ago, Rudy Velthuis said:

But not open array arguments, IMO. Somehow that is against the idea of operators. It feels wrong.

 

But that is my personal opinion.

It feels wrong isn't much of an argument. If open array arguments are fine for functions why not for operators, which are just functions with syntactic sugar. 

 

Can you explain your objection with reason, rather than relying on what it feels like. 

 

And surely you understand why operators are often preferred to functions. 

Share this post


Link to post

@Eugine Savin Is it possible to rewrite your C++ snippet as:

    MyArray myArr;
    if (myArr == {0} && myArr == {0, 0}) {}

As I understand you need to inline array constants to the Delphi expressions.

Edited by Kryvich

Share this post


Link to post
9 hours ago, David Heffernan said:

It feels wrong isn't much of an argument. If open array arguments are fine for functions why not for operators, which are just functions with syntactic sugar.

There are other things that are not fine for operators. One of the parameters or the return value must be of the type of record; there can only be one or two parameters, depending on the operator; etc. So they are not just normal functions.

 

As I said, IMO it is against the notion of using operators to have open array parameters, i.e. against the semantics, not necessarily against the technical possibilities.

 

As I said, I can't really put my finger on it yet, but it simply feels wrong. Not everything that compiles (i.e. that the compiler allows) is the right thing to do.

 

> And surely you understand why operators are often preferred to functions.

 

Sure. That doesn't mean they are the best thing to use in all circumstances.

Edited by Rudy Velthuis

Share this post


Link to post
11 hours ago, Rudy Velthuis said:

Sure. That doesn't mean they are the best thing to use in all circumstances.

In this circumstance, for testing equality, why do you believe that a function is better than an operator? 

Share this post


Link to post
11 hours ago, Rudy Velthuis said:

So they are not just normal functions.

Methods aren't normal functions by that argument, so why are open array parameters fine for them? 

 

The real problem here is the decision to allow literals to have type determined by context. So [10] is either a set or an array. That is incompatible with other language features. Like method and operator overloading. 

 

There is a good reason why other languages don't attempt to infer type of literals from context. 

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×