How to proceed when passing a Record vs Passing a class to a procedure - pascal

Lets say i have a class called TProgramSettings which looks like this:
TProgramSettings = class
flags: UInt32;
...
end;
PProgramSettings = ^TProgramSettings;
So I initialize my program like this:
var
MyProgramSettings: TProgramSettings;
begin
MyProgramSettings := TProgramSettings.Create;
MyProgramSettings.Flags := 0;
ApplySettings(#MyProgramSettings);
And the procedure ApplySettings looks like this:
procedure ApplySettings(ProgramSettings: PProgramSettings);
var
MyObject : TCustomObject;
begin
MyObject := TCustomObject.Create;
MyObject.Settings.Flags := ProgramSettings^.Flags;
...
end;
Right now my code looks like this, however I wonder if there is any better way to do it? Am I breaking Object Pascal/Delphi code conventions? Would it be better to just make TProgramSettings a record? I really dont understand the difference between Records and Classes, all I know is that classes must be initialized. In the case I make TProgramSettings a record from what I've read I shouldn't pass a pointer to it since the record points to the same addres in memory and doesn't need to be referenced like the class. How should I go about it? Any help would be aprreciated :)

An object of type class is already a pointer, so there really is no need to dereference it as you do. The equivalent is
TProgramSettings = class
flags: UInt32;
...
end;
…
var
MyProgramSettings: TProgramSettings;
begin
MyProgramSettings := TProgramSettings.Create;
MyProgramSettings.Flags := 0;
ApplySettings(MyProgramSettings);
…
procedure ApplySettings(ProgramSettings: TProgramSettings);
var
MyObject : TCustomObject;
begin
MyObject := TCustomObject.Create;
MyObject.Settings.Flags := ProgramSettings.Flags;
...
end;
As regards the difference between a record and a class object, well, one way to think of it is that when you define a record as a var, that definition creates the record (no need to call 'Create') whereas for a class you are just creating a point to the object, and you still need to create the object itself. There is a lot more to than that, but it is away to start thinking about it.

Related

Declare a variable without datatype

I have a piece of code that needs a variable declaration w/o its type. The variable is assigned a value later and that is an interface. pseudo code will look very similar to this:
var client
if some_condn {
client = ssh.Dial(params)
} else {
client = my_own_ssh_dial(my_params)
}
session,_ := client.NewSession()
The problem is GO does not allow a variable declaration w/o type. Is there any way I can use something like an generic Object (from Java) to default client to start with?
TIA
A variable must have a type in order to use it. The closest thing to an untyped variable would be the type interface{}, which is an interface type, but has no methods to call.
Since the goal here is to call the NewSession method, declare the variable with an interface containing that method.
var client interface {
NewSession() (*ssh.Session, error)
}
if some_condn {
client = ssh.Dial(params)
} else {
client = my_own_ssh_dial(my_params)
}
session, _ := client.NewSession()
I'm pretty new to Go, but I'll take a stab. If it's wrong, cool. Unfortunately, I don't have enough code from you to actually try and do this - but essentially, you'll need to make your own type my_own_ssh, and then add a Dial function to it. Then, you'll define an
interface Client {
Dial()
}
And then you can define your client variable with var client Client before the conditional check. You'll also need to create a function Dial that has a my_own_ssh receiver in order for the interface to work/make sense.
I'm very happy to receive feedback on this answer, because like I said - I'm pretty new to it :)

How to declare data type ENUM in Yaskawa MotionWorks IEC 3?

