In Delphi, how do I make a type passable only between functions/methods of a class [closed] - class

Closed. This question is not reproducible or was caused by typos. It is not currently accepting answers.
This question was caused by a typo or a problem that can no longer be reproduced. While similar questions may be on-topic here, this one was resolved in a way less likely to help future readers.
Closed 7 years ago.
Improve this question
I currently have the following code;
interface
{...}
type
TMyRecord = record
List : TShellList;
Tree : TShellTree;
Image : TImage;
end;
TSDIAppForm = class(TForm)
{ Published declarations }
private
function GetChildren(Sheet : TTabSheet) : TMyRecord;
public
{ Public declarations }
end;
As I understand it this means that TMyRecord is a global type visible to the whole program. The type only needs to be visible within the class, although objects of the type do need to be passed and returned as parameters to between "private" functions/procedures of the class. How can I do that? I can't declare the type under the "private" part of the class interface, and if I declare it in the implements then I don't believe it is visible to be used in the interface function prototypes. Also, I think implements/interface relate more to visibility within the unit than the class. Do I need to declare theGetChildren() function in some other way?

As noted in other answers, in versions of Delphi that support nested types you can simply declare the type within the required scope and visibility.
For older versions of Delphi you can achieve a similar outcome by using an untyped var parameter to avoid having to reference the 'private type' in the interface section of your unit:
TSDIAppForm = class(TForm)
..
procedure GetChildren(Sheet : TTabSheet; var aRecord);
..
end;
For convenience and declarative type enforcement in the implementation of the method you can use an absolute declaration to create a local variable to act as placeholder for the untyped parameter:
procedure TSDIAppForm.GetChildren( Sheet : TTabSheet;
var aRecord);
var
result: TMyRecord absolute aRecord;
begin
result.List := ...;
// etc
end;
In this case, since the function has no direct return value and uses the var param in a directly analagous way you might choose to use the name result, as illustrated. Or of course you can use any other name for the local variable you prefer.
In use, you would simply call this method as normal with an appropriate variable in the var param:
var
myRec: TMyRecord;
begin
..
sdiForm.GetChildren(someSheet, myRec);
..
end;
In this way, you can keep a type which is an implementation detail truly confined to the implementation section of your unit.
NOTE: This technique can also be useful in situations where typed var parameters might otherwise cause the compiler to complain about 'formal var parameter types not matching'.
You should of course always consider carefully whether they are the right approach. Not least because whenever you use untyped parameters of course you take on a greater responsibility for ensuring type safety in your code. The potential for abuse should be obvious, but they sometimes offer advantages as well (as in this case, removing entirely a type from the interface section that is arguably most properly entirely confined to the implementation section).
They can also be a useful tool to keep in mind if you create code that you might wish to make available to users of older versions of Delphi where private types etc are not available.

Per Uwe, just declare in private section. Tested in XE8. The following works
TSDIAppForm = class(TForm)
private
type
TMyRecord = record
List : TShellList;
Tree : TShellTree;
Image : TImage;
end;
function GetChildren(Sheet : TTabSheet) : TMyRecord;
public
{ Public declarations }
end;

Related

Why is this code working and how is Delphi instantiating a class in this case?

I'm doing the Delphi track at exercism, and following how Delphi generates code for a form, answered one of the basic questions like this:
unit uLeap;
interface
type
TSYear = class
public
{ public declarations here }
function isLeap(y: integer): boolean;
end;
var
TYear: TSYear;
implementation
function TSYear.isLeap(y: integer): boolean;
begin
result := ((y mod 4) = 0) and (((y mod 400) = 0) or ((y mod 100) <> 0));
end;
end.
the code compiles without a single complaint, I can run it step by step, and the "isLeap" function is called from another unit several times this way:
procedure YearTest.year_divisible_by_4_not_divisible_by_100_leap_year;
begin
assert.IsTrue(TYear.IsLeap(1996), 'Expected ''true'', 1996 is a leap year.');
end;
...
I've never explicitly created the instance of the class, but it seems as if Delphi is doing it somewhere, maybe when declaring TYear? Is that a valid way?
Despite passing all the tests the code was rejected because it isn't done the conventional way. I'll surely end up doing it differently to have it accepted, but, besides the bad naming, why is this working? Would this code cause problems somewhere I can't see in this simple example?
I've never explicitly created the instance of the class, but it seems as if Delphi is doing it somewhere, maybe when declaring TYear?
No, Delphi is NOT automatically creating an instance of you. When you declare a variable of a class type, it is simply a pointer variable that can be made to point at a valid instance. But you must always create this instance yourself, and save the pointer in the variable:
SYear := TSYear.Create; // create a `TSYear` object and save its address in `SYear`
Is that a valid way?
No.
[W]hy is this working?
Because you are lucky: The isLeap function doesn't access any fields on the class instance.
Would this code cause problems somewhere I can't see in this simple example?
If the function had been using any fields in the class instance, you would have ended up with an AV if lucky and memory corruption if unlucky.
The solution is either to create an instance and use it:
SYear := TSYear.Create;
try
ShowMessage(BoolToStr(SYear.IsLeap(2000), True));
finally
SYear.Free;
end;
Or, since you clearly don't need any instance variables to determine if a year is a leap year or not, it is better to make this a class method:
type
TSYear = class
public
class function IsLeap(AYear: Integer): Boolean; static;
end;
This way, it can be called without any class instance: TSYear.IsLeap(2000). Notice that TSYear is the class (type) name, not a variable of this type.
Please see the documentation for a great conceptual introduction to all these concepts.

