I want one class object to be a global variable in one module in Delphi, and other modules(.pas-files, maybe it's not correct to call it a module, I'm not really familiar with Delphi) be able to use this global variable. Is it a way to initialize global object not inside some global function and have other modules use this global object directly? Without any global functions. Is it possible?
Simply declare the variable in the unit's interface, and then use that unit in other units' uses clauses as needed. You can initialize the variable in its unit's initialization section.
UnitA.pas:
unit A;
interface
type
TMyClass = class
// members as needed...
end;
var
GlobalObj: TMyClass;
implementation
// implement TMyClass methods as needed...
initialization
GlobalObj := TMyClass.Create;
finalization
GlobalObj.Free;
end.
UnitB.pas:
unit B;
...
uses
..., A;
// use A.GlobalObj as needed...
end.
Related
I'm new to Lua "classes" (metatables) and I have a doubt.
In the following constructor code that I wrote, I declared the variable obj as local. But in most examples on the web, this variable is just assigned to without a local declaration. So in my understanding, it becomes a global variable (not efficient from what I understood). But is there a reason for that?
A = {}
A.__index = A
function A:new(obj_init)
local obj = obj_init or {val = 0}
setmetatable(obj, A)
return obj
end
I also noticed that members of the class can be accessed directly, even from another Lua module:
x = A:new{val = 2}
print(x.val)
But is there a way to make val a private member? Maybe also using local?
First, let's look at what these examples you found might have looked like.
Parameters: Implicit locals
function A:new(obj)
obj = obj or {val = 0}
...
end
in this snippet, obj is a local variable. This is because all function parameters in Lua are local variables. We may rewrite the function as follows to highlight this:
function A:new(...)
local obj = ...
obj = obj or {val = 0}
end
I assume this is what you saw e.g. in the PIL. You probably renamed the parameter to obj_init, losing the implicit local declaration of obj.
Global assignment
If you happened to consume particularly bad resources, you might have seen the following:
function A:new(obj_init)
obj = obj_init or {val = 0}
...
end
in this snippet, obj is indeed a global variable. This is very bad for multiple reasons:
Efficiency: You are right - excepting pathological cases, global variables are always slower than local variables as global variables are entries in the (hash part of the) _G table whereas locals are stored in fast registers of the Lua VM.
Code quality: Global pollution: This constructor now has a side effect: It modifies the global variable obj and expects it to not be modified during its execution. This might lead to this function overwriting a global variable obj and, even worse, you may not yield from a coroutine in the constructor now, because you're dependent on a global state which may not be altered.
Private members
The typical way to implement private table fields in Lua is by convention: You may prefix the field names with an underscore to indicate that these fields may not be modified from outside. Of course programmers are free to circumvent this.
Otherwise, the concept of "private" variables doesn't mesh too well with the scripting language nature of Lua; you can shoehorn full-fledged OOP onto Lua using metatables, but it will be neither idiomatic nor efficient.
Upvalues
The most idiomatic way to implement private members in Lua is to have them be upvalues of closures ("accessors"):
A = {}
A.__index = A
function A:new(obj_init)
local obj = {} -- empty object: only member is private
local val = obj.val
-- note: this does not need `self`, thus no `:` is used;
-- for setters you might want to discard `self` for consistency
function obj.getVal()
return val
end
setmetatable(obj, A)
return obj
end
x = A:new{val = 2}
print(x.getVal())
-- val can not be set from outside (excepting the debug library);
-- it is "private" and only accessible through the getter method
The downside is that all functions accessing your private members will have to be instantiated with each object creation.
Note that even upvalues aren't fully "private" as they may be accessed through the debug library.
debug library workarounds
The debug library allows you to inspect the stack. This allows you to tell which method triggered your __index metamethod. You could thus return different values to different callers. This may be nice for a proof-of-concept to showcase Lua's metaprogramming capabilities, but should not be done in practice as it is very inefficient and hacky.
There is a problem when u define an enum in a method.
I was trying to do this:
VAR
enumA:(A,B,C);
END_VAR
and there is the compiler reaction when I used this in TwinCAT3 Shell (TcXaeShell).
any help would be appreciated.
You can only use global enumerations in methods. It's one of the limitations with local enumerations.
https://alltwincat.com/2021/11/16/local-enumerations/
You should first define variable type as enumeration in DUT
TYPE MyEnum:
(A, B, C)
END_TYPE
Then in a program you can declare variable of that type
VAR
enum: MyEnum;
END_VAR
Inside the program if you want to compare it.
IF enum = MyEnum.C THEN
// Do something
END_IF;
I’ve run into this issue before. You must declare the local enumeration in the variables section of the function block. Then you can use it in the methods of the function block.
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.
This doesn't work
interface String {
contains(s:string):boolean;
}
String.prototype.contains=(s:string):boolean=>this.indexOf(s)!==-1;
because Property 'contains' does not exist on type 'String'
This is a bit of a surprise since adding it was the entire point of the interface declaration. http://www.typescriptlang.org/docs/handbook/declaration-merging.html suggests that the above code is legal. String is in the global namespace as far as I can tell by examining lib.es2015.wellknown.d.ts.
What's the right way to go about this? After reading Aluan Haddad's Extending third party module that is globally exposed I rewrote like this
declare global {
interface String {
contains(s: string): boolean;
}
}
String.prototype.contains=(s:string):boolean=>this.indexOf(s)!==-1;
and the interface change is now correct. But now 'this' implicitly has type 'any' because it does not have a type annotation.
Per further comments this can be explicitly typed using function syntax.
String.prototype.contains = function (this: string, s:string):boolean {
return this.indexOf(s)!==-1;
};
It should also be noted that in the course of this investigation I discovered that contains is implemented with the name includes and is declared in lib.es2015.core.d.ts
If you're defining the augmentation inside of a module, that is a file containing a top-level import or export then you need to use a declare global block in order to augment the global scope. Otherwise, the interface that you declare will not be merged into the global array interface because it's local to the module like any other declaration would be. The declare global syntax is specifically to cover this use case.
Furthermore, when you define the actual method you can't use an arrow function if the method itself is defined in terms of this because Arrow functions have a statically scope this while a dynamic this is needed for methods.
Putting it together
// this is a module
export {}
declare global {
interface String {
contains(other: string): boolean;
}
}
String.prototype.contains = function (other) {
return this.indexOf(other) and !== -1;
};
Note that whether the type being augmented is a class or an interface, the member needs to be declared in an interface as above because interfaces can merge with classes and interfaces can merge with interfaces but classes do not merge.
How to declare type for a class procedure, for example
type
TTest = class
procedure Proc1;
class procedure Proc2;
class procedure Proc3; static;
end;
TProc1 = procedure of object;
TProc2 = ???;
TProc3 = ???;
TProc2 = procedure of object;
A class method still has a Self pointer. It's the class rather than the instance.
An interesting consequence of this is that it provides a way to implement event handlers without having to instantiate an object. For instance, you could use a class method of a class that that is never instantiated as a way to provide event handlers for the global Application object.
TProc3 = procedure;
A static class method has no Self pointer. It is assignment compatible with a plain procedural type.
Static class methods can be used as an alternative to globally scoped procedures. This does allow you to put such methods in a namespace, that of the class, and so avoid polluting the global namespace.
Take care when implementing static class methods that you do not call virtual class methods. Such calls are bound statically at compile time because the lack of a Self pointer means that dynamic polymorphic binding at runtime is not possible. Rather disappointingly the compiler fails to warn of this and so you do need to keep your wits about you.