Assigning events to a VCL control created dynamically at runtime (2) - c++builder

This is related to Assigning events to a VCL control created dynamically at runtime.
I used the above listed/reference post to solve a problem that I had with some code. Thank you for providing that example. I found it very useful, and implemented the "alternative" way that it provided as I was unable to make the first way work.
I am using C++Builder 10.x from Embarcadero. I just updated to C++Builder 10.3. This new update is now throwing a warning:
[bcc32c Warning] LogitToMemo.cpp(196): implicit conversion between pointer-to-function and pointer-to-object is a Microsoft extension
The line it is throwing on is:
Method.Code = &LogLevelComboBoxOnChange;
I am not sure how to "fix" this.
The code is in support of a logging function to a memo field, where the page for the logging memo has a TComboBox to select the logging level/verbosity.
The TComboBox is external to the logging function, as it is on the user's form. I want the TComboBox::OnChange event to call my LogLevelComboBoxOnChange function, which adjusts the logging level based on the TComboBox item/entry selected.
Supporting code around this includes.
Function declaration - TComboBox::OnChange Event Function
void __fastcall LogLevelComboBoxOnChange(void *pThis, TObject *Sender);
Function Declaration - Logging start up where the TMemo field to log to & the TComboBox are provided
int LogStartWithComboBox(TMemo *LogIt_MemoField, TComboBox *AppLogLevelComboBox, int iThreshold, AnsiString &asFieldTitles);
This is the function that assigns the OnChange function to the TComboBox object on the user's logging form.
int LogStartWithComboBox(TMemo *LogIt_MemoField, TComboBox *AppLogLevelComboBox, int iThreshold, AnsiString &asFieldTitles)
{
static TMethod Method;
//
// Set-Up CombBox and OnChange Event
// - Save ComboBox pointer
// - Assign List of Log Levels
// - Assign/Set-Up OnChange Function
//
LogLevelComboBox = AppLogLevelComboBox;
AppLogLevelComboBox->Items->Text =
"Off\n"
"All Messages\n"
"Verbose\n"
"Trace\n"
"Informational\n"
"Warning\n"
"Error\n"
"Severe\n"
"Fatal";
AppLogLevelComboBox->ItemIndex = iThreshold + 1;
//
// Set-Up - On Change Function for "external" Log Level Combo-Box
//
Method.Data = NULL; // passed to the pThis parameter, can be whatever you want
//
// The Following line generates the warning
// [bcc32c Warning] LogitToMemo.cpp(196): implicit conversion between pointer-to-function and pointer-to-object is a Microsoft extension
//
Method.Code = &LogLevelComboBoxOnChange;
LogLevelComboBox->OnChange = reinterpret_cast<TNotifyEvent&>(Method);
return 0;
}