I'm trying to add my own enum in MotionWorks.
After creation of new data type the only available types are ARRAY,STRING,STRUCT.
Writing the following code:
TYPE SimulationType:
(
Passing := 0,
Random := 1,
Failing := 2
) INT := 0;
END_TYPE
does not compile.
Yaskawa seem to be complying to ENUM (according to this list) but I can't figure out how to declare it.
Edit:
I can do the following:
TYPE
ResultType:(Pass, Random, Fail);
END_TYPE
But it doesn't seem to create an enum, as I can't access it's value. I can access it like a structure.
Edit 2:
If I declare:
TYPE
ResultType:(Pass, Random, Fail);
END_TYPE
And set variable
ExpectedResultType : ResultType;
Then in the code I try to use:
IF ExpectedResultType = ResultType.Pass THEN
Done := TRUE;
END_IF;
It compiles, but will not run.
Trying to use this code will not compile:
CASE ExpectedResultType OF
ResultType.Pass:
Done := TRUE;
Error := FALSE;
ResultType.Random:
Done := TRUE;
ResultType.Fail:
Error := TRUE;
Done := FALSE;
END_CASE;
Enums in MotionWorks are declared in data types as this example:
TYPE
MyEnum:(Zero,One,Two,Three);
END_TYPE
ENUMs in MotionWorks can't be assigned a value. First enum will always be equal to 0 (zero), second one to 1 (one), and so on.
Then enums can be used in IF .. END_IF statements like this:
I'll call my variable "i". The variable must be declared as INT. Any other type will not work.
In the code use it like this:
IF i = MyEnum#Zero THEN
(* do some work *)
ELSIF i = MyEnum#One THEN
(* do some other work *)
END_IF
ENUMs can't be used in CASE statements in MotionWorks.
This what I have for Schneider which is IEC61131 so it should be the same
TYPE E_HomeLimitSwitch:
(
ePositiveDirectionRisingEdge := 0,
eNegativeDirectionRisingEdge := 1,
ePositiveDirectionFallingEdge := 2,
eNegativeDirectionFallingEdge := 3
);
END_TYPE
I don't think you INT:=0 should be there.
You can only set the default value to one of your local enum members. Not to other values or even a number as you tried.
Try this instead in line 6:
) INT := Passing;
unlike Codesys, Yaskawa's MotionWorksIEC does not fully support enumerations. In ST language enum usage is quite popular in CASE statements but MotionWorksIEC does not support enum use in case statements.
But, you still can define enums as shown below.
TYPE
PackMLState:(Starting,Starting,Aborting,Aborted,Helding,Held,Etc);
END_TYPE
You can use the enum type as;
IF machineState = PackMLState#Starting THEN
;;
END_IF
Comparing Codesys and MotionWorksIEC (Which is basically Phoenix Contact, KW Software Multiprog), there are some differences. For clarification, the lack of enum use in Cases doesn't make Multiprog an inferior IDE.

Overloading the assignment operator for Object Pascal

