I want to return a function as a return value in Pascal.
Generally it should look something like this:
function a(function b: Integer): function : Integer;
begin
a := b;
end;
but this doesn't work. I know that there is some problem with returning function as
a return value from another function but as far as I know this code should somehow work
what am I missing?
You need to define a function type to do what you want. See the following code example:
type
TFunc = function: Integer;
function a(b: TFunc): TFunc;
begin
a := b;
end;
function x: Integer;
begin
x := 11;
end;
begin
Writeln(a(#x));
end.
Related
I'd like a function that takes an enum and returns a stringlist (or name:value pairs). It must be possible, the Object Inspector and Code Complete seem to do that. The pseudocode seems simple enough, but the details escape me....
function ParseEnum(e: Enum, S: TStringList): TStringList;
var
i: Integer;
begin
for i := 0 to length(enum) - 1 do
S.Add(GetEnumName(e, i));
Result := S;
end;
if this is a common funciton(all of Enum):
if the Enum is Continuity, as follows code is OK(by rtti)
if not Continuity i can't find a way now
type
TMyEnum = (s, b, c, d, e, f);
implementation
uses
System.Rtti, TypInfo;
procedure ParseEnum<T>(sl: TStrings);
var
rt: TRttiType;
rot: TRttiOrdinalType;
i: Integer;
begin
rt := TRttiContext.Create.GetType(TypeInfo(T));
rot := rt.AsOrdinal;
for i := rot.MinValue to rot.MaxValue do
sl.Add(GetEnumName(TypeInfo(T), i));
end;
procedure TForm1.btn1Click(Sender: TObject);
var
sl: TStringList;
begin
sl := TStringList.Create;
try
ParseEnum<TMyEnum>(sl);
ShowMessage(sl.Text);
finally
sl.Free;
end;
end;
why sl as a param but result: to attent people don't forget to free
How can an array of record be stored in JSON via SuperObject library. For example..
type
TData = record
str: string;
int: Integer;
bool: Boolean;
flt: Double;
end;
var
DataArray: Array[0..100] of TData;
Just use the superobject Marshalling TSuperRTTIContext
program Project1;
{$APPTYPE CONSOLE}
{$R *.res}
uses
superobject,
System.SysUtils;
type
TData = record
str : string;
int : Integer;
bool : Boolean;
flt : Double;
end;
TDataArray = Array [0 .. 100] of TData;
procedure Test;
var
DataArray : TDataArray;
so : ISuperObject;
ctx : TSuperRttiContext;
begin
ctx := TSuperRttiContext.Create;
try
so := ctx.AsJson<TDataArray>( DataArray );
finally
ctx.Free;
end;
Writeln( so.AsJson );
end;
begin
try
Test;
except
on E : Exception do
Writeln( E.ClassName, ': ', E.Message );
end;
ReadLn;
end.
Make it a string first.
Your array:
//Array[0] := 'Apple';
//Array[1] := 'Orange';
//Array[2] := 'Banana';
myArrayAsStr := '"MyArray": [{ "1": "' + Array[0] +'", "2": "' + Array[1] +'"}';
Then you can just make it into JSON with SO(myArrayAsStr)
You can always generate your array as string in a different procedure but I think thats the way to do it.
Ill keep checking if there is an easier way ;)
EDIT:
SuperObject also has the following function:
function SA(const Args: array of const): ISuperObject; overload;
You will be able to convert that to a string again and add it in the total JSON string.
Using a TJSONObject, I've noticed that its AddPair function has the following overloads:
function AddPair(const Pair: TJSONPair): TJSONObject; overload;
function AddPair(const Str: TJSONString; const Val: TJSONValue): TJSONObject; overload;
function AddPair(const Str: string; const Val: TJSONValue): TJSONObject; overload;
function AddPair(const Str: string; const Val: string): TJSONObject; overload;
In particular, I've noticed that there's no overload for adding not-string values like integers, datetimes...
Due to this reason, calling ToString function, every value is shown as double quoted:
{"MyIntegerValue":"100"}
From what I've read in this answer, it goes against the JSON standard for not-string values.
How should a not-string value be added to a TJSONObject?
You can use TJSONNumber and the AddPair overload that uses TJSONValue to create a numeric JSON value, like so:
program Project1;
{$APPTYPE CONSOLE}
uses
System.SysUtils, System.JSON;
var
JSON: TJSONObject;
begin
JSON := TJSONObject.Create;
try
JSON.AddPair('MyIntegerValue', TJSONNumber.Create(100));
writeln(JSON.ToString);
readln;
finally
JSON.Free;
end;
end.
Outputs {"MyIntegerValue":100}
This is also how it's done in the codesample from help.
I want to open a 50 MB binary file and read only the last 4 bytes and convert it to string for some purpose.
The only way I found to do it now is using LoadStringFromFile, to load the file entirely on the memory then copy that last 4 bytes, however this method is very slow because the binary file is heavy.
Is there any better way to do it in Inno Setup script?
Update: This is a final working function that I edited from Martin Prikryl's answer
function readlast4byte() : AnsiString;
var
Stream: TFileStream;
Buffer: string;
Count: Integer;
Index: Integer;
begin
Count := 4;
Stream := TFileStream.Create('C:\test.txt', fmOpenRead);
try
Stream.Seek(-Count, soFromEnd);
SetLength(Buffer, 1);
SetLength(Result, Count);
for Index := 1 to Count do
begin
Stream.ReadBuffer(Buffer, 1);
Result[Index] := Chr(Ord(Buffer[1])) ;
end;
finally
Stream.Free;
end;
end;
Update 2: Also this is another great working function that written by TLama and should mark as answer too:
[Code]
#IFNDEF Unicode
#DEFINE CharSize 1
#ELSE
#DEFINE CharSize 2
#ENDIF
type
TSeekOrigin = (
soBeginning,
soCurrent,
soEnd
);
#IFDEF UNICODE
function BufferToAnsi(const Buffer: string): AnsiString;
var
W: Word;
I: Integer;
begin
SetLength(Result, Length(Buffer) * 2);
for I := 1 to Length(Buffer) do
begin
W := Ord(Buffer[I]);
Result[(I * 2)] := Chr(W shr 8); // high byte
Result[(I * 2) - 1] := Chr(Byte(W)); // low byte
end;
end;
#ENDIF
function ReadStringFromFile(
const FileName: string; Origin: TSeekOrigin; Offset, Length: Integer;
var S: AnsiString): Boolean;
var
Buffer: string;
Stream: TFileStream;
begin
Result := True;
try
Stream := TFileStream.Create(FileName, fmOpenRead);
try
Stream.Seek(Offset, Ord(Origin));
SetLength(Buffer, Length div {#CharSize});
Stream.ReadBuffer(Buffer, Length);
#IFNDEF UNICODE
S := Buffer;
#ELSE
S := BufferToAnsi(Buffer);
#ENDIF
finally
Stream.Free;
end;
except
Result := False;
end;
end;
You can use TFileStream support class:
TStream = class(TObject)
function Read(Buffer: String; Count: Longint): Longint;
function Write(Buffer: String; Count: Longint): Longint;
function Seek(Offset: Longint; Origin: Word): Longint;
procedure ReadBuffer(Buffer: String; Count: Longint);
procedure WriteBuffer(Buffer: String; Count: Longint);
function CopyFrom(Source: TStream; Count: Longint): Longint;
property Position: Longint; read write;
property Size: Longint; read write;
end;
THandleStream = class(TStream)
constructor Create(AHandle: Integer);
property Handle: Integer; read;
end;
TFileStream = class(THandleStream)
constructor Create(Filename: String; Mode: Word);
end;
Use the .Seek(-4, soFromEnd) property to seek the read pointer to the desired position.
A complication is that the TStream works with characters not bytes, so you have to convert Unicode strings that your read back to bytes.
When reading just four bytes, it's way easier to read byte by byte, preventing any multibyte conversions:
procedure ReadFileEnd;
var
Stream: TFileStream;
Buffer: string;
Count: Integer;
Index: Integer;
begin
Count := 4;
Stream := TFileStream.Create('my_binary_file.dat', fmOpenRead);
try
Stream.Seek(-Count, soFromEnd);
SetLength(Buffer, 1);
for Index := 1 to Count do
begin
Stream.ReadBuffer(Buffer, 1);
Log(Format('Byte %2.2x: %2.2x', [Index, Ord(Buffer[1])]));
end;
finally
Stream.Free;
end;
end;
Here's a generic alternative from by #TLama that efficiently works for arbitrary large read:
https://pastebin.com/nzGEdXVj
By the way, for me LoadStringFromFile function seems to be efficient enough to load 50 MB file. It takes only 40 ms.
How can an array of record be stored in JSON via SuperObject library. For example..
type
TData = record
str: string;
int: Integer;
bool: Boolean;
flt: Double;
end;
var
DataArray: Array[0..100] of TData;
Just use the superobject Marshalling TSuperRTTIContext
program Project1;
{$APPTYPE CONSOLE}
{$R *.res}
uses
superobject,
System.SysUtils;
type
TData = record
str : string;
int : Integer;
bool : Boolean;
flt : Double;
end;
TDataArray = Array [0 .. 100] of TData;
procedure Test;
var
DataArray : TDataArray;
so : ISuperObject;
ctx : TSuperRttiContext;
begin
ctx := TSuperRttiContext.Create;
try
so := ctx.AsJson<TDataArray>( DataArray );
finally
ctx.Free;
end;
Writeln( so.AsJson );
end;
begin
try
Test;
except
on E : Exception do
Writeln( E.ClassName, ': ', E.Message );
end;
ReadLn;
end.
Make it a string first.
Your array:
//Array[0] := 'Apple';
//Array[1] := 'Orange';
//Array[2] := 'Banana';
myArrayAsStr := '"MyArray": [{ "1": "' + Array[0] +'", "2": "' + Array[1] +'"}';
Then you can just make it into JSON with SO(myArrayAsStr)
You can always generate your array as string in a different procedure but I think thats the way to do it.
Ill keep checking if there is an easier way ;)
EDIT:
SuperObject also has the following function:
function SA(const Args: array of const): ISuperObject; overload;
You will be able to convert that to a string again and add it in the total JSON string.