DXE7: "type A = type B" and var x (of type A):= A.create leads to E2010 incompatible type compile error. Why?

A. Short summary of the problem:
type
A = class(TObject)
end;
B = type A;
The compiler error E2010 incompatible types: 'B' and 'A' is shown, when you instantiate a variable of class B in the following style:
var
TheB: B;
begin
TheB:= B.Create;
..
I can avoid that problem by removing the second "type", so the declaration is the standard one:
type
A = class(TObject)
end;
B = A;
But in my opinion, the error should not come, even WITH the second "type", because A is not directly used (The second "type" tells the compiler to see both classes as individuals, see http://docwiki.embarcadero.com/RADStudio/Seattle/en/Declaring_Types for details). Can someone explain, because of what reason the error ignores my opinion? (o;
B. Complete history and more complex details:
First of all: The error:
[dcc32 Fehler] gboDHL.pas(165): E2010 Inkompatible Typen: 'Origin' und 'CountryType'
(which means "incompatible types" in english),
occurs in the following line of code:
AShipmentOrder.Shipment.Shipper.Address.Origin:= DHL_geschaeftskundenversand_api_2.Origin.Create;
Now here is the background:
I try to communicate with the "new" DHL Geschäftskundenversand API v2.2 which still uses SOAP to create a shipment order. DHL_geschaeftskundenversand_api_2 is the completely generated unit from the integrated delphi xe7 WSDL generator for that service.
AShipmentOrder is the instance which represents the top level xml node of the request.
The class "Origin" is implemented/generated this way:
type
CountryType = class(TRemotable)
private
Fcountry: country2;
Fcountry_Specified: boolean;
FcountryISOCode: countryISOType;
Fstate: state;
Fstate_Specified: boolean;
Fcountry_: country;
Fcountry__Specified: boolean;
FcountryISOCode_: countryISOType;
Fstate_: state2;
Fstate__Specified: boolean;
procedure Setcountry(Index: Integer; const Acountry2: country2);
function country_Specified(Index: Integer): boolean;
procedure Setstate(Index: Integer; const Astate: state);
function state_Specified(Index: Integer): boolean;
procedure Setcountry_(Index: Integer; const Acountry: country);
function country__Specified(Index: Integer): boolean;
procedure Setstate_(Index: Integer; const Astate2: state2);
function state__Specified(Index: Integer): boolean;
published
property country: country2 Index (IS_OPTN) read Fcountry write Setcountry stored country_Specified;
property countryISOCode: countryISOType read FcountryISOCode write FcountryISOCode;
property state: state Index (IS_OPTN) read Fstate write Setstate stored state_Specified;
property country_: country Index (IS_OPTN) read Fcountry_ write Setcountry_ stored country__Specified;
property countryISOCode_: countryISOType read FcountryISOCode_ write FcountryISOCode_;
property state_: state2 Index (IS_OPTN) read Fstate_ write Setstate_ stored state__Specified;
end;
Origin = type CountryType; { "http://dhl.de/webservice/cisbase"[GblElm] }
The "property" Origin of AShipmentOrder.Shipment.Shipper.Address is implemented/generated this way:
type
NativeAddressType = class(TRemotable)
...
published
...
property Origin: Origin Index (IS_OPTN or IS_REF) read FOrigin write SetOrigin stored Origin_Specified;
...
end;
If it is unclear, let me say, that ´AShipmentOrder.Shipment.Shipper.Address´ is of class NativeAddressType
I'm using Delphi XE7 on Windows 10, 64bit, compiling to 32bit.
The delphi WSDL generator has different problems, which I had to manage before, and it is really not "nice" that in the output the property names are identical to the class names, but that is not changeable, it seems.
The class "Origin" is not the only class in that generated unit which is named as the property it belongs to, but the error only occurs, when such a class is implemented as
type
A = type B
I tried to find a description of that special declaration variant, but that is quite hard, because the keywords are too often used.
On http://www.delphibasics.co.uk/RTL.asp?Name=Type I found the following explanation:
1.type Name = Existing type  
Refers to an existing type, such as string by a new Name.
 
2.type Name = type Existing type  
This has the same effect as above, but ensures that at run time, variables of this type are identified by their new type name, rather than the existing type name.
If I understand that correctly, that means that in my example the class Origin is not identified anymore as CountryType, but as Origin. That should be fine, because the property Origin is declared as to be of class Origin.
While going on to try to understand that problem, I manually renamed The class "Origin" in the generated unit to OriginType (delphi used that postfix "Type" with several other classes in that unit, not clear, why it has not used it with the Origin-Type). After that, I can exclude a name-conflict, because now the error is
[dcc32 Fehler] gboDHL.pas(165): E2010 Inkompatible Typen: 'OriginType' und 'CountryType'
There seems to be no reason because of what that error occurs.
After some hours without some ideas I found this Embarcadero WIKI entry: http://docwiki.embarcadero.com/RADStudio/Seattle/en/Declaring_Types
After reading and thinking about that, I understood, that the "type" in A = type B is not necessary in that case, because there is no need to handle class A in another way than class B, so I removed that manually in the generated unit and now, it works fine.
I also understand, that a special option in the generator was checked to prevent "name conflicts". If this option is not checked, this problem also should be solved (but maybe some others come around (o; ).
Maybe this helps someone to spent more time outside than with a problem like that (o;
But in the end, I do not understand what was the problem here, because all properties were of type "Origin" and later of "OriginType", also this type was created with Origin.Create and later with OriginType.Create. So the "CountryType" was never used, except for the class declaration of "Origin" / "OriginType". In my opinion this should not lead to that problem, but it does.
Can somebody explain that?
Thank you in advance
Kai
You are getting: E2010 Incompatible types: 'Tb' and 'Ta', which might seems odd:
type
Ta = class
end;
Tb = type Ta;
var
b: Tb;
begin
b := Tb.Create; // E2010 Incompatible types: 'Tb' and 'Ta'
end.
Ta and Tb are two distinct types.
From Type Compatibility you can see that none of the conditions listed indicates that the two distinct types are compatible.
Type Compatibility
Every type is compatible with itself. Two distinct types are compatible if they satisfy at least one of the following conditions.
They are both real types.
They are both integer types.
One type is a subrange of the other.
Both types are subranges of the same type.
Both are set types with compatible base types.
Both are packed-string types with the same number of characters.
One is a string type and the other is a string, packed-string, or Char type.
One type is Variant and the other is an integer, real, string, character, or Boolean type.
Both are class, class-reference, or interface types, and one type is derived from the other.
One type is PAnsiChar or PWideChar and the other is a zero-based character array of the form array[0..n] of PAnsiChar or PWideChar.
One type is Pointer (an untyped pointer) and the other is any pointer type.
Both types are (typed) pointers to the same type and the {$T+} compiler directive is in effect.
Both are procedural types with the same result type, the same number of parameters, and type-identity between parameters in corresponding positions.
The reason for the compiler error is thus: When you call Tb.Create, the compiler identifies that with Ta.Create and since b is of a distinct non-compatible type, it is not accepted.
You can call that a flaw, but it follows strict type rules and can easily be corrected like shown below.
Declaring Tb = class(Ta) would resolve the compiler error since Tb is derived from Ta.
Also declaring Tb = Ta would resolve the compiler error, since they would denote the same type (not two distinct types) and thus assignment compatible.

Haxe: how to declare "static" methods in an Interface?

This question has been asked (and probably answered) in the old Haxe forums on babble ... but it appears that that entire forum system no longer functions. Therefore, I'm asking here:
In Haxe, I need to declare an "Interface" to a class which includes a static function, "instance()." But when I do so:
You can't declare static fields in interfaces
So I remove the word "static" from public function instance() [...], and I get this:
Field instance needed by [...] is missing.
Apparently a "Catch-22." But there obviously must be some easy solution. What is it?
As you stated the language doesn't allow for static fields on interfaces. The choice is intentional. Another thing that doesn't exist is inheriting static fields.
There are several ways to structure your code to avoid such usage that in my point of view it doesn't give you many advantages. A factory pattern or DI approach (I suggest the minject library) seems the most obvious.
Given the comment below go for a typedef instead of an interface:
typedef GetInstance = Void -> Void;
You can pass that typedef around the same as an interface with the advantage that you can use both static and instance methods to satisfy that signature.
Check out the Singleton library. Any class that implements Singleton will automatically declare a static "instance" variable and corresponding getter function.
Note: as of this writing, the Haxelib version (1.0.0) is out of date. Download the Git version instead.

What is the reason you can't use static methods/variables in a class [closed]

Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 8 years ago.
Improve this question
I was wondering why you cant use the static keyword in a class.
I know that you can use it in a struct but not in a class
Does anyone know why they choose for this?
And if so are there any advantages for letting static away?
It is a terminological bêtise. What Apple did was decide that you use static in an enum or struct and class in a class. In my view this was a very foolish decision, as they are exactly parallel to one another. Many people have posted on the dev forums suggesting that they just call them all static.
The outcome is a candidate for Occam's Razor: two phrases, e.g. static property and class property, or static method and class method, that mean exactly the same thing. And then they try to cover their tracks in the documentation by adding a third term as an umbrella, "type property" (meaning a static-or-class property) or "type method" (meaning a static-or-class method), which just makes the situation even worse.
The badness of the situation is also revealed by what you have to do in protocols: you declare a class property in a protocol and then if a struct adopts it, it calls that a static property. Same thing with a class method in a protocol; an adopting struct must call that a static method. You'd think that this alone would have told them they'd done a bad thing.
Edit: The more I think about it, the more I like the proposal put forth by newacct in a comment below. If Apple had simply used the umbrella keyword type here as part of the language, the whole problem would have been solved. We could have declarations type var, type let, type func, and this would work equally in enums, structs, classes, and protocols across the board.
From the Swift Language Guide on Methods, Apple has chosen to differentiate the syntax used, based upon the fundamental Type that you're building.
Classes declare "Type Methods" with the keyword Class. Example:
class MyClass {
class func myFunc(){ ... }
}
Structs declare "Type Methods" with the keyword Static. Example:
struct MyStruct {
static func myFunc(){ ... }
}
In both cases, crating a Type Method allows you to invoke the method without first creating an instance of the class or struct.
Whereas a non class/static function would require something like
let instance = MyClass()
instance.myFunc()
... declaring the variable as a Type Method allows something like
MyClass.myFunc()

what is the meaning of `Class of ` type declaration?

While going through one of my code, I am stuck on one statement which is as below.
TMyObjectClass = class of TMyObject;
I am a bit confused, and wondering what is the meaning of this statement.
As TMyObjectClass has no declaration above the statement.
and TMyObject is having declaration as below:
TMyObject = class(TObject)
private
//some private member declaration
Public
// some public variables
end;
So, my question is what is the meaning of the statement
TMyObjectClass = class of TMyObject;
and How TMyObjectClass works?
I am a bit new to Delphi, so please help me to get some idea about these type of declaration and there workarounds.
This is a Class Reference.
They are used to work with meta classes. The canonical example is the Delphi streaming framework which uses
TComponentClass = class of TComponent;
This allows for dynamic binding to virtual constructors. The TComponent constructor is virtual. The streaming framework needs to instantiate classes derived from TComponent. It does so something like this:
var
ComponentClass: TComponentClass;
Component: TComponent;
....
ComponentClass := GetComponentClassSomehowDoesntMatterHow;
Component := ComponentClass.Create(Owner);
Now, because TComponent.Create is virtual, this is bound in a polymorphic fashion. If TComponentClass is TButton, then TButton.Create is called. If TComponentClass is TPanel, then TPanel.Create is called. And so on.
The most important thing to realise is that the class that is constructed is determined only at runtime. Note that many languages lack this capability, most notably C++.