You are using C++Builder's CLang-based C++ compiler. The "implicit conversion" warning you are seeing is a CLang issue that has cropped up during the past few years in multiple toolchains that use CLang, not just in C++Builder. This issue does not affect C++Builder's "classic" (non-CLang) compiler.
&LogLevelComboBoxOnChange creates a pointer-to-function, which CLang does not like to implicitly convert to void* (which is what the TMethod::Code field is declared as) unless Clang is put into a Microsoft compatibility mode.
Possible solutions are to either:
type-cast the pointer explicitly:
(UPDATE: apparently CLang doesn't like static_casting a pointer-to-function to void*, either!)
Method.Code = static_cast<void*>(&LogLevelComboBoxOnChange);
Method.Code = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(&LogLevelComboBoxOnChange));
enable the -Wmicrosoft-cast flag in the CLang compiler settings.
disable the warning in your code by using #pragma statements around the assignment to Method.Code:
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wmicrosoft-cast"
Method.Code = &LogLevelComboBoxOnChange;
#pragma clang diagnostic pop
use a union (though, I'm sure some people will argue that this probably violates Strict Aliasing rules, but if it works...):
union {
void (__fastcall *func)(void*, TObject*);
void *ptr;
} u;
u.func = &LogLevelComboBoxOnChange;
Method.Code = u.ptr;

Related

type TMessage is not a defined class with virtual function C++ builder sample

Im trying to use embarcadero sample on useing android camera and geting an error:
"type TMessage is not a defined class with virtual function" on lines:
void __fastcall TForm1::DoMessageListener(const TObject *Sender, TMessage const *M) {
TMessageDidFinishTakingImageFromLibrary const *v = dynamic_cast<TMessageDidFinishTakingImageFromLibrary const *>(M);
if (v) {
Image1->Bitmap->Assign(v->Value);
}
}
In Delphi, TMessage works fine, but in C++Builder you must use TMessageBase instead:
void __fastcall TForm1::DoMessageListener(const TObject *Sender, TMessageBase const *M)
This is clearly stated in the documentation:
Sending and Receiving Messages Using the RTL:
The RTL only defines one type of message, TMessage. It is actually a template that you can use to create messages for specific types of values; for example: TMessage<int> or TMessage<UnicodeString>. You can also subclass TMessage to define your own message types or, if you are using FireMonkey, you can reuse the message types defined by the framework.
Note: For C++ projects use TMessageBase instead of TMessage.
System.Messaging.TMessage
TMessage represents the base class used for message purposes. It can be inherited in order to send custom messages.
Warning: For C++ projects, use TMessageBase instead.
System.Messaging.TMessageBase
Alias to System.Messaging.TMessage.
Use System.Messaging.TMessageBase for C++ projects instead of System.Messaging.TMessage.
This use of TMessageBase is also demonstrated in the documentation's System.Messaging (C++) example.

SHA-512 returns NULL even using 2 DLLs (ssleay32.dll and libeay32.dll)

My Environment:
C++ Builder XE4
using VCL component
Indy 10.6.0.4975
I was studying to use MD5, SHA-1, and SHA-2s.
Unit1.cpp
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop
#include <IdHashSHA.hpp> // SHA-1, SHA-2
#include <IdHashMessageDigest.hpp> // for MD5
#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
Memo1->Lines->Clear();
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
String msg;
msg = L"Hello, world";
String hash;
// 1. MD5
TIdHashMessageDigest5 *md5;
md5 = new TIdHashMessageDigest5();
//
hash = md5->HashStringAsHex(msg, IndyTextEncoding(TEncoding::ASCII)).LowerCase();
Memo1->Lines->Add(L"MD5: " + hash);
delete md5;
// 2. SHA-1
TIdHashSHA1 *sha1;
sha1 = new TIdHashSHA1();
//
hash = sha1->HashStringAsHex(msg, IndyTextEncoding(TEncoding::ASCII)).LowerCase();
Memo1->Lines->Add(L"SHA-1:" + hash);
delete sha1;
// 3. SHA-2 (SHA-512)
TIdHashSHA512 *sha512;
sha512 = new TIdHashSHA512();
//
hash = sha512->HashStringAsHex(msg, IndyTextEncoding(TEncoding::ASCII)).LowerCase();
Memo1->Lines->Add(L"SHA-512:" + hash);
delete sha512;
}
//---------------------------------------------------------------------------
The result is as follows.
Then, I found the following:
TidHashSHA512.isavailable is false on Windows 10
According to the suggestion, I add two files to where the .exe file exists:
ssleay32.dll
libeay32.dll
Still, the SHA-512 returns NULL.
What I am missing?
10.6.0.4975 is a VERY old version of Indy 10. The current version is 10.6.2.5485. You need to upgrade.
In any case, Indy 10 has native implementations of MD5 and SHA-1, those do not rely on any external hashing library at all. But SHA-512 does. Yet, you are not telling Indy which hashing library to use, such as OpenSSL. You are not instructing Indy to load the OpenSSL DLLs so it can initialize itself to use OpenSSL's SHA-512 functionality. As such, sha512->IsAvailable returns false, and sha512->HashStringAsHex() returns a blank string 1.
This is clearly stated in the accepted answer to the question you linked to:
Indy provides an implementation that uses hashing functions from OpenSSL. To use it, you can either:
add the IdSSLOpenSSLHeaders unit to your uses clause, and then call its Load() function at runtime.
add the IdSSLOpenSSL unit to your uses clause, and then call its LoadOpenSSLLibrary() function at runtime.
In this case, since you are using C++ instead of Pascal, you need to add the corresponding #include statement to your code instead, either #include <IdSSLOpenSSLHeaders.hpp> or #include <IdSSLOpenSSL.hpp>, and then you can call the relevant Load function, such as in your Form's constructor.
1: BTW, you should be using IndyTextEncoding_ASCII() instead of IndyTextEncoding(TEncoding::ASCII).

'No matching constructor for initialization' Rad Studio 10 Clang Compiler

