0

I am using newest Delphi 10.3.3

I have several main classes, which are extended from same parent class and also some, let's call it, Reflection classes, which has also same parent. I want instance of main class to have link to corresponding reflection instance, but I run to this issue (I simplified it to this example):

Main classes:

TMainClass = class
  Link: TReflectionClass;
end;
TCarMainClass = class(TMainClass)
  Link: TCarReflectionClass;
end;

Reflection classes:

TReflectionClass = class;
TCarReflectionClass = class(TReflectionClass);

The problem lies in Link field. I want Link field in TCarMainClass to be directly defined as TCarReflectionClass to avoid type-casting on lot of pieces of code (and also risking some errors), however if I define class like this, TCarMainClass.Link just hides TMainClass.Link - it is defined as different field with same name. That is not good because it consumes extra memory and mainly I can't access that field from parent class, which I want (as pointer to generic instance).

Of course I can solve this by making field private of common type and define property setter/getter which handles re-typing in each class. But that is lot of extra code for these classes and also overhead on each get/set because of calling getter/setter methods.

My question is - is there some easy hack I missed, to tell compiler I want field in child class to occupy same memory location as certain field in parent class?

Thanks

micropro.cz
  • 598
  • 4
  • 17
  • 1
    Not sure about Delphi specifically, but in general what you're trying to do is not type-safe, because the field is mutable. If someone holds a reference to an instance of the base class, and the compile-time type is for the base class, then they can set its property value using the property type for the base class. That change will be visible via other references, including references whose compile-time type promises that the same property will have a stricter type than it's just been set to. So the change shouldn't be allowed in the first place; you should only do this with immutable fields. – kaya3 Dec 16 '19 at 17:00
  • 1
    Various solutions come to mind, including generics and interfaces, but neither will make this more lightweight than it is, and if they apply depends completely on how you're gonna use this. Your question is a bit vague in that regard. I'm curious about your performance demands, though, if adding getters and setters (an OOP good practice) is something you want to avoid here. – GolezTrol Dec 16 '19 at 17:22
  • @kaya3 Actually this case is type-safe, because also TCarReflectionClass is inherited from TReflectionClass, which is type of the other parent class. However is true (unless Delphi do special check for that case) it also allows unsafe casts. But Delphi doesn't care much about type safety, it is not managed like C#, so it allows to manually re-type any class to any class, leading to runtime error in worst case. So my question is more just about how to tell compiler what to do (if that is possible) than if that is possible at all - because manual casts works fine for me now, it is just annoying. – micropro.cz Dec 16 '19 at 17:24
  • @GolezTrol I access these Links a lot, like maybe tens millions times per second, during processing. When I write in a code like TCarReflectionClass(Obj.Link).DoSomething it is basically zero cost cast - it just tells compiler I know what I am doing and don't emit warning, but this cast does not output any code. When I use getter, it will need to call procedure, which will just do this virtual retype and basically converts to function with just Result := A; However, this is far from zero-cost and it affects performance a bit. – micropro.cz Dec 16 '19 at 17:27
  • Basically I just want to avoid writing TCarReflectionClass(Obj.Link) all the time in a code. I want compiler to know, that if Obj is TCarMainClass, then Link is not TReflectionClass but TCarReflectionClass. I use it really lot over a code and on different classes, and it really descrease code readability to have lot of these casts. – micropro.cz Dec 16 '19 at 17:29
  • If speed/performance is a concern, I think you're looking in the wrong place. I don't see memory usage being an issue, but rather heap allocations, especially in the code we cannot see which accesses this. Anywhere you have a local variable in a method which is called rapidly is a bottleneck in that regard. – Jerry Dodge Dec 16 '19 at 17:29
  • @Jerry Dodge I just want to avoid unnecessary procedure call, which is not needed. I don't want Delphi to do something whis can't do - it is just about some "macro" instead of manual cast. I don't want to do getter way, which makes absolutely unnecessary overhead, no matter if it cost 0.0001% or 1% of performance - it is still unnecessary. Also I need to write such getters which is again manual work. What I want is very simple thing - question is, if Delphi can do it or not - if it has some command I can't find or not. It is kind of having multiple fields of different type on same memory ... – micropro.cz Dec 16 '19 at 17:32
  • ... location, which Delphi can do. Like C++'s union type. Which is btw much more type unsafe than this. – micropro.cz Dec 16 '19 at 17:32
  • Ok. Then the answer is "no, there is no easy hack you've missed to tell the compiler that you want to have a field in the child class to be at the same memory location as a field in the parent class" – GolezTrol Dec 16 '19 at 18:09
  • @GolezTrol Thought so. There is actually `absolute` directive which does exactly what I want, but it saidly works only for variables and not class fields. – micropro.cz Dec 16 '19 at 18:13
  • Btw, you can do more or less what unions do in Delphi using [variant records](https://stackoverflow.com/questions/13324368/how-do-i-translate-a-c-union-into-delphi). But I guess you don't want to write a union that contains all TReflection descendents, and that's what you'd have to do to make that work.. But then you still don't have type safety. – GolezTrol Dec 16 '19 at 18:16
  • Is polymorphism an option for your case? – Delphi Coder Dec 16 '19 at 20:09
  • You really have a naming issue which is causing some confusion (at least for me) - don't name your classes XXXClass... these names are usually suited for class references (aka `TSoAndSoClass = class of TSoAndSo`) – Stefan Glienke Dec 17 '19 at 14:21

1 Answers1

1

and mainly I can't access that field from parent class

While it's true that parent class can't access the descendant's field, there is nothing preventing you from synchronizing the 2.

procedure TCarMainClass.SetLink(const Value : TCarReflectionClass);
begin
  FLink := Value;
  TMainClass(Self).FLink := Value;
end;

Now, if you absolutely require no additionnal memory use and no setters, the only remaining option (that I can think of at this time) is as GolezTrol suggested, generics.

TMainClass<T : TReflectionClass> = class
  Link: T;
end;

TCarMainClass = class(TMainClass<TCarReflectionClass>)
end;

But that would probably break your design though, because TCarMainClass would be incompatible with TMainClass<TReflectionClass>. (if you want to understand why, search for the terms covariance/contravariance).

Ken Bourassa
  • 6,363
  • 1
  • 19
  • 28