I am looking at the source code for CodeGear C++ Builder header files. In Source/vcl/forms.pas I find the following lines of code:
procedure TApplication.CreateForm(InstanceClass: TComponentClass; var Reference);
var
Instance: TComponent;
begin
Instance := TComponent(InstanceClass.NewInstance); // 메타클래스 이용하여, 인스턴스화
TComponent(Reference) := Instance;
try
Instance.Create(Self); // 폼 생성
except
TComponent(Reference) := nil;
raise;
end;
if (FMainForm = nil) and (Instance is TForm) then
begin
TForm(Instance).HandleNeeded; // 윈도우핸들을 생성
FMainForm := TForm(Instance);
end;
end;
Contextually, what I think is happening is that this procedure creates an instance of type InstanceClass and returns that instance through Reference. In my call, InstanceClass is not TForm, so the second half doesn't matter.
I am confused by TComponent(Reference) := Instance;. Syntactically, what is happening here? Is this assignment by reference? Is this assigning a new TComponent with the instantiator argument Reference being assigned the value? Is TComponent() a type casting?
In the procedure’s signature the formal parameter reference does not indicate a data type, but is declared as a variable parameter.
Typeless parameters are not legal in Pascal, but permitted in dialects such as GNU Pascal and FreePascal.
There, such variable parameters accept actual parameters of any data type.
The compiler will not enforce/restrict permissible data types, but it must be addressable (i. e. literals are not permitted).
dataTypeName(expression) is indeed a typecast (also illegal in Pascal, but allowed by some dialects).
Specifically, dataTypeName(variableName) is a variable typecast.
It will be treated as if variableName was of the named data type.
This is necessary, because in this particular case reference has no associated data type.
No associated data type means, there is no agreed rule in how to access the variable in question (i. e. any read/write access is impossible).
The procedure probably creates an instance of TComponentClass, i. e. the data type of the parameter InstanceClass, but there you should really read the documentation of the NewInstance method.
I can only tell you it’s a descendant of TComponent otherwise it hardly makes sense to typecast to an unrelated class.
The Reference parameter is an untyped var parameter. In C++ terms, it is roughly equivalent to void*, ie the address of any variable can be bound to it. The code is type-casting the Reference to the equivalent of TComponent*& or TComponent** and then assigning the Instance variable (a TComponent* pointer) to the caller's passed variable.
The code is roughly equivalent to the following in C++ (except that metaclasses and virtual constructors don't exist in C++, so this code is actually not usable in C++):
void __fastcall TApplication::CreateForm(TComponentClass* InstanceClass, void* Reference)
{
// this just allocates a block of memory of the required size...
TComponent* Instance = static_cast<TComponent*>(InstanceClass->NewInstance());
// *static_cast<TComponent**>(Reference) = Instance;
reinterpret_cast<TComponent*&>(Reference) = Instance;
try
{
// This is calling the class' virtual constructor
// on the allocated memory. In C++, this would be
// similar to calling 'placement-new' if the class
// type was known statically here...
// new(Instance) InstanceClass->ClassType()(this);
Instance->Create(this);
}
catch (...)
{
// *static_cast<TComponent**>(Reference) = NULL;
reinterpret_cast<TComponent*&>(Reference) = NULL;
throw;
}
if ((!FMainForm) && (dynamic_cast<TForm*>(Instance) != NULL))
{
static_cast<TForm*>(Instance)->HandleNeeded();
FMainForm = static_cast<TForm*>(Instance);
}
}
// Application.CreateForm(TForm, Form1);
Application->CreateForm(__classid(TForm), reinterpret_cast<void*>(&Form1));
Related
Is there a reference to a class property in C++Builder, analogous to a regular reference in C++? To understand what I mean, I will give the code (so far this is my solution to the problem):
void change(TControl* object) {
struct TAccessor : TControl { __property Text; };
static_cast<TAccessor*>(object)->Text = L"some text";
}
This function allows you to change the Text property of any object inherited from TControl.
But maybe there is a more elegant solution to this problem?
Your approach will update the Text of any TControl, even if it doesn't actually expose access to Text (which is declared protected in TControl itself, derived classes decide whether to promote it to public/__published as needed).
To account for that fact, you would have to use RTTI to discover if Text is accessible or not. You can also use RTTI to set the property value, without resorting to the Accessor trick.
For example, old-style RTTI (via the <TypInfo.hpp> header) works only with __published properties, nothing else, eg:
#include <TypInfo.hpp>
void change(TControl* object) {
if (IsPublishedProp(object, _D("Text"))
SetStrProp(object, _D("Text"), _D("some text"));
}
Alternatively:
#include <TypInfo.hpp>
void change(TControl* object) {
PPropInfo prop = GetPropInfo(object, _D("Text"), TTypeKinds() << tkUString);
if (prop)
SetStrProp(object, prop, _D("some text"));
}
Whereas newer-style Extended RTTI (via the <Rtti.hpp> header) supports fields, methods, and properties, and all the supported member visibilities, eg:
#include <Rtti.hpp>
typedef Set<TMemberVisibility, mvPrivate, mvPublished> TMemberVisibilitySet;
void change(TControl* object) {
static const TMemberVisibilitySet WantedVisibilities = TMemberVisibilitySet() << mvPublic << mvPublished;
TRttiContext ctx;
TRttiType *type = ctx.GetType(object->ClassType());
TRttiProperty* prop = type->GetProperty(_D("Text"));
if ((prop) && (WantedVisibilities.Contains(prop->Visibility)) && (prop->IsWritable))
prop->SetValue(object, _D("some text"));
}
If I am using OleVariant instead of VARIANTARG to store VT_BSTR (VOleStr in OleVariant), do I still need to call SafeSysFreeString when I am done with the string, or is it called automatically by OleVariant, when it goes out of scope?
For example:
{
WB->Navigate("https://www.example.com");
while (WB->ReadyState != Shdocvw::READYSTATE_COMPLETE) Application->ProcessMessages();
DelphiInterface<IOleCommandTarget> pOleCmdTarget;
WB->Document->QueryInterface(IID_IOleCommandTarget, (void**)&pOleCmdTarget);
OleVariant v;
if (pOleCmdTarget->Exec(&CMDSETID_Forms3, IDM_FONTNAME, Shdocvw::MSOCMDEXECOPT_DONTPROMPTUSER, NULL, v)==S_OK)
{
// utilize v.VOleStr here...
}
} // Does OleVariant auto-deallocate (SafeSysFreeString) here?
OleVariant will automatically free its data's memory for you when it goes out of scope.
The following Delphi program calls method upon nil reference and runs fine.
program Project1;
{$APPTYPE CONSOLE}
type
TX = class
function Str: string;
end;
function TX.Str: string;
begin
if Self = nil then begin
Result := 'nil'
end else begin
Result := 'not nil'
end;
end;
begin
Writeln(TX(nil).Str);
Readln;
end.
However, in a structurally similar C# program, System.NullReferenceException will be raised, which seems to be the right thing to do.
namespace ConsoleApplication1
{
class TX
{
public string Str()
{
if (this == null) { return "null"; }
return "not null";
}
}
class Program
{
static void Main(string[] args)
{
System.Console.WriteLine(((TX)null).Str());
System.Console.ReadLine();
}
}
}
Because TObject.Free uses such style, it seems to be "supported" to call method on nil reference in Delphi. Is this true ? (Let's suppose that in the if Self = nil branch, no instance field will be accessed.)
It is reasonable to call a method on a nil reference, subject to the following rules:
The method must not be virtual or dynamic. That is because virtual or dynamic methods are bound using the runtime type of the reference. And if the reference is nil then there is no runtime type. By way of contrast, non-virtual, non-dynamic methods are bound at compile time.
You are allowed to read the value of Self, for instance to compare it against nil.
In case Self is nil, then you must not refer to any instance variables.
To send data to multiple clients I create a TIdThreadSafeStringList in OnConnect and assign it to AContext->Data like so
AContext->Data = new TIdThreadSafeStringList
When the client disconnects, its stringlist is deleted in OnDisconnect like so
delete AContext->Data
However this results in an AV. If I zero the parameter, like so
delete AContext->Data
AContext->Data = NULL
the AV goes away. Is there some auto cleanup I'm not aware of?
Using C++ Builder 10.2.3.
Is there some auto cleanup I'm not aware of?
Yes. TIdContext derives from TIdTask, which owns the Data property. The TIdTask destructor is called after the OnDisconnect event and will free the Data object if it is not NULL.
Another (preferred) way to handle this situation is to instead derive a new class from TIdServerContext and add your TIdThreadSafeStringList to that class (and any other per-client custom functionality you want), eg:
class TMyContext : public TIdServerContext
{
public:
TIdThreadSafeStringList *MyList;
__fastcall TMyContext(TIdTCPConnection *AConnection, TIdYarn *AYarn, TIdContextThreadList *AList = NULL)
: TIdServerContext(AConnection, AYarn, AList)
{
MyList = new TIdThreadSafeStringList;
}
__fastcall ~TMyContext()
{
delete MyList;
}
//...
};
Then assign your class type to the server's ContextClass property at runtime before activating the server, eg:
__fastcall TMyForm::TMyForm(TComponent *Owner)
: TForm(Owner)
{
IdTCPServer1->ContextClass = __classid(TMyContext);
//...
}
Then, you can simply type-cast any TIdContext* pointer belonging to the server to your class type in order to access the TIdThreadSafeStringList (or other functionality):
static_cast<TMyContext*>(SomeIdContext)->MyList->...
This way, you can ignore the TIdContext::Data property altogether, or use it for other purposes, if desired.
I have this (defined in delphi source code):
TCanvasTextureMaterial = class(TCustomMaterial)
private
[Weak] FTexture: TTexture;
procedure SetTexture(const Value: TTexture);
protected
public
property Texture: TTexture read FTexture write SetTexture;
end;
how to override the setter SetTexture in descendant class?
In short, you can't.
The setter is declared as private and thus is not accessible to descendants. But even if it were accessible, it is not declared as virtual so it can't be override'n anyway.
The only thing a descendant could do in this case is (re)declare its own property with its own setter (it could even reuse the same Texture property name), eg:
type
TMyCanvasTextureMaterial = class(TCanvasTextureMaterial)
private
function GetMyTexture: TTexture;
procedure SetMyTexture(const Value: TTexture);
public
property Texture: TTexture read GetTexture write SetMyTexture;
end;
But, accessing the Texture property via a TCanvasTextureMaterial pointer will not call the descendant's setter, even if it does use the same property name:
var
TM: TCanvasTextureMaterial;
begin
TM := ...; // any TMyCanvasTextureMaterial object
// reading from TM.Texture returns TCanvasTextureMaterial.FTexture,
// it does not call TMyCanvasTextureMaterial.GetMyTexture()
// assigning to TM.Texture calls TCanvasTextureMaterial.SetTexture(),
// it does not call TMyCanvasTextureMaterial.SetMyTexture()
end;