I'm working with simple sets in Pascal, and simply want to output the contents of a set. Everytime I run the code i get the following error message: 'project1.lpr(17,13) Error: Can't read or write variables of this type'.
here is my code:
program Project1;
{$mode objfpc}{$H+}
uses
sysutils;
type TFriends = (Anne,Bob,Claire,Derek,Edgar,Francy);
type TFriendGroup = Set of TFriends;
Var set1,set2,set3,set4:TFriendGroup; x:integer;
begin
set1:=[Anne,Bob,Claire];
set2:=[Claire,Derek];
set3:=[Derek,Edgar,Francy];
writeln(set1);
readln;
end.
Is there a special method/function to output sets?
thanks
Free Pascal allows write/writeln() of enums without explicit typinfo calls.
So
{$mode objfpc} // or Delphi, For..in needs Object Pascal dialect iirc.
var Person :TFriends;
for Person in Set1 do
writeln(Person);
works fine.
Using WriteStr this can also be written to strings. (writestr functions like write/writestr but then to an string. Originally implemented for ISO/Mac dialects)
You can't directly display the set as a string because there is no type information emitted for it. To do so, your set must be a published property of a class.
Once published in a class, you can use the unit TypInfo to display the set as a string, using the function SetToString(). TypInfo is the FPC unit which does all the compiler reflection things.
Short working example of what you try to do:
program Project1;
{$mode objfpc}{$H+}
uses
sysutils, typinfo;
type
TFriends = (Anne,Bob,Claire,Derek,Edgar,Francy);
TFriendGroup = Set of TFriends;
TFoo = class
private
fFriends: TFriendGroup;
published
property Friends: TFriendGroup read fFriends write fFriends;
end;
Var
Foo: TFoo;
FriendsAsString: string;
Infs: PTypeInfo;
begin
Foo := TFoo.Create;
Foo.Friends := [Derek, Edgar, Francy];
//
Infs := TypeInfo(Foo.Friends);
FriendsAsString := SetToString(Infs, LongInt(Foo.Friends), true);
//
Foo.Free;
writeln(FriendsAsString);
readln;
end.
This program outputs:
[Derek,Edgar,Francy]
To go further:
official documentation about SetToString
this blog article
Related
I really love the way mORMot / Synopse has implemented the handling of JSON, especially the way you can use the JSON elements in your code (like MyString := myjson.name). This is very intuitive and useful in wrapping objects which only have one Variant (JSON) with state which we access through getters/setters, like below:
TMyObject = class
private
FState: Variant;
function GetName: String;
public
constructor Create(AJson: Variant);
property Name: String read GetName;
end;
function TMyObject.GetName: String;
begin
Result := FState.name;
end;
It is really powerful, but I would like to get the 'default' variant value if an element is not found in the corresponding JSON document (so, above example should return an empty string if 'name' is not present).
I don't want to use NullStrictConvert, because that is not thread safe and influences the rest of our program. Of course I could check VarIsNull(FState.name), but then I have to do this for each element and I prefer not to have this extra boilerplate.
Any suggestions?
When you unserialize some JSON into an object, the missing fields are left untouched IIRC.
So you can just set the fields to their default values before unserializing the JSON.
One way of doing it is to inherit from TSynPersistent and override the Create constructor and set default value(s).
Edit: You may use a TDocVariantData instead of variant, and call GetAsRawUTF8() and such methods which return false if the property is not existing.
I would like to use the REST.JSON Librairy to convert an Object to a JSON-String and back, but i'm encountering some problems.
Let's say the classes i would like to convert is "TFooChild1" and "TFooChild2" which both descend from "TFoo".
The classes look like this:
TFoo = class
protected
Name: string;
Value: Double;
end;
TFooChild1 = class(TFoo)
private
Limit: Double;
end;
TFooChild2 = class(TFoo)
private
Limit: Double;
WorkerID: Integer;
end;
Creating and converting to JSON would look something like this:
var
Foo: TFoo;
s: string;
begin
Foo := TFooChild1.Create;
Foo.Name:= '...';
... //assign all the Fields
s := TJson.ObjectToJsonString(Foo);
Foo.Free;
//conversion to string is correct...
Foo := TJson.JsonToObject<TFoo>(s, []);
//Field "Limit" is lost
end
I'm aware that this (TJson.JsonToObject<TFoo>(s, [])) would never return the type TFooChild1, but that's exactly what i need.
When you convert back to an Object, the Fields of the child classes are lost. How do I avoid this? I can't do JsonToObject<TFooChild1> because I don't know if it's gonna be Child1 or Child2.
Any hints?
I've googled if there's maybe a ConvertOption that would include Type information, but i haven't found anything
Add Rest.JsonReflect to your uses class as you will need to use the [JsonReflect(ctObject,ctObject,TFooInterceptor)] attribute attached to where you are referencing the TFoo class. Then you will have to code a TFooInterceptor object to intercept and handle the appropriate conversion. I would code the ObjectReverter and ObjectConverter overrides and start coding from there. Your TFooInterceptor should descend from TJSONInterceptor. Some examples are in the source REST.Json.Interceptors.pas.
I have this unit
unit helper;
interface
{$MODE OBJFPC}
{$MODESWITCH TYPEHELPERS}
{$H+}
uses
sysutils;
type
THelloWorldHelper = type helper(TStringHelper) for string
public
function world() : string;
end;
implementation
function THelloWorldHelper.world() : string;
begin
result := self + ' world';
end;
end.
and other unit that redeclare this type helper
unit helperalias;
interface
{$MODE OBJFPC}
{$MODESWITCH TYPEHELPERS}
{$H+}
uses
helper;
type
THelloWorldHelper = helper.THelloWorldHelper;
implementation
end.
and program as follows
program helloworld;
{$MODE OBJFPC}
{$MODESWITCH TYPEHELPERS}
{$H+}
uses
helperalias;
var hello : string;
world :string;
begin
hello:='hello';
world := hello.world();
writeln(world);
end.
When I run
fpc helloworld.pas
It refused to compile with message Error: Objective-C categories and Object Pascal class helpers cannot be used as types.
If in helloword.pas, I replaced helperalias unit with helper, it worked.
I read about helper restriction.
Why is redeclaring type helper prohibited?
Consider this class:
unit Unit2;
interface
type
TTeste = class
private
texto: string;
public
function soma(a, b: integer): string;
end;
implementation
procedure TForm2.Button1Click(Sender: TObject);
var
teste: TTeste;
begin
teste:= nil;
teste.texto:= '';//access violation
showmessage(teste.soma(5, 3));//no access violation
end;
{ TTeste }
function TTeste.soma(a, b: integer): string;
begin
result:= (a+b).ToString;
end;
end.
should it really work? why? the var crashed but the function doesnt, does it works like a class funtion?
This works because you are not attempting to access any fields of the class. The function doesn't require any memory allocation. If the field texto was used in that function, then it would crash (as you see in your other test), because that memory isn't addressed. But here, there is no memory involved. Both a and b (and the function result for that matter) are allocated elsewhere outside of the class. Part of the instantiation process is allocating memory for each and every one of the fields in the object.
This is just coincidental. It's still highly discouraged to actually use something like this. If you feel the need to access the function without instantiation, then you should make it a class function instead.
A function is returning an anonymous function. I would like to assign the result to a variable. However the compiler thinks that I am trying to assign the function and not the result of the function. How can I resolve this?
program Project9;
{$APPTYPE CONSOLE}
type
TMyEvent = reference to function: string;
var
v1: TMyEvent;
function GetHandler: TMyEvent;
begin
Result := function: string
begin
Result := '';
end;
end;
begin
v1 := GetHandler; // <- Incompatible types: 'TMyEvent' and 'Procedure'
end.
Note: I do have a workaround but I hope that this problem can be solved without introducing a wrapper:
program Project9;
{$APPTYPE CONSOLE}
type
TMyEvent = reference to function: string;
TWrapper = record
FHandler: TMyEvent;
end;
var
v1: TMyEvent;
function GetHandler: TWrapper;
begin
Result.FHandler := function: string
begin
Result := '';
end;
end;
begin
v1 := GetHandler.FHandler; // <- works
EDIT: this is not specific to anonymous or any special kind of functions: that is actual for any function returning the function, it was the same in Turbo Pascal before even the 1st Delphi arrived.
If your anonymous methods/functions are paramless, you must assign with ();
v1 := GetHandler();
Without the parentheses Delphi will try to assign the function to the variable. The parens tell it to invoke the function and assign the function result to the variable.
Delphi's function call syntax is a little different from most other languages. In most languages, in order to call a function you must use parens () after the function name, commonly referred to as the function call operator. If the function is simply named, and no parens supplied, that expression evaluates to the function without invoking a call.
So, with the C++ language as our example,
i = foo();
calls the function and stores the return value in i.
On the other hand,
fn = foo;
stores the address of the function in the function pointer variable fn.
Delphi varies from this, for a parameterless function, by allowing you to omit the parens, and yet still call the function. So in Delphi, the first line of code above could be written
i := foo;
and this would call the function.
Where it gets slightly tricky is if the function return type is a procedural type, a method, or an anonymous method, as you have found out.
In your scenario,
v1 := GetHandler;
is ambiguous in the eyes of the compiler. Because v1 is a variable whose type is an anonymous method, the compiler will never generate a call when parens are omitted. If it did generate a call then you would not be able to make the simple assignment of a function to a procedural type variable.
So the compiler switches to the behaviour that you find in languages like C++. You must supply parens if you wish for the function to be called. To make your code compile and work, write
v1 := GetHandler();
The documentation covers the issue in some detail. The key excerpt is this:
In assignment statements, the type of the variable on the left determines the interpretation of procedure or method pointers on the right.
Now, casting judgement, I find the idea that the context of an expression can determine its interpretation to be rather unsettling. This all stems from allowing function calls to be made when parens are omitted. I would rather have to use parens always and so avoid the ambiguities discussed above. In particular this would allow expression meaning to be independent of context.
To see what I mean, we return to my original example. Let us now be more specific about the types involved:
type
TIntFunc = function: Integer;
function foo: Integer;
begin
Result := 42;
end;
var
i: Integer;
fn: TIntFunc;
At this point we can write:
i := foo; // i is an integer, so the function is called
fn := foo; // fn is a procedural type variable, so the function is not called
I personally find this state of affairs not at all satisfactory.