How to reference an external function from inside a unit - pascal

I have two files. The first, a.pas, contains:
uses b;
function f(x: integer): integer;
begin
f := x+1;
end;
begin
writeln(g(10));
end.
The file b.pas contains:
unit b;
interface
function g(x: integer): integer;
implementation
function g(x: integer): integer;
begin
g := f(x)*2;
end;
end.
Is it possible to somehow reference the function f defined in a.pas? I thought about using the forward keyword like this: (b.pas)
unit b;
interface
function g(x: integer): integer;
implementation
function f(x: integer): integer; forward;
function g(x: integer): integer;
begin
g := f(x)*2;
end;
end.
But it doesn't work. It gives a "Forward declaration not solved" error.
I also thought about using the external keyword but, in order to use the external f function, b.pas requires a.pas to be already compiled (but a.pas require b.pas as well).
The only way to do this seems to be moving f (interface and implementation) to a new helper.pas file, and modifying b.pas like this:
unit b;
interface
function g(x: integer): integer;
implementation
uses helper;
function g(x: integer): integer;
begin
g := f(x)*2;
end;
end.
I would prefer not having a helper.pas file. But maybe it's just impossible?

With the current source code layout it is not possible. If you do not want to make a.pas a unit (why?), you can rewrite function g to have a function parameter:
{$ifdef FPC}
{$mode delphi}
{$endif}
type
tintfunc = function(x: integer): integer;
function g(x: integer; f: tintfunc): integer;
begin
g := 2*f(x);
end;
function f(x: integer): integer;
begin
f := x+1;
end;
begin
writeln(g(3,f));
end.
There are other methods (including global function pointers), but function parameters are IMO the most clean/safe way to implement it.

Related

Referencing Service.Name crashes after moving service code to ancestor