I have a snippet of code which compiles in C++ Builder XE8 using the classic BCC compiler. However, in Rad Studio 10 Seattle using the Clang compiler I get the error
'no matching constructor found for initialization of TChoiceItem'
Here is the snippet of code which causes the error.
LISTITEM_BEGIN( sch_TYPE_Choice )
LISTITEM_DATA( sch_TYPE_Daily, "Daily" )
LISTITEM_DATA( sch_TYPE_Weekly, "Weekly" )
LISTITEM_DATA( sch_TYPE_Monthly, "Monthly" )
LISTITEM_END()
Here is the code which defines TChoiceItem
//------------------------------------------------------------------------------
#define LISTITEM_BEGIN( Name ) TChoiceItem Name[] = {
//------------------------------------------------------------------------------
#define INT_LISTITEM_BEGIN( Name ) TIntChoiceItem Name[] = {
//------------------------------------------------------------------------------
#define LISTITEM_DATA( XCode, XText ) { XCode, 0, (char*)XText, 0 },
#define LISTITEM_DATA_NC( XShortText, XText ) { 0, (char*)XShortText, (char*)XText, 0 },
#define LISTITEM_DATA_EX( XCode, XShortText, XText ) { XCode, (char*)XShortText, (char*)XText, 0 },
#define LISTITEM_DATA_EX2( XCode, XShortText, XText, XDesc ) { XCode, (char*)XShortText, (char*)XText, (char*)XDesc },
#define LISTITEM_END() LISTITEM_DATA(0,0) };
I am fairly new to C++ so I am not exactly sure what to call the above method of defining a class/method.
Is this some sort of dated language feature not supported by the Clang compiler? Is there a way to modify the code or definition so the compiler will accept it?
Edit:
I found the actual declaration of the TChoiceItem class.
class TChoiceItem : public TChoiceBase
{
public:
char Code;
char *ShortText;
char *Text;
char *Desc;
};
It does't appear to have any sort of standard constructor at all. But somehow, everything still compiles and works with the classic BCC compiler.
Edit 2:
I found this question which looks to be describing a similar issue. Could it be that I need to include some kind of compiler flag when compiling the code? If so can I add a flag somehow in the embarcadero project compiler settings?
Using a list of values in braces to initialize the individual members of a class or struct is known as aggregate initialization.
As explained on cppreference.com, aggregate initialization isn't permitted if the class has a base class (among other restrictions). TChoiceItem inherits from TChoiceBase, so aggregate initialization isn't allowed (and the "classic" bcc32 compiler shouldn't have allowed it).
You have a couple of choices:
First, you can change the code to not inherit from TChoiceBase.
Second, you can define a constructor:
TChoiceItem(char code, char *short_text, char *text, char *desc)
: Code(code), ShortText(short_text), Text(text), Desc(desc) {}
C++11's uniform initialization means that your macros' syntax doesn't have to change: instead of braces meaning a list of values for individual members, the braces will mean a list of parameters to the constructor, but the result will be the same.

with or without & for calling Synchronize() in C++ builder

My Environment:
OS - Windows7 Pro(32bit)
IDE - RadStudio XE2 Update4
I am wondering about Synchronize() function.
The Synchronize() function is used in the thread program. About using the Synchronize() in C+ builder, the example is as follows ( as can be seen in here)
// Important: Methods and properties of objects in VCL can only be
// used in a method called using Synchronize, for example:
//
// Synchronize(&UpdateCaption);
//
// where UpdateCaption could look like:
//
// void __fastcall TMyThreadClass::UpdateCaption()
// {
// Form1->Caption = "Updated in a thread";
// }
What I am confused is that , in some older version of C++ builder (e.g. bcb6),
the Synchronize() was used
// Synchronize(UpdateCaption);
without "&" before the function name;
Also in the delphi code as in here,
there is no "&" before the function name;
// Synchronize(UpdateCaption);
So, which is the correct way to use Synchronize() in C++ builder
// Synchronize(UpdateCaption);
or
// Synchronize(&UpdateCaption);
I tried both in the actual code, but seems identical in the working manner.
Do both UpdateCaption and &UpdateCaption return address of functions?
So, which is the correct way to use Synchronize() in C++ builder
// Synchronize(UpdateCaption);
or
// Synchronize(&UpdateCaption);
They both work, but & is preferred.
Do both UpdateCaption and &UpdateCaption return address of functions?
Yes. If you refer to a function/method without specifying parenthesis for the parameter list, the address of the function/method is assumed. The & just makes it more explicit.

How do I construct this Delphi class in C++Builder?

What is the C++ equivalent of this code
ImageEnView1.IEBitmap.VirtualBitmapProvider := TIESlippyMap.Create();
I get a compile error
[bcc32 Error] Unit1.cpp(12907): E2285 Could not find a match for 'TIESlippyMap::TIESlippyMap()'
on my code
ImageEnview1->IEBitmap->VirtualBitmapProvider = new TIESlippyMap();
ImageEnView1->IEBitmap->VirtualBitmapProvider = new TIESlippyMap();
Update: You are trying to call this constructor:
constructor Create(provider:TIESlippyMapProvider = iesmpMapQuest; const cachePath:string = '');
The compiler error you are getting means that the C++ compiler cannot find a constructor that has no parameters, or at least a constructor with parameters that all have default values assigned to them. Depending on which C++Builder version you are using, it is likely that the Delphi compiler included with it is not emitting the default parameter values when generating the C++ .hpp file for the class. Older Delphi compiler versions did not do that correctly, but newer versions do. In which case, it sounds like you are using an affected version, so you will have to fill in those parameter values explicitly:
ImageEnView1->IEBitmap->VirtualBitmapProvider = new TIESlippyMap(iesmpMapQuest, "");
Or else edit the .hpp file to include the default values correctly:
class TIESlippyMap : public ...
{
...
public:
__fastcall TIESlippyMap(TIESlippyMapProvider provider = iesmpMapQuest, const String cachePath = "");
...
};

Resources