Assigning events to a VCL control created dynamica

2019-07-25 08:08发布

问题:

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

回答1:

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;