I had a unit for a Windows (32 bit) service that was built up like this:
unit uSvcBase;
interface
type
TMyServiceBase = class(TService)
procedure ServiceBeforeUninstall(Sender: TService);
procedure ServiceCreate(Sender: TObject);
procedure ServiceStop(Sender: TService; var Stopped: Boolean);
procedure ServiceExecute(Sender: TService);
procedure ServiceStart(Sender: TService; var Started: Boolean);
procedure ServiceAfterInstall(Sender: TService);
private
public
function GetServiceController: TServiceController; override;
end;
var
MyServiceBase: TMyServiceBase;
implementation
{$R *.DFM}
{$R SvcEventLogMessages.res}
procedure ServiceController(CtrlCode: DWord); stdcall;
begin
MyServiceBase.Controller(CtrlCode);
end;
function TMyServiceBase.GetServiceController: TServiceController;
begin
Result := ServiceController;
end;
const
rsServiceMessages =
'SYSTEM\CurrentControlSet\Services\EventLog\Application';
procedure TMyServiceBase.ServiceAfterInstall(Sender: TService);
var
lReg : TRegistry;
lAppName: String;
begin
lReg := TRegistry.create;
try
with lReg do
begin
Rootkey := HKEY_LOCAL_MACHINE;
if OpenKey(rsServiceMessages, False) then
begin
if OpenKey(MyServiceBase.Name, True) then
begin
lAppName := ParamStr(0);
WriteString('EventMessageFile', lAppName);
WriteString('CategoryMessageFile', lAppName);
WriteInteger('CategoryCount', 2);
WriteInteger('TypesSupported', EVENTLOG_ERROR_TYPE OR EVENTLOG_WARNING_TYPE OR EVENTLOG_INFORMATION_TYPE);
CloseKey;
end;
CloseKey;
end; { if OpenKey }
end; { with lReg }
finally
lReg.Free;
end;
end;
Because I needed to make a second service which was largely identical, I decided to make this a 'base' unit that others derive from (you can already see that in the names above):
unit uSvcTasks;
interface
uses
System.SysUtils, System.Classes, uSvcBase;
type
TMyServiceScheduler = class(TMyServiceBase)
procedure ServiceCreate(Sender: TObject);
private
public
end;
var
MyServiceScheduler: TMyServiceScheduler;
implementation
{%CLASSGROUP 'System.Classes.TPersistent'}
{$R *.dfm}
Uses uTypesAlgemeen;
procedure TMyServiceScheduler.ServiceCreate(Sender: TObject);
begin
inherited;
// Set some properties
end;
At design time, the MyServiceScheduler.Name in this descendant differs from the MyServiceBase.Name.
Issue: The AfterInstall now crashed. Trying to use the original code using OpenKey(MyServiceBase.Name was not allowed.
I worked around his by using a property for the name (setting it in the descendant Create), but I do not understand why referencing MyServiceBase.Name in the AfterInstall does not work. Can anyone explain?
Thanks to Uwe Raabe's comments I was able to figure out how to fix this:
The project had an Application.CreateForm(TMyServiceScheduler, MyServiceScheduler) in the project source which initializes MyServiceScheduler, but there was nothing initializing MyServiceBase, so refering to it was illegal.
Replace the reference to MyServiceBase.Name with Name in the AfterInstall(That should've been done anyway).
Move the code for the ServiceController from uSvcBase to uSvcTasks

Ada "Subtype mark required"

I was given a project at uni where I need to write an ADA package of a graph. The points of the graph are stored in an array, the edges are stored in a matrix.(In the matrix if there is an edge between two points the number at that index is the weight/length of the edge)
The two procedures needed are NewPoint and NewEdge.
The three functions needed are IsItaPoint, IsitAnEdge, and print (the matrix and array).
I started learning ADA a week ago and I'm sure this is a 2 minute code for some of you.
I wrote this:
Graph.ads
package Graph is
function IsItAPoint (G: Graph;I: Integer) return Boolean;
function IsItAnEdge (G: Graph;I: Integer; J: Integer ) return Boolean;
procedure NewEdge (G: Graph;I: Integer; J: Integer; S: Integer);
procedure NewPoint(G: Graph;I: Integer);
type PointArray is array(Integer range <>) Of Integer;
type EdgeMatrix is array(Integer range <>,
Integer range <>) of INTEGER;
PointCount: Integer:=0;
end Graph;
Graph.adb
package body Graph is
procedure NewPoint(G: Graph;I: Integer) is
begin
G.PointCount:=G.PointCount+1;
G.PointArray(G.PointCount):=I;
end;
procedure NewEdge(G: Graph;I: Integer; J: Integer; S: Integer) is
begin
G.EdgeMatrix(I,J):=S;
end;
function IsItAPoint (G: Graph;I: Integer) return Boolean is
begin
for J in 1..100 loop
if (G.PointArray(J)=I) then return True; end if;
end loop;
return False;
end;
function IsItAnEdge (G: Graph;I: Integer; J: Integer ) return Boolean is
begin
return (G.EdgeMatrix(I,J)=Null);
end;
end Graph;
I get "Graf is not visable" and "subtype mark required in this context" error in the adb file.
Can you help me fix this whole thing?
OK, looking at the code so far, I think you may be mistaking a Package as a replacement for a C++ Class, whereas it's really more like a C++ Namespace.
C++ didn't have namespaces when I first used it but they are such a good organising principle that it tacked them on later. In contrast, packages were an original part of Ada.
Now a C++ Class (or Struct, or Union) would map onto an Ada Record. If it stands alone it can be a simple record, but if it's intended to be inheritable, it'll be a Tagged Record. Tagged Record, allowing inheritance, wasn't part of Ada-83, it was added 20 years ago in Ada-95.
And typical practice would be to wrap the Record and all its externally visible operations in a Package.
So I think you're looking for something like:
package Graph_Pkg is
type Graph is tagged private; -- hide everything about the actual record!
function IsItAPoint (G: Graph;I: Integer) return Boolean;
function IsItAnEdge (G: Graph;I: Integer; J: Integer ) return Boolean;
procedure NewEdge (G: in out Graph;I: Integer; J: Integer; S: Integer);
procedure NewPoint(G: in out Graph;I: Integer);
-- PointCount: Integer:=0; -- moved to package body
function PointCount return Integer;
private
-- Everything below here is hidden from package users
type PointArray is array(Integer range <>) Of Integer;
type EdgeMatrix is array(Integer range <>,
Integer range <>) of INTEGER;
type Graph is tagged record
-- here the member variables are declared
Points : PointArray;
end record;
end Graph_Pkg;
Now all the implementation details belong in the body.
package body Graph_Pkg is
-- The equivalent of C++ "static members" can be declared here
PointCount: Integer:=0;
-- and add the subprogram implementations here
end Graph_Pkg;
If you want the number of points in a graph to be variable after the graph has been constructed, I can see problems ahead in the implementation. You might want to look at Ada-2005 Container classes for that.

FreePascal adding elements on binaryTree

So im trying to add all 'spent' data, belonging to specific client number, on a binary tree.
Type
pointer_tree = ^bTree;
bTree = record
nclient: integer;
spent: integer;
big, small: pointer_tree
end;
{adder is 0}
function add (pT: pointer_tree; client_number: integer; adder: integer): integer;
begin
if pT <> nil then begin
if pT^.nclient = client_number then
adder:= pT^.spent + adder
add(pT^.big,client_number,adder);
add(pT^.small,client_number,adder);
end;
add:= adder;
end;
Function add will not return the added elements and will return a random number instead. Also is there a better way to add them all up?
I don't like the way that you're using the variable 'adder' as both a parameter to the function and also as a temporary store for the calculated value. I think it would be better if the function were written as follows, without the 'adder' variable:
function add (pT: pointer_tree; client_number: integer): integer;
begin
result:= 0;
if pT <> nil then
begin
if pT^.nclient = client_number then result:= pT^.spent;
inc (result, add (pT^.big, client_number));
inc (result, add (pT^.small, client_number));
end;
end;
Incidentally, your code is missing a semicolon after the 'adder:= pT^.spent + adder' line.

Oracle Forms 6i SendMessage with String

I am converting my pascal code to PL/SQL for use with Oracle Forms 6i. My pascal code below, works perfectly.
program WebcamTest;
//cswi
//www.delphibasics.info
const
WM_CAP_DRIVER_CONNECT = 1034;
WM_CAP_GRAB_FRAME = 1084;
WM_CAP_SAVEDIB = 1049;
WM_CAP_DRIVER_DISCONNECT = 1035;
function SendMessageA(hWnd: Integer;
Msg: Integer;
wParam: Integer;
lParam: Integer): Integer;
stdcall;
external 'user32.dll' name 'SendMessageA';
function capGetDriverDescriptionA(DrvIndex: Cardinal;
Name: PAnsiChar;
NameLen: Integer;
Description: PAnsiChar;
DescLen: Integer) : Boolean;
stdcall;
external 'avicap32.dll' name 'capGetDriverDescriptionA';
function capCreateCaptureWindowA(lpszWindowName: PAnsiChar;
dwStyle: Integer;
x : Integer;
y : Integer;
nWidth : Integer;
nHeight : Integer;
ParentWin: Integer;
nId: Integer): Integer;
stdcall;
external 'avicap32.dll' name 'capCreateCaptureWindowA';
function IntToStr(i: Integer): String;
begin
Str(i, Result);
end;
var
WebCamId : Integer;
CaptureWindow : Integer;
x : Integer;
FileName : PAnsiChar;
begin
WebcamId := 0;
CaptureWindow := capCreateCaptureWindowA('CaptureWindow', 0, 0, 0, 0, 0, 0, 0);
if CaptureWindow <> 0 then
begin
if SendMessageA(CaptureWindow, WM_CAP_DRIVER_CONNECT, WebCamId, 0) = 1 then
begin
for x := 1 to 20 do // Take 20 photos.
begin
SendMessageA(CaptureWindow, WM_CAP_GRAB_FRAME, 0, 0);
FileName := PAnsiChar('C:\Test' + IntToStr(x) + '.bmp');
SendMessageA(CaptureWindow, WM_CAP_SAVEDIB, 0, LongInt(FileName));
end;
end;
SendMessageA(CaptureWindow, WM_CAP_DRIVER_DISCONNECT, 0, 0);
end;
end.
I have successfully translated all of the apis using the ora_ffi package.
I am having trouble translating the below line from Pascal to PL/SQL (which saves the captured picture to a specified location identified by FileName).
SendMessageA(CaptureWindow, WM_CAP_SAVEDIB, 0, **LongInt**(FileName));
because I am unsure how to pass the FileName as a PLS_INTEGER (see bold for how I achieve this with pascal) which the SendMessageA definition requires. With pascal I can just cast the FileName to LongInt (does this return a pointer to the FileName? If so, I would like to emulate this casting with PL/SQL).
NOTE: The question is about how to convert Pascal to PL/SQL. The Pascal code works fine.
Any ideas?

How to get the Windows version is Vista and up versus XP on Delphi?

Is there any way to know which verion of Windows we are working on?
I need to set image to TBitButton in Windows XP and no image in Windows7. It should be done automatically.
Check the SysUtils.Win32MajorVersion (in Delphi 7, you'll need to add SysUtils to your uses clause if it's not there already - later versions add it automatically). The easiest way is to assign the Glyph as usual in the IDE, and clear it if you're running on Vista or higher:
if SysUtils.Win32MajorVersion >= 6 then // Windows Vista or higher
BitBtn1.Glyph := nil;
For more info on detecting specific Windows editions and versions, see this post. It hasn't been updated for the latest Windows versions and editions, but it'll get you started. You can also search SO for [delphi] GetVersionEx to see other examples.
This is actually a little project of mine - a drop-in component which provides info of the operating system - even preview it in design-time...
unit JDOSInfo;
interface
uses
Classes, Windows, SysUtils, StrUtils, Forms, Registry;
type
TJDOSInfo = class(TComponent)
private
fReg: TRegistry;
fKey: String;
fMinor: Integer;
fMajor: Integer;
fBuild: Integer;
fPlatform: Integer;
fIsServer: Bool;
fIs64bit: Bool;
fProductName: String;
function GetProductName: String;
procedure SetProductName(Value: String);
procedure SetMajor(Value: Integer);
procedure SetMinor(Value: Integer);
procedure SetBuild(Value: Integer);
procedure SetPlatform(Value: Integer);
procedure SetIs64Bit(const Value: Bool);
procedure SetIsServer(const Value: Bool);
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
published
property Major: Integer read fMajor write SetMajor;
property Minor: Integer read fMinor write SetMinor;
property Build: Integer read fBuild write SetBuild;
property Platf: Integer read fPlatform write SetPlatform;
property ProductName: String read GetProductName write SetProductName;
property IsServer: Bool read fIsServer write SetIsServer;
property Is64Bit: Bool read fIs64bit write SetIs64Bit;
end;
function IsWOW64: Boolean;
function GetOSInfo: TOSVersionInfo;
procedure Register;
implementation
procedure Register;
begin
RegisterComponents('JD Custom', [TJDOSInfo]);
end;
function GetOSInfo: TOSVersionInfo;
begin
FillChar(Result, SizeOf(Result), 0);
Result.dwOSVersionInfoSize := SizeOf(Result);
if not GetVersionEx(Result) then
raise Exception.Create('Error calling GetVersionEx');
end;
function IsWOW64: Boolean;
type
TIsWow64Process = function( // Type of IsWow64Process API fn
Handle: THandle;
var Res: BOOL): BOOL; stdcall;
var
IsWow64Result: BOOL; // result from IsWow64Process
IsWow64Process: TIsWow64Process; // IsWow64Process fn reference
begin
// Try to load required function from kernel32
IsWow64Process:= GetProcAddress(GetModuleHandle('kernel32'),'IsWow64Process');
if Assigned(IsWow64Process) then
begin
// Function is implemented: call it
if not IsWow64Process(GetCurrentProcess, IsWow64Result) then
raise Exception.Create('Bad process handle');
// Return result of function
Result := IsWow64Result;
end else
// Function not implemented: can't be running on Wow64
Result:= False;
end;
constructor TJDOSInfo.Create(AOwner: TComponent);
var
Info: TOSVersionInfo;
Str: String;
begin
inherited Create(AOwner);
fReg:= TRegistry.Create(KEY_READ);
fReg.RootKey:= HKEY_LOCAL_MACHINE;
fKey:= 'Software\Microsoft\Windows NT\CurrentVersion';
fReg.OpenKey(fKey, False);
Info:= GetOSInfo;
fMajor:= Info.dwMajorVersion;
fMinor:= Info.dwMinorVersion;
fBuild:= Info.dwBuildNumber;
fIsServer:= False;
fIs64bit:= False;
fPlatform:= Info.dwPlatformId;
if fMajor >= 5 then begin
//After 2000
if fReg.ValueExists('ProductName') then
Str:= fReg.ReadString('ProductName')
else begin
Str:= 'Unknown OS: '+IntToStr(fMajor)+'.'+IntToStr(fMinor)+'.'+
IntToStr(fBuild)+'.'+IntToStr(fPlatform);
end;
if fReg.ValueExists('InstallationType') then begin
if UpperCase(fReg.ReadString('InstallationType')) = 'SERVER' then
fIsServer:= True;
end;
fIs64bit:= IsWOW64;
if fIs64bit then
Str:= Str + ' 64 Bit';
end else begin
//Before 2000
case fMajor of
4: begin
case fMinor of
0: Str:= 'Windows 95';
10: Str:= 'Windows 98';
90: Str:= 'Windows ME';
end;
end;
else begin
Str:= 'Older than 95';
end;
end;
end;
Self.fProductName:= Str;
end;
destructor TJDOSInfo.Destroy;
begin
if assigned(fReg) then begin
if fReg.Active then
fReg.CloseKey;
fReg.Free;
end;
inherited Destroy;
end;
function TJDOSInfo.GetProductName: String;
begin
Result:= Self.fProductName;
end;
procedure TJDOSInfo.SetProductName(Value: String);
begin
//Do Nothing Here!
end;
procedure TJDOSInfo.SetMinor(Value: Integer);
begin
//Do Nothing Here!
end;
procedure TJDOSInfo.SetMajor(Value: Integer);
begin
//Do Nothing Here!
end;
procedure TJDOSInfo.SetBuild(Value: Integer);
begin
//Do Nothing Here!
end;
procedure TJDOSInfo.SetPlatform(Value: Integer);
begin
//Do Nothing Here!
end;
procedure TJDOSInfo.SetIs64Bit(const Value: Bool);
begin
//Do Nothing Here!
end;
procedure TJDOSInfo.SetIsServer(const Value: Bool);
begin
//Do Nothing Here!
end;
end.

Resources