TIdTCPServer's TIdContext->Data odd behaviour - c++builder

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.

Related

Pascal - Assignment to Function Call

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));

"Property reference" of the class

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"));
}

How to serialize a reference to the managed peer of an Android Callable Wrapper in Xamarin

I have a native Android Activity that receives a callback interface as part of the Intent used to start it:
public interface ICallback : Serializable
{
void invoke(Result result);
}
I want to implement the callback in Xamarin as a lambda:
class CallbackWrapper : Java.Lang.Object, ICallback
{
private Action<Result> onInvoke;
public CallbackWrapper(Action<Result> onInvoke)
{
this.onInvoke = onInvoke;
}
public void Invoke(Result result)
{
this.onInvoke(result);
}
}
...
intent.PutExtra(CALLBACK_EXTRA, new CallbackWrapper(result => { ... }));
StartActivityForResult(intent);
The first problem is that when my callback gets deserialized from the intent bundle, I get the following exceptions:
System.NotSupportedException
Unable to activate instance of type CallbackWrapper from native handle 0xff...
System.MissingMethodException
No constructor found for CallbackWrapper::.ctor(System.IntPtr, Android.Runtime.JniHandleOwnership)
I add the constructor as explained in the exception:
class CallbackWrapper : Java.Lang.Object, ICallback
{
public CallbackWrapper(IntPtr handle, JniHandleOwnership transfer) : base(handle, transfer)
{
}
...
}
The exception is fixed, but now when the activity calls my handler, the onInvoke field is null. How do I get a reference to the onInvoke delegate that was used to create the Intent?
The solution - serialize a handle to the original object.
The first step is to enable object serialization. Serialization in Java is done using specially-named private methods, instead of through interface methods. Xamarin allows you to inject these methods into the generated Android callable wrappers using the Java.Interop.ExportAttribute attribute:
using Java.Interop;
class CallbackWrapper : Java.Lang.Object, ICallback
{
...
[Export("readObject", Throws = new[] { typeof(Java.IO.IOException), typeof(Java.Lang.ClassNotFoundException) })]
private void ReadObject(Java.IO.ObjectInputStream source)
{
}
[Export("writeObject", Throws = new[] { typeof(Java.IO.IOException), typeof(Java.Lang.ClassNotFoundException) })]
private void WriteObject(Java.IO.ObjectOutputStream destination)
{
}
}
Even if an ACW implements Serializable, the ACW itself has no useful fields - that why you need to serialize the managed state through the readObject/writeObject method pair.
Note that for this to work, your project needs to reference the Mono.Android.Export assembly, otherwise you'll get a build-time error.
The second part is getting a serializable reference to CallbackWrapper. This can be achieved using System.Runtime.InteropServices.GCHandle. The first step is to create a handle to the object and write it during serialization:
[Export("writeObject", Throws = new[] { typeof(Java.IO.IOException), typeof(Java.Lang.ClassNotFoundException) })]
private void WriteObject(Java.IO.ObjectOutputStream destination)
{
var handle = GCHandle.Alloc(this);
destination.WriteLong(GCHandle.ToIntPtr(handle).ToInt64());
}
The second step is deserialization:
[Export("readObject", Throws = new[] { typeof(Java.IO.IOException), typeof(Java.Lang.ClassNotFoundException) })]
private void ReadObject(Java.IO.ObjectInputStream source)
{
// deserialize GCHandle from stream
var handle = GCHandle.FromIntPtr(new IntPtr(source.ReadLong()));
// convert handle to object
var trueSelf = handle.Target as NativeValidationHandler;
// copy fields from original callback
this.onInvoke = trueSelf.onInvoke;
// free this handle
handle.Free();
}
The handle doesn't need to be a pinned handle, because we don't ever access the object's address, we just use the handle.
Note that in the above implementation you can only deserialize a callback once, because deserialization will free the handle. Alternatively you can allocate the handle once in the constructor and provide a Dispose method that frees that handle, if you wish to be able to deserialize the handle multiple times. Freeing the handle during deserialization also means that the object will never be collected if it's never deserialized, because the handle will prevent the object from being collected.
If you want to use Serializable than you are right. but i would recommend you to use Parcelable, because
Parcelable is a part of Android sdk and it's mainly made for parcelling purpose.
Parcelable is faster than Serializable because it doesn't use reflection while later does.
Although there is demerit that it has some boilerplate code.
Worth to read => https://android.jlelse.eu/parcelable-vs-serializable-6a2556d51538

Bind TServerSocket to a specific IP address in C++ Builder (10.1.2)

I am still using deprecated TServerSocket component.
I would like to bind TServerSocket to a specific IP. This question has been previously asked before for Delphi in this site: How to Bind a TServerSocket to a specific IP address
However I couldn't make it work in C++ Builder.
My code is:
class ServerWrapper : private TServerSocket {
public:
ServerWrapper();
private:
};
ServerWrapper::ServerWrapper()
: TServerSocket (0)
{
//---
}
ServerWrapper* pServer =0;
//...
//..
//.
// And in a function:
pServer = new ServerWrapper;
pServer->Address = "192.168.0.1" ;
pServer->Active = true;
However it doesn't compile.
It says:
E2247 'TAbstractSocket::Address' is not accessible
I am using C++ Builder 10.1.2 Berlin by the way.
It doesn't work because you did not translate the Delphi code to C++ correctly.
For one thing, you are using private inheritance instead of public inheritance. So all public and protected members inherited from TServerSocket will be private in ServerWrapper. Delphi has no notion of protected/private inheritance, only public inheritance.
But more importantly, Delphi has a notion of implicit friendship. Within a unit, all classes have full access to private/protected members of other classes that are declared in the same unit. That includes inherited protected members. The Delphi example in the other question takes advantage of that feature, by declaring a local helper class to implicitly gain public access to the Address property for the unit that declares the helper.
But C++ has no notion of implicit friendship. To translate the Delphi example to C++, you have to explicitly promote access to the protected Address property.
A literal translation of the Delphi code would look like this in C++:
class TServerSocketAccess : public TServerSocket
{
public:
__property Address;
// or:
// using TServerSocket::Address;
};
((TServerSocketAccess*)ServerSocket1)->Address = "192.168.0.1";
ServerSocket1->Active = true;
Applied to your ServerWrapper class, it would look like this:
class ServerWrapper : public TServerSocket
{
public:
ServerWrapper();
__property Address;
// or:
// using TServerSocket::Address;
};
ServerWrapper::ServerWrapper()
: TServerSocket (0)
{
//---
}
ServerWrapper* pServer = 0;
//...
pServer = new ServerWrapper;
pServer->Address = "192.168.0.1";
pServer->Active = true;

add event to component in the run time

Hi any idea how to add event to the constructed button in the run time?
I understand how to build component in the run time, but adding event is another thing. Got any example to explain how it works?
Thanks
A VCL event is just a pointer to a class method for a particular object. You can assign that pointer directly, eg:
__fastcall TForm1::TForm1(TComponent *Owner)
: TForm(Owner)
{
TButton *btn = new TButton(this);
btn->Parent = this;
// set other properties as needed...
btn->OnClick = &ButtonClicked;
/*
behind the scenes, this is actually doing the equivalent of this:
TMethod m;
m.Data = this; // the value of the method's hidden 'this' parameter
m.Code = &TForm1::ButtonClicked; // the address of the method itself
btn->OnClick = reinterpret_cast<TNotifyEvent&>(m);
*/
}
void __fastcall TForm1::ButtonClicked(TObject *Sender)
{
// do something ...
}

Resources