What happens when the assign operator := gets overloaded in Object Pascal? I mainly mean what gets evaluated first and more importantly how (if possible) can I change this order. Here is an example that bugs me:
I declare TMyClass thusly:
TMyClass = class
private
FSomeString: string;
class var FInstanceList: TList;
public
function isValid: boolean;
property SomeString: String write setSomeString;
end;
the isValid function checks MyObject for nil and dangling pointers.
Now lets assume I want to overload the := operator to assign a string to TMyClass. I also want to check if the object I'm assigning this string to is a valid object and if not create a new one, so:
operator :=(const anewString: string): TMyClass;
begin
if not(result.isValid) then
result:= TMyObject.Create;
result.SomeString:= aNewString;
end;
In short I was hoping that the result would automatically hold the pointer to the object I'm assigning to. But tests with the following:
procedure TForm1.TestButtonClick(Sender: TObject);
var
TestObject: TMyObject;
begin
TestObject:= TMyObject.Create;
TestObject:= 'SomeString';
TestObject.Free;
end;
led me to believe that instead an intermediate value for result is assigned first and the actual assignment to TestObject happens after the code in := executes.
Everything I know about coding is self taught but this example shows that I clearly missed some basic concept somewhere.
I understand that there are easier ways to do this than by overloading a := operator but out of scientific curiosity is there ANY way to make this code work? (No matter how complicated.)
It's not possible to do what you want with operator overloads. You must use a method.
The problem is that the := operator does not give you the access to the left hand side (LHS) argument (here it's the Self, a pointer to the current instance) but only to the right hand side argument.
Currently in you example if not(result.isValid) then is dangereous because the result at the beginning of the function is undefined (it can have any value, it can be either nil or not and when not nil, calling isValid will lead to some possible violation access. It does not represent the LHS at all.
Using a regular method you would have an access to the Self and you could call isValid.
I do not have Lazarus to check, but it is possible in Delphi in the following way. We give access to an instance of the class indirectly via TValue.
Here is a sample class:
type
TMyClass = class(TComponent)
private
FSomeString: string;
published
property SomeString: string read FSomeString write FSomeString;
end;
And we do the following in the container class (for example, TForm1).
TForm1 = class(TForm)
private
FMyClass: TMyClass;
function GetMyTypeString: TValue;
procedure SetMyTypeString(const Value: TValue);
public
property MyClass: TValue read GetMyTypeString write SetMyTypeString;
end;
...
function TForm1.GetMyTypeString: TValue;
begin
Result := FMyClass;
end;
procedure TForm1.SetMyTypeString(const Value: TValue);
begin
if Value.Kind in [TTypeKind.tkChar, TTypeKind.tkUString,
TTypeKind.tkString, TTypeKind.tkWChar, TTypeKind.tkWString]
then
begin
if not Assigned(FMyClass) then
FMyClass := TMyClass.Create(self);
FMyClass.SomeString := Value.AsString;
end else
if Value.Kind = TTypeKind.tkClass then
FMyClass := Value.AsType<TMyClass>;
end;
In this case both button clicks will work properly. In other words, it simulates := overloading:
procedure TForm1.Button1Click(Sender: TObject);
begin
MyClass := 'asd';
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
MyClass := TMyClass.Create(self);
end;
And here is how to get access to TMyClass instance:
procedure TForm1.Button3Click(Sender: TObject);
begin
if Assigned(TMyClass(MyClass.AsObject)) then
ShowMessage(TMyClass(MyClass.AsObject).SomeString)
else
ShowMessage('nil');
end;

Seg fault/error 216 when using Fillchar()

After I added Fillchar() looking for do C's memset() equivalent in pascal the program give a runtime error 216.
Here's my code (if I remove FillChar() it works fine):
function NewFoo(name : string) : ptrfoo;
var sym : ptrfoo;
begin
new(sym);
FillChar(sym, SizeOf(foo), #0);
sym^.name := name;
NewFoo := sym;
end;
foo is a record and fooptr a pointer to it defined as type fooptr = ^foo;.
How do I fix this?
Fillchar takes an untyped parameter, not a pointer. As it is you are overwriting the pointer itself (and memory well beyond it) with zeroes. You want to dereference the pointer to use it with Fillchar:
FillChar(sym^, SizeOf(foo), #0);
Untyped parameters are the parameters of the form const foo, var foo, out foo with seemingly no type attached to them. They cannot be assigned or used directly, but you can dereference them and get a pointer to them. Basically, they are syntactic sugar to be able to pass records and other variables around as if they were blobs of data without having to take a pointer to them C-style from the calling end (in reality it's a bit more complex, but that's the gist of it).

ResourceString VS Const for string literals

I have a couple of thousands string literals in a Delphi application. They have been isolated in a separate file and used for localization in the past.
Now I don't need localization any more.
Is there any performance penalty in using resourcestring compared to plain constants.
Should I change those to CONST instead?
The const string makes a call to _UStrLAsg and the resource string ends up in LoadResString.
Since the question is about speed there is nothing like doing a test.
resourcestring
str2 = 'str2';
const
str1 = 'str1';
function ConstStr1: string;
begin
result := str1;
end;
function ReceStr1: string;
begin
result := str2;
end;
function ConstStr2: string;
begin
result := str1;
end;
function ReceStr2: string;
begin
result := str2;
end;
procedure Test;
var
s1, s2, s3, s4: string;
begin
s1 := ConstStr1;
s2 := ReceStr1;
s3 := ConstStr2;
s4 := ReceStr2;
end;
For the first time I used AQTime added in DelphiXE to profile this code and here is the result. The time column show Machine Cycles.
I might have done a lot of rookie mistakes profiling this but as I see it there is a difference between const and resourcestring. If the difference is noticeable for a user depends on what you do with the string. In a loop with many iterations it can matter but used to display information to the users, not so much.
Since they are stored in a single file which presumably does little else (well done!), there's no reason not to try it out. I predict it won't make any discernible difference to performance, but I guess it depends on what else you are doing in your app.
Resource strings do incur overhead.
Compared to displaying such a string, or writing it to a file or database, the overhead is not much.
On the other hand it is just a switch from the resourcestring to const keyword (and back if you ever consider to to localization again).

Resources