I am at loss after spending hours on this.
Protected type declaration:
protected type AdditionMachine is
procedure setTask (Item : in WorkerTask);
procedure getTask (Item : out WorkerTask);
procedure calculate;
private
machineType : String (1 .. 1) := "A";
job : workerTask;
end AdditionMachine;
Task:
task body SimpleTask1 is
Available : Natural := ADDITION_MACHINES;
Elements : array (1 .. ADDITION_MACHINES) of AdditionMachine;
begin
loop
select
when Available > 0 =>
accept getMachine (machine : out AdditionMachine) do
machine := Elements(Available);
Available := Available - 1;
end getMachine;
or
accept addMachine (machine : in AdditionMachine) do
Available := Available + 1;
Elements(Available) := machine;
end addMachine;
end select;
end loop;
end SimpleTask1;
At line Elements(Available) := machine; and machine := Elements(Available); I get "left hand of assingment must not be limited type"
I have no idea how to fix this, I've found nothing when I googled, can anyone help?
edit: I now know that protected types are limited, but how do I accomplish a pool of protected objects without something like above?
From ARM 7.5, Limited Types,
A limited type is (a view of) a type for which copying (such as for an assignment_statement) is not allowed. A nonlimited type is a (view of a) type for which copying is allowed.
However, you are allowed to copy accesses to limited objects.
So, if you have declared
protected type AdditionMachine is
...
end AdditionMachine;
type AdditionMachine_Access is access AdditionMachine;
you can write something like this (I haven’t tested it):
task body SimpleTask1 is
Available : Natural := ADDITION_MACHINES;
Elements : array (1 .. ADDITION_MACHINES) of AdditionMachine_Access
:= (others => new AdditionMachine);
begin
You’ll need to take care of deallocating the allocated objects when you’ve done with them (if the program needs to run indefinitely, anyway). I don’t think there’ll be a problem with protected objects (but what happens if a task is waiting on a PO that you deallocate?), and recent GNATs are OK with deallocating tasks provided the tasks have open terminate alternatives, ARM 9.7.1, or have already terminated - perhaps by being aborted).
I have the same query only the connections change :
if DataModule1.1_CONNECTION.Connected = true
then begin
DataModule1.ZAM_GESLO.SQL.Text:='select user,pwd from users where user = :a';
DataModule1.ZAM_GESLO.Params.ParamByName('a').AsString := DataModule1.LOGIN_QUERY.FieldByName('user').AsString;
DataModule1.ZAM_GESLO.Open;
cxGrid1.ActiveLevel.GridView := MYGRIDVIEW1;
end else
if DataModule1.2_CONNECTION.Connected = true
then begin
DataModule1.ZAM_GESLO.SQL.Text:='select user,pwd from users where user = :a';
DataModule1.ZAM_GESLO.Params.ParamByName('a').AsString := DataModule1.LOGIN_QUERY.FieldByName('user').AsString;
DataModule1.ZAM_GESLO.Open;
cxGrid1.ActiveLevel.GridView := MYGRIDVIEW2;
end;
.......
This is a long way arround,so I was wondering if this can be done in any other optimized way so I dont have to write the same query all over again?
To save duplicated code, you could add a procedure to your form
procedure TxForm.UseDataSet(ADataSet : TxDataSet; AUserName : String);
begin
if ADataSet.Active then
ADataSet.Close;
ADataSet.SQL.Text:='select user, pwd from users where user = :a';
ADataSet.Params.ParamByName('a').AsString := AUser;
ADataSet.Open;
cxGrid1.ActiveLevel.GridView := AGridView;
end;
Note: I've used "x" in the ADataset parameter's type because I didn't know which type of dataset you're using, likewise your Form's type.
Then, you could re-write your code as
if DataModule1.1_CONNECTION.Connected then
begin
UseDataSet(DataModule1.ZAM_GESLO,
DataModule1.LOGIN_QUERY.FieldByName('user').AsString);
end
else
if DataModule1.2_CONNECTION.Connected then
begin
UseDataSet(DataModule1.ZAM_GESLO,
DataModule1.LOGIN_QUERY.FieldByName('user').AsString);
end;
Btw, it's good that you obviously wondered about duplicated code when you asked your q. Whenever you find yourself writing essentially the same code using different objects, consider writing a procedure (or function) which contains the repetitive code and which operates on the objects as parameters.
I've got the following LUT (lookup table) for retrieval of display name for pseudo-PChar (all these predefined PChars are integers under their skin, you know) input:
const
RT_MIN = DWORD(RT_CURSOR);
RT_MAX = DWORD(RT_MANIFEST);
ResourceTypes: array [RT_MIN..RT_MAX] of PChar = (
'Hardware-dependent cursor',
'Bitmap',
'Hardware-dependent icon',
'Menu',
'Dialog box',
'String-table entry',
'Font directory',
'Font',
'Accelerator table',
'Application-defined resource (raw data)',
'Message-table entry',
'Hardware-independent cursor',
nil, { unknown, reserved or not used }
'Hardware-independent icon',
nil, { unknown, reserved or not used }
'Version',
'Dialog Include',
nil, { unknown, reserved or not used }
'Plug and Play',
'VxD',
'Animated cursor',
'Animated icon',
'HTML resource',
'Side-by-Side Assembly Manifest'
);
Will I get any advantages/disadvantages in rewriting that as case statement? Are there any advantages/disadvantages in leaving that as is?
I think that using an array is the fastest method. If you e.g. query ResourceTypes[2], the program will first look at ResourceTypes[2], dereference the PChar and output the zero terminated string. If the compiler is smart, it could recognize that the strings are unchangeable and so it could place all strings directly in the array, so you would save one dereferencing operation. (For those who are interested in it, can view the memory contents using an hex-editor like HxD to check if this is true or not).
Another problem which might happen in future could be following scenario: Let's say Microsoft defines a new resource type which is something very special, and so it gets a large number like $FFFF . If you are using case of, you can simply add 2 lines of code to add this new resource type. By having a lookup-table (or LUT, this abbreviation is new to me), you would have a problem then, since you would need to create an array with size 65535 whose contents are to 99% just nils.
I would accomplish it by creating a function:
function GetHumanFriendlyResourceTypeName(AResourceType: PChar): string;
begin
if not Is_IntResource(AResourceType) then
begin
result := AResourceType;
end
else
begin
case Integer(AResourceType) of
Integer(RT_CURSOR):
result := 'Hardware-dependent cursor';
Integer(RT_BITMAP):
result := 'Bitmap';
Integer(RT_ICON):
result := 'Hardware-dependent icon';
Integer(RT_MENU):
result := 'Menu';
Integer(RT_DIALOG):
result := 'Dialog box';
Integer(RT_STRING):
result := 'String-table entry';
Integer(RT_FONTDIR):
result := 'Font directory';
Integer(RT_FONT):
result := 'Font';
Integer(RT_ACCELERATOR):
result := 'Accelerator table';
Integer(RT_RCDATA):
result := 'Application-defined resource (raw data)';
Integer(RT_MESSAGETABLE):
result := 'Message-table entry';
Integer(RT_GROUP_CURSOR):
result := 'Hardware-independent cursor';
Integer(RT_GROUP_ICON):
result := 'Hardware-independent icon';
Integer(RT_VERSION):
result := 'Version';
Integer(RT_DLGINCLUDE):
result := 'Dialog Include';
Integer(RT_PLUGPLAY):
result := 'Plug and Play';
Integer(RT_VXD):
result := 'VxD';
Integer(RT_ANICURSOR):
result := 'Animated cursor';
Integer(RT_ANIICON):
result := 'Animated icon';
Integer(RT_HTML):
result := 'HTML resource';
Integer(RT_MANIFEST):
result := 'Side-by-Side Assembly Manifest';
else
result := Format('(Unknown type %d)', [Integer(AResourceType)]);
end;
end;
end;
Here is a demonstration of the code:
procedure TForm1.Button1Click(Sender: TObject);
begin
// Hardware-dependent icon
ShowMessage(GetHumanFriendlyResourceTypeName(MAKEINTRESOURCE(3)));
// (Unknown type 123)
ShowMessage(GetHumanFriendlyResourceTypeName(MAKEINTRESOURCE(123)));
// AVI
ShowMessage(GetHumanFriendlyResourceTypeName(PChar('AVI')));
end;
The performance is not as high as in your solution, but this function has several advantages:
This function is much easier to read since every RT_ constant is standing in front of its human-friendly name. So the code is also much better to maintain. In the LUT, the human-friendly names could be accidently interchanged (also since no comment in front of each human-friendly name indicates the official RT_ constant name).
This function does also show a nice human-friendly string "(Unknown type 123)" if the identifier is unknown.
This function will also dereference the string if it is not a predefined type (RT_)
Using this function you can internationalize your application either statically by putting the strings into resourcestrings or dynamically by querying a translation function/stringlist.
I have a problem with printing
procedure Sendparams(const Pparams,pparvalues :array of string);
begin
for I := 0 to Length(Pparams) - 1 do
begin
lpar_name:=Pparams[i];
lpar_val:=pparvalues[i] ;
FfrxReport.Variables.AddVariable('Bez', lpar_name, lpar_val);
end;
Sendparams(['buyer','delivery'], ['buyer address', 'delivery address']);
Everything works fine until I try to print report; it says: Expression expected on Memo2.
Memo1.memo = '[buyer]';
Memo2.memo = '[delivery]';
memo1 and memo2 all other properties are the same. Any suggestions?
There are different possible traps.
If you want to use Addvariable (instead of variables.add) the category, in your case Bez has to be defined in the report, otherwise the variables won't be add. **
The assignment of the variables within the report hast to look like Memo1.Lines.Text :=<buyer>;
You will have to quote the string values of the variables
Sendparams(['buyer','delivery'], [QuotedStr('buyer address'), QuotedStr('delivery address')]);
**
Another attempt could be something like this, to avoid open arrays of string (where count of names and values accidentally could differ), to avoid a hard reference to the report within Sendparams and to deal with variables which already could be defined within the report.
Function PrepareReport(Report:TfrxReport; Variables: TfrxVariables;
ReportName: String):Boolean;// -- other parameters
var
i,k:Integer;
begin
// ....... other initializations
if Assigned(Variables) then
for i := 0 to Variables.Count - 1 do
begin
k := Report.Variables.IndexOf(Variables.Items[i].Name);
if k > -1 then
Report.Variables.Items[k].Value := Variables.Items[i].Value
else
begin
with Report.Variables.Add do
begin
Name := Variables.Items[i].Name;
Value := Variables.Items[i].Value;
end;
end;
end;
end;
I'm trying to evaluate ODAC for using Oracle AQ.
The request queue contains JMS objects like these (but without linebreaks and other whitespace):
SYS.AQ$_JMS_BYTES_MESSAGE(
SYS.AQ$_JMS_HEADER(
'null','null','null','null','null','null',
SYS.AQ$_JMS_USERPROPARRAY(
SYS.AQ$_JMS_USERPROPERTY('Key1',100,'Value1','null',27),
SYS.AQ$_JMS_USERPROPERTY('Key2',100,'Value2','null',27),
SYS.AQ$_JMS_USERPROPERTY('Key3',100,'Value3','null',27),
SYS.AQ$_JMS_USERPROPERTY('Key4',100,'Value4','null',27),
SYS.AQ$_JMS_USERPROPERTY('Key5',100,'Value5','null',27),
SYS.AQ$_JMS_USERPROPERTY('Key6',100,'Value6','null',27),
SYS.AQ$_JMS_USERPROPERTY('Key7',100,'Value7','null',27),
SYS.AQ$_JMS_USERPROPERTY('Key8',100,'Value8','null',27),
SYS.AQ$_JMS_USERPROPERTY('Key9',100,'Value9','null',27),
SYS.AQ$_JMS_USERPROPERTY('Key10',100,'Value10.0','null',27),
SYS.AQ$_JMS_USERPROPERTY('Key11',100,'Value11','null',27),
SYS.AQ$_JMS_USERPROPERTY('Key12',100,'Value12','null',27),
SYS.AQ$_JMS_USERPROPERTY('Key13',100,'Value13','null',27),
SYS.AQ$_JMS_USERPROPERTY('Key14',100,'Value14','null',27),
SYS.AQ$_JMS_USERPROPERTY('Key15',100,'Value15','null',27),
SYS.AQ$_JMS_USERPROPERTY('Key16',100,'Value16','null',27),
SYS.AQ$_JMS_USERPROPERTY('Key17',100,'Value17','null',27)
)
),
4168,'null','oracle.sql.BLOB#959acc'
)
I can receive the underlying object (a string Payload comes back as an empty string, but a TOraObject PayLoad contains data).
I'm trying to disscect the TOraObject PayLoad, and am looking for a table that converts the DataType values into the correct AttrXxxx[Name] property calls.
OraType.AttributeCount:4
OraType.Name:"SYS"."AQ$_JMS_BYTES_MESSAGE"
OraType.DataType:15
Attribute[0].Name:HEADER
Attribute[0].DataType:15
OraType.AttributeCount:7
OraType.Name:"SYS"."AQ$_JMS_HEADER"
OraType.DataType:15
Attribute[0].Name:REPLYTO
Attribute[0].DataType:15
OraType.AttributeCount:3
OraType.Name:"SYS"."AQ$_AGENT"
OraType.DataType:15
Attribute[0].Name:NAME
Attribute[0].DataType:1
Attribute[1].Name:ADDRESS
Attribute[1].DataType:1
Attribute[2].Name:PROTOCOL
Attribute[2].DataType:5
Attribute[1].Name:TYPE
Attribute[1].DataType:1
Attribute[2].Name:USERID
Attribute[2].DataType:1
Attribute[3].Name:APPID
Attribute[3].DataType:1
Attribute[4].Name:GROUPID
Attribute[4].DataType:1
Attribute[5].Name:GROUPSEQ
Attribute[5].DataType:5
Attribute[6].Name:PROPERTIES
Attribute[6].DataType:17
OraType.AttributeCount:1
OraType.Name:"SYS"."AQ$_JMS_USERPROPARRAY"
OraType.DataType:17
Attribute[0].Name:ELEMENT
Attribute[0].DataType:15
OraType.AttributeCount:5
OraType.Name:"SYS"."AQ$_JMS_USERPROPERTY"
OraType.DataType:15
Attribute[0].Name:NAME
Attribute[0].DataType:1
Attribute[1].Name:TYPE
Attribute[1].DataType:5
Attribute[2].Name:STR_VALUE
Attribute[2].DataType:1
Attribute[3].Name:NUM_VALUE
Attribute[3].DataType:5
Attribute[4].Name:JAVA_TYPE
Attribute[4].DataType:5
Attribute[1].Name:BYTES_LEN
Attribute[1].DataType:5
Attribute[2].Name:BYTES_RAW
Attribute[2].DataType:1
Attribute[3].Name:BYTES_LOB
Attribute[3].DataType:102
By trial and error, I have come so far:
case DataType of
102:
LOB := ObjectPayLoad.AttrAsLob[Name];
15:
AttributeOraObject := ObjectPayLoad.AttrAsObject[Name];
17:
AttributeOraArray := ObjectPayLoad.AttrAsArray[Name];
else
begin
PayLoadAttributeAsString := ObjectPayLoad. AttrAsString[Name];
Logger.Log(' "%s"', [PayLoadAttributeAsString]);
end;
end;
A more complete list is welcome :-)
After this, I will need to research the other way around: generating the right TOraObject that has a JMS content in it.
Tips for that are also welcome.
--jeroen
Edit:
ODAC has multiple units defining constants.
The constant dtOraBlob with value 102 is in the OraClasses unit; constants defining DataType values start with the prefix dt, regardless of the unit that defines them.
Original:
I have found a few of these constants in the MemData unit:
case DataType of
102:
LOB := OraObject.AttrAsLob[Name];
MemData.dtObject: // 15
begin
AttributeOraObject := OraObject.AttrAsObject[Name];
LogOraObject(AttributeOraObject, Level+1);
end;
MemData.dtArray: // 17
begin
AttributeOraArray := OraObject.AttrAsArray[Name];
LogOraArray(AttributeOraArray, Level);
end;
MemData.dtFloat: // 5
begin
AttributeFloat := OraObject.AttrAsFloat[Name];
Logger.Log(Prefix+'"%g"', [AttributeFloat]);
end;
MemData.dtString: // 1
begin
PayLoadAttributeAsString := OraObject.AttrAsString[Name];
Logger.Log(Prefix+'"%s"', [PayLoadAttributeAsString]);
end;
else
begin
PayLoadAttributeAsString := OraObject.AttrAsString[Name];
Logger.Log(Prefix+'"%s"', [PayLoadAttributeAsString]);
end;
end;
I can't find the 102 constant though, but I'm pretty sure it is for a LOB field.
Anyone who can confirm that?
--jeroen