I have a class of the following shape
TParser = class
private
FFlag : boolean;
FIntermediateValue : double;
procedure F1(var aPartOfInput : string);
procedure F2(var aSmallerPartOfInput : string) ;
public
function Parse(const anInput : string): double;
end;
function TParser.Parse(const anInput : string) : double;
var
aPartOfInput : string;
begin
{ Do some checks on the input and set FFlag to true/false.
Set aPartOfInput to a piece of the input}
f1(aPartOfInput);
Result := FIntermediateValue;
end;
procedure TParser.F1(var aPartOfInput : string);
begin
{ Slice some more off the input.
Set the FIntermediateValue }
f2(aPartOfInput)
end;
procedure TParser.F2(var aSmallerPartOfInput : string);
begin
{ Depending on the input and FFlag, update FIntermediateValue }
end;
We currently create/destroy a squillion (technical term) of these through the system.
I am looking at creating a single instance and invoking it from each of these places.
This single instance will be invoked from multiple threads.
Does the use of the private fields mean that this is not thread safe?
If this is not thread safe, what are my options for making it thread safe?
EDIT - Options
Thanks all for the comments.
A separate instance for each thread
Sounds good, but because of the way that the code is structured, the instance would need to be 'global' to the thread, there is no way (as far as I can see) to inject the instance down the object graph to where is it needed (at the moment, new instances of the parser are created where they are needed)
threadvar
TheParser : TParser;
seems to be a way to give me this. Create the instance when we start the thread and free it on leaving. Are there any problems with this?
Get rid of the instance variables
If we re-design the parser so that it is a function rather than an instance of a class and all the values are passed around, would this make it thread safe?
interface
function Parse(const anInput : string): double;
implementation
function F2(var aSmallerPartOfInput : string; theFlag : boolean; theIntermediateValue : double) : double ;
begin
{ update theIntermediateValue based on flag and input }
Result := theIntermediateValue;
end;
function F1(var aPartOfInput : string; theFlag : boolean) : double;
var
anIntermediateValue : double;
begin
{ Slice some more off the input
Set the FIntermediateValue }
Result := f2(aPartOfInput, theFlag, anIntermediateValue)
end;
function Parse(const anInput : string) : double;
var
aPartOfInput : string;
aFlag : boolean;
begin
{ set aFlag
set aPartOfInput to a piece of the input }
Result := f1(aPartOfInput, aFlag);
end;
No, they aren't.
You must synchronize the access, if you want to use the same object instance within different threads.
On Windows just use a critical section (TRTLCriticalSection) and on posix os' use mutex (pthread_mutex_t)
You always can choose to design your class idempotent, so you can have a instance of it in each thread and just use it in there.
Related
I saw in this question: Empty string becomes null when passed from Delphi to C# as a function argument that Delphi's empty string value in reality is just a null-pointer - which I understand the reasoning behind.
I do have an issue though as I am developing a Web API in Delphi and I am having trouble implementing a PATCH endpoint and I wondered if anyone has had the same issue as me.
If i have a simple resource Person which looks like this.
{
"firstName": "John",
"lastName": "Doe",
"age": 44
}
and simply want to change his lastName property using a PATCH document - I would sent a request that looks like this:
{
"lastName": "Smith"
}
Now - in my api, using Delphis System.JSON library I would just check if the request has the firstName and age properties before setting them in the request handler which sets the properties in an intermediate object PersonDTO, but later I have to map these values to the actual Person instance - and here comes my issue:
When mapping between multiple objects I cannot tell if a string is empty because it was never set (and should be treated as null) or was explicitly set to '' to remove a property from my resource - How do I circumvent this?
if personDTO.FirstName <> '' then
personObject.FirstName := personDTO.FirstName;
Edit: I have considered setting the strings to #0 in the DTO's constructor to distinguish between null and '' but this is a large (1M line) code base, so I would prefer to find a robust generic way of handling these scenarios
Delphi does not differentiate between an empty string and an unassigned string. They are implemented the exact same way - as a nil pointer. So, you will have to use a different type that does differentiate, such as a Variant. Otherwise, you will have to carry a separate boolean/enum flag alongside the string to indicate its intended state. Or, wrap the string value inside of a record/class type that you can set a pointer at when assigned and leave nil when unassigned.
in Delphi String is an array, but it's an array a little longer than the actual count of characters. For exemple in Delphi string always end up with the #0 at high(myStr) + 1. this is need when casting the string to pchar. if in your flow you don't plan to cast the string to pchar then you can write a special char in this "invisible" characters to distinguish between null and empty (actually i never tested this solution)
The answer is in your question itself. You need to know what has been supplied. This means that you either need to use what was actually provided to the API rather than serialising into an object (which has to include all the members of the object), or you need to serialise into an object whose members will support you knowing whether they have been set or not.
If you are serialising into an intermediate object for the API then when you come to update your actual application object you can use an assign method that only sets the members of the application object that were set in the API. Implementing these checks in the intermediate object for your API means that you won't have to change any code in the main application.
Code that suggests how you might do this:
unit Unit1;
interface
uses Classes;
type
TAPIIVariableStates = (APIVarSet, APIVarIsNull);
TAPIVariableState = Set of TAPIIVariableStates;
TAPIString =class(TObject)
protected
_szString: String;
_MemberState: TAPIVariableState;
function _GetHasBeenSet(): Boolean; virtual;
function _GetIsNull(): Boolean; virtual;
function _GetString(): String; virtual;
procedure _SetString(szNewValue: String); virtual;
public
procedure AfterConstruction(); override;
procedure Clear(); virtual;
procedure SetToNull(); virtual;
property Value: String read _GetString write _SetString;
property HasBeenSet: Boolean read _GetHasBeenSet;
property IsNull: Boolean read _GetIsNull;
end;
TAPIPerson = class(TPersistent)
protected
FFirstName: TAPIString;
FLastName: TAPIString;
FComments: TAPIString;
procedure AssignTo(Target: TPersistent); override;
function _GetComments(): String; virtual;
function _GetFirstName(): String; virtual;
function _GetLastName(): String; virtual;
procedure _SetComments(szNewValue: String); virtual;
procedure _SetFirstName(szNewValue: String); virtual;
procedure _SetLastName(szNewValue: String); virtual;
public
destructor Destroy; override;
procedure AfterConstruction(); override;
property FirstName: String read _GetFirstName write _SetFirstName;
property LastName: String read _GetLastName write _SetLastName;
property Comments: String read _GetComments write _SetComments;
end;
TApplicationPerson = class(TPersistent)
protected
FFirstName: String;
FLastName: String;
FComments: String;
public
property FirstName: String read FFirstName write FFirstName;
property LastName: String read FLastName write FLastName;
property Comments: String read FComments write FComments;
end;
implementation
uses SysUtils;
destructor TAPIPerson.Destroy();
begin
FreeAndNil(Self.FFirstName);
FreeAndNil(Self.FLastName);
FreeAndNil(Self.FComments);
inherited;
end;
procedure TAPIPerson.AfterConstruction();
begin
inherited;
Self.FFirstName:=TAPIString.Create();
Self.FLastName:=TAPIString.Create();
Self.FComments:=TAPIString.Create();
end;
procedure TAPIPerson.AssignTo(Target: TPersistent);
begin
if(Target is TApplicationPerson) then
begin
if(Self.FFirstName.HasBeenSet) then
TApplicationPerson(Target).FirstName:=Self.FirstName;
if(Self.FLastName.HasBeenSet) then
TApplicationPerson(Target).LastName:=Self.LastName;
if(Self.FComments.HasBeenSet) then
TApplicationPerson(Target).Comments:=Self.Comments;
end
else
inherited;
end;
function TAPIPerson._GetComments(): String;
begin
Result:=Self.FComments.Value;
end;
function TAPIPerson._GetFirstName(): String;
begin
Result:=Self.FFirstName.Value;
end;
function TAPIPerson._GetLastName(): String;
begin
Result:=Self.FLastName.Value;
end;
procedure TAPIPerson._SetComments(szNewValue: String);
begin
Self.FComments.Value:=szNewValue;
end;
procedure TAPIPerson._SetFirstName(szNewValue: String);
begin
Self.FFirstName.Value:=szNewValue;
end;
procedure TAPIPerson._SetLastName(szNewValue: String);
begin
Self.FLastName.Value:=szNewValue;
end;
procedure TAPIString.AfterConstruction();
begin
inherited;
Self._MemberState:=[APIVarIsNull];
end;
procedure TAPIString.Clear();
begin
Self._szString:='';
Self._MemberState:=[APIVarIsNull];
end;
function TAPIString._GetHasBeenSet(): Boolean;
begin
Result:=(APIVarSet in Self._MemberState);
end;
function TAPIString._GetIsNull(): Boolean;
begin
Result:=(APIVarIsNull in Self._MemberState);
end;
function TAPIString._GetString(): String;
begin
Result:=Self._szString;
end;
procedure TAPIString._SetString(szNewValue: String);
begin
Self._szString:=szNewValue;
Include(Self._MemberState, APIVarSet);
(* optionally treat an emoty strung and null as the same thing
if(Length(Self._szString)=0) then
Include(Self._MemberState, APIVarIsNull)
else
Exclude(Self._MemberState, APIVarIsNull); *)
end;
procedure TAPIString.SetToNull();
begin
Self._szString:='';
Self._MemberState:=[APIVarSet, APIVarIsNull];
end;
end.
Using AssignTo in the TAPIPerson means that if your TApplicationPerson object derives from TPersistent (and has a properly implemented Assign method) then you can just use <ApplicationPersonObject>.Assign(<APIPersonObject>) to update just those fields which have changed. Otherwise you need a public method in the TAPIPerson that will update the TApplicationPerson appropriately.
i use this component for processing drag and drop files
http://melander.dk/delphi/dragdrop
unit DragThread;
interface
uses
Classes,DragDrop, DropTarget,DragDropFile,Dialogs,SysUtils;
type
TDragThread = class(TThread)
private
{ Private declarations }
ArraysLength : Integer;
DragComponent : TDropFileTarget;
DragArray,HashsArray : Array of string;
Procedure FDArray;
//Procedure FDHArray;
protected
procedure Execute; override;
Public
Constructor Create(Com: TDropFileTarget);
Destructor Destroy; Override;
end;
implementation
{ TDragThread }
Constructor TDragThread.Create(Com: TDropFileTarget);
begin
inherited Create(True);
DragComponent := Com;
end;
Destructor TDragThread.Destroy;
begin
//DragComponent.Free;
end;
Procedure TDragThread.FDArray;
var
A : Integer;
begin
SetLength(DragArray,DragComponent.Files.Count);
SetLength(HashsArray,DragComponent.Files.Count);
ShowMessage(IntToStr(DragComponent.Files.Count)); // just working in the first time !!
for A := 0 to DragComponent.Files.Count -1 do begin
DragArray[A] := DragComponent.Files[A];
//ShowMessage(DragComponent.Files[A]);
end;
ArraysLength := DragComponent.Files.Count-1;
//ShowMessage(DragComponent.Files[0]);
end;
procedure TDragThread.Execute;
begin
{ Place thread code here }
FDArray;
end;
end.
the strange thing that the Drop process working just one time then the DragComponent.Files.Count gives 0 for ever .!!
that's how i call it
procedure TForm1.DropFileDrop(Sender: TObject; ShiftState: TShiftState;
APoint: TPoint; var Effect: Integer);
var
DropThread : TDragThread;
begin
DropThread := TDragThread.Create(DropFile);
DropThread.Resume;
end;
i want to know why this happened and thanks in advance :) .
Don't operate VCL components from other threads.
There's no guarantee that the component's drop-event information will continue to be valid once the drop event has completed.
Copy all the information you need out of the component when you construct the thread (i.e., fully populate DragArray) and then use that cached data when executing the thread. Don't store a reference in DragComponent or you might be tempted to use it from the thread's Execute method, which you really shouldn't do.
Is it possible to call a function whose name is stored in a string in Delphi?
Please give more details on what are you trying to achieve.
As far as I know:
It is not possible to call a random function like that.
For class and object functions (MyObject.Function) this can be done with RTTI, but it's a lot of work.
If you just need to call one particular type of functions (say, function(integer, integer): string), it's a lot easier.
For the last one, declare a function type, then get a function pointer and cast it like this:
type
TMyFuncType = function(a: integer; b: integer): string of object;
TMyClass = class
published
function Func1(a: integer; b: integer): string;
function Func2(a: integer; b: integer): string;
function Func3(a: integer; b: integer): string;
public
function Call(MethodName: string; a, b: integer): string;
end;
function TMyClass.Call(MethodName: string; a, b: integer): string;
var m: TMethod;
begin
m.Code := Self.MethodAddress(MethodName); //find method code
m.Data := pointer(Self); //store pointer to object instance
Result := TMyFuncType(m)(a, b);
end;
{...}
//use it like this
var MyClass: TMyClass;
begin
MyClass := TMyClass.Create;
MyClass.Call('Func1', 3, 5);
MyClass.Call('Func2', 6, 4);
MyClass.Destroy;
end.
You didn't specify your Delphi version, However if you have Delphi 2010(+) you can do it using the enhanced RTTI, I'm not expert on them, but I tried this sample for you:
TProcClass = class
public
procedure SayHi;
function GetSum(X,Y:Integer): Integer;
end;
uses
Rtti;
{ TProcClass }
procedure TProcClass.SayHi;
begin
ShowMessage('Hi');
end;
function TProcClass.GetSum(X, Y: Integer): Integer;
begin
ShowMessage(IntToStr(X + Y));
end;
procedure ExecMethod(MethodName:string; const Args: array of TValue);
var
R : TRttiContext;
T : TRttiType;
M : TRttiMethod;
begin
T := R.GetType(TProcClass);
for M in t.GetMethods do
if (m.Parent = t) and (m.Name = MethodName)then
M.Invoke(TProcClass.Create,Args)
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
ExecMethod('SayHi',[]);
ExecMethod('GetSum',[10,20]);
end;
The good things, if you have procedure or function with parameters it will work without more work.
I'm surprised no one has suggested a dispatch table. This is exactly what it's for.
program RPS;
uses
SysUtils,
Generics.Collections;
type
TDispatchTable = class(TDictionary<string, TProc>);
procedure Rock;
begin
end;
procedure Paper;
begin
end;
procedure Scissors;
begin
end;
var
DispatchTable: TDispatchTable;
begin
DispatchTable := TDispatchTable.Create;
try
DispatchTable.Add('Rock', Rock);
DispatchTable.Add('Paper', Paper);
DispatchTable.Add('Scissors', Scissors);
DispatchTable['Rock'].Invoke; // or DispatchTable['Rock']();
finally
DispatchTable.Free;
end;
end.
The implementation I wrote uses generics so it would only work with Delphi 2009+. For older versions it would probably be easiest to implement using TStringList and the command pattern
With Delphi 2010 you can uses JSON and SuperObject to invoke method with parametters.
http://code.google.com/p/superobject/source/browse/#svn/trunk
If you need, there is also an xml parser to transform xml to json.
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
procedure TestMethod(const value: string);
end;
var
Form1: TForm1;
implementation
uses superobject;
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
begin
SOInvoke(Self, 'TestMethod', SO('{value: "hello"}'));
end;
procedure TForm1.TestMethod(const value: string);
begin
Caption := value;
end;
If you are asking if there is something like the JavaScript eval() is possible in Delphi, no this is not (easily) achievable since Delphi compiles to native code.
If you need only to support some strings you can always do many if or a case... Something like:
if myString = 'myFunction' then
myFunction();
OK, I'm very late to the party, but you can definitely call routines by name with this code (There are some limitations thought)
type
TExec = procedure of Object;
// rest of section...
procedure TMainForm.ExecuteMethod(MethodName : String);
var
Exec : TExec;
Routine : TMethod;
begin
Routine.Data := Pointer(Form1);
Routine.Code := Form1.MethodAddress(MethodName);
if Not Assigned(Routine.Code) then
Exit;
Exec := TExec(Routine);
Exec;
end;
Just in case someone needs this for Delphi 7 / 2010
Put each function in an Action. Then you can find the Action by name and Execute it
function ExecuteActionByName(const S: String);
var
I: Integer;
begin
for I := 0 to MainForm.ComponentCount-1 do
if (MainForm.Components[I] is TAction)
and SameText(TAction(MainForm.Components[I]).Name,S) then
begin
TAction(MainForm.Components[I]).Execute;
Break;
end;
end;
You can do something like this by crafting one or more classes with published properties that use functions to implement their read and write functionality. The properties can then be discovered using RTTI reflection and referenced, causing the underlying functions to get called.
Alternatively, you can store function pointers in a table, or even the Object property of TStringList and effectively index them by string name.
Straight calling of a function by name is not possible in Delphi.
The following simple solution using exports and GetProcAddress also works for old Delphi versions:
type
TMyProc = procedure(const value: Integer);
procedure Test(const value: Integer);
exports Test;
implementation
procedure Test(const value: string);
begin
ShowMessage('It works! ' + value);
end;
procedure TForm1.Button1Click(Sender: TObject);
var
p: TMyProc;
begin
p := GetProcAddress(HInstance, 'Test');
if Assigned(p) then P('Yes');
end;
function ExecuteMethod(AClass : TClass; AMethodName : String; const AArgs: Array of TValue) : TValue;
var
RttiContext : TRttiContext;
RttiMethod : TRttiMethod;
RttiType : TRttiType;
RttiObject : TObject;
begin
RttiObject := AClass.Create;
try
RttiContext := TRttiContext.Create;
RttiType := RttiContext.GetType(AClass);
RttiMethod := RttiType.GetMethod(AMethodName);
Result := RttiMethod.Invoke(RttiObject,AArgs);
finally
RttiObject.Free;
end;
end;
In a single-threaded application I use code like this:
Interface
function GetNextUID : integer;
Implementation
function GetNextUID : integer;
const
cUID : integer = 0;
begin
inc( cUID );
result := cUID;
end;
This could of course be implemented as a singleton object, etc. - I'm just giving the simplest possible example.
Q: How can I modify this function (or design a class) to achieve the same result safely from concurrent threads?
You can use the Interlocked* functions:
function GetNextUID : integer;
{$J+} // Writeble constants
const
cUID : integer = 0;
begin
Result := InterlockedIncrement(cUID);
end;
More modern Delphi versions have renamed these methods into Atomic* (like AtomicDecrement, AtomicIncrement, etc), so the example code becomes this:
function GetNextUID : integer;
{$J+} // Writeble constants
const
cUID : integer = 0;
begin
Result := AtomicIncrement(cUID);
end;
The easiest way would probably be to just call InterlockedIncrement to do the job.
With modern Delphi compilers it is better to use Increment function of class TInterlocked from unit System.SyncObjs. Something like this:
type
TMyClass = class
class var
FIdCounter: int64;
var
FId: int64;
constructor Create;
end;
constructor TMyClass.Create;
begin
FId := TInterlocked.Increment(FIdCounter);
end;
This helps to keep the code platform-independent.
Question: What I'm looking for is the most typical or best practice way to use a separate thread to receive data using an IdTCPClient in Indy 10.
Background: The below code is a sample of what I'm trying to do with the actual data processing parts removed for clarity. The Idea of the Thread is to receive all data (Variable size with a header declaring the rest of the message length) and to then to parse it (That's what the HandleData procedure does) and trigger an Event Handler depending on the command.
The TIdIOHandlerSocket is passed to the thread by the main application which also Writes data to the socket as and when it is required.
TScktReceiveThread = class(TThread)
private
{ Private declarations }
procedure HandleData;
protected
procedure Execute; override;
public
FSocket: TIdIOHandlerSocket;
constructor Create(CreateSuspended: boolean);
end;
procedure TScktReceiveThread.Execute;
var
FixedHeader: TBytes;
begin
Assert(FSocket <> nil, 'You must assign the connected socket to the receiving thread');
SetLength(FixedHeader, 2);
while not Terminated do
begin
if not FSocket.Connected then
Suspend
else
begin
FSocket.CheckForDataOnSource(10);
if not FSocket.InputBufferIsEmpty then
begin
FSocket.ReadBytes(FixedHeader, SizeOf(FixedHeader), false);
// Removed the rest of the reading and parsing code for clarity
Synchronize(HandleData);
end;
end;
end;
end;
As a prefix, I have used another StackOverflow question which deals with the server components of Indy: "Delphi 2009, Indy 10, TIdTCPServer.OnExecute, how to grab all the bytes in the InputBuffer" to get the basis of what I have so far.
Thanks for any help!
If you want to avoid the overhead imposed by creating thread classes for each and every client-server data exchange, you could create a motile threading class as described in
http://delphidicas.blogspot.com/2008/08/anonymous-methods-when-should-they-be.html
I had the same problem a few days ago and I just wrote me a class TMotileThreading which has static functions that let me create threads using the new anonymous method feature of D2009. Looks something like this:
type
TExecuteFunc = reference to procedure;
TMotileThreading = class
public
class procedure Execute (Func : TExecuteFunc);
class procedure ExecuteThenCall (Func : TExecuteFunc; ThenFunc : TExecuteFunc);
end;
The second procedure allows me to perform a client-server communication like in your case and do some stuff whenever the data has arrived. The nice thing about anonymous methods is that you can use the local variables of the calling context. So a communication looks something like this:
var
NewData : String;
begin
TMotileThreading.ExecuteThenCall (
procedure
begin
NewData := IdTCPClient.IOHandler.Readln;
end,
procedure
begin
GUIUpdate (NewData);
end);
end;
The Execute and ExecuteThenCall method simply create a worker thread, set FreeOnTerminate to true to simplify memory management and execute the provided functions in the worker thread's Execute and OnTerminate procedures.
Hope that helps.
EDIT (as requested the full implementation of class TMotileThreading)
type
TExecuteFunc = reference to procedure;
TMotileThreading = class
protected
constructor Create;
public
class procedure Execute (Func : TExecuteFunc);
class procedure ExecuteAndCall (Func : TExecuteFunc; OnTerminateFunc : TExecuteFunc;
SyncTerminateFunc : Boolean = False);
end;
TMotile = class (TThread)
private
ExecFunc : TExecuteFunc;
TerminateHandler : TExecuteFunc;
SyncTerminateHandler : Boolean;
public
constructor Create (Func : TExecuteFunc); overload;
constructor Create (Func : TExecuteFunc; OnTerminateFunc : TExecuteFunc;
SyncTerminateFunc : Boolean); overload;
procedure OnTerminateHandler (Sender : TObject);
procedure Execute; override;
end;
implementation
constructor TMotileThreading.Create;
begin
Assert (False, 'Class TMotileThreading shouldn''t be used as an instance');
end;
class procedure TMotileThreading.Execute (Func : TExecuteFunc);
begin
TMotile.Create (Func);
end;
class procedure TMotileThreading.ExecuteAndCall (Func : TExecuteFunc;
OnTerminateFunc : TExecuteFunc;
SyncTerminateFunc : Boolean = False);
begin
TMotile.Create (Func, OnTerminateFunc, SyncTerminateFunc);
end;
constructor TMotile.Create (Func : TExecuteFunc);
begin
inherited Create (True);
ExecFunc := Func;
TerminateHandler := nil;
FreeOnTerminate := True;
Resume;
end;
constructor TMotile.Create (Func : TExecuteFunc; OnTerminateFunc : TExecuteFunc;
SyncTerminateFunc : Boolean);
begin
inherited Create (True);
ExecFunc := Func;
TerminateHandler := OnTerminateFunc;
SyncTerminateHandler := SyncTerminateFunc;
OnTerminate := OnTerminateHandler;
FreeOnTerminate := True;
Resume;
end;
procedure TMotile.Execute;
begin
ExecFunc;
end;
procedure TMotile.OnTerminateHandler (Sender : TObject);
begin
if Assigned (TerminateHandler) then
if SyncTerminateHandler then
Synchronize (procedure
begin
TerminateHandler;
end)
else
TerminateHandler;
end;
You're on the right track. Indy is intended to be used like that. It uses blocking sockets, so the ReadBytes call doesn't return until it's read what you've asked for. Contrast that with non-blocking sockets, where a call may return early, so you either poll or get notified asynchronously to determine when a request has been filled.
Indy is designed with the expectation that the socket objects have their own threads (or fibers). Indy comes with TIdAntifreeze for the folks who want to drag and drop socket components onto their forms and data modules and use the Indy components from the main GUI thread, but that's not generally a good idea if you can avoid it.
Since your thread cannot work without FSocket being assigned, I advise you to simply receive that value in the class's constructor. Assert in the constructor if it's not assigned. Furthermore, it is an error to create your thread non-suspended, so why even give the option? (If the thread is not created suspended, then it will start running, check whether FSocket is assigned, and fail because the creating thread hasn't gotten to assign that field yet.)