将事件分配给在运行时动态创建的 VCL 控件 (2)
Posted
技术标签:
【中文标题】将事件分配给在运行时动态创建的 VCL 控件 (2)【英文标题】:Assigning events to a VCL control created dynamically at runtime (2) 【发布时间】:2019-01-23 15:36:07 【问题描述】:这与Assigning events to a VCL control created dynamically at runtime有关。
我使用上面列出的/参考帖子来解决我在使用某些代码时遇到的问题。感谢您提供该示例。我发现它非常有用,并实现了它提供的“替代”方式,因为我无法使第一种方式工作。
我正在使用 Embarcadero 的 C++Builder 10.x。我刚刚更新到 C++Builder 10.3。此新更新现在发出警告:
[bcc32c 警告] LogitToMemo.cpp(196):函数指针和对象指针之间的隐式转换是 Microsoft 扩展
它抛出的线是:
Method.Code = &LogLevelComboBoxOnChange;
我不确定如何“解决”这个问题。
该代码支持对备忘录字段的日志记录功能,其中日志记录备忘录的页面具有TComboBox
以选择日志记录级别/详细程度。
TComboBox
在日志功能之外,因为它位于用户表单上。我希望TComboBox::OnChange
事件调用我的LogLevelComboBoxOnChange
函数,该函数根据所选的TComboBox
项目/条目调整日志记录级别。
支持这方面的代码包括。
函数声明-TComboBox::OnChange
事件函数
void __fastcall LogLevelComboBoxOnChange(void *pThis, TObject *Sender);
函数声明 - 记录在提供 TMemo
记录到的字段和 TComboBox
的位置开始
int LogStartWithComboBox(TMemo *LogIt_MemoField, TComboBox *AppLogLevelComboBox, int iThreshold, AnsiString &asFieldTitles);
这是将OnChange
函数分配给用户日志记录表单上的TComboBox
对象的函数。
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;
【问题讨论】:
“实现了它提供的“替代”方式,因为我无法使第一种方式工作” - 为什么不呢?您对“第一种”方式到底有什么问题?如果使用得当,这两种方式都可以正常工作。 您无需将TMethod
变量声明为static
,因为一旦分配了OnChange
事件,就无需保留它。为什么在 Unicode 环境中使用(并忽略)AnsiString
?您应该改用String
(或直接使用UnicodeString
)。我假设您打算改用AppLogLevelComboBox->Items->Text = asFieldTitles;
?
【参考方案1】:
您正在使用 C++Builder 的基于 CLang 的 C++ 编译器。您看到的“隐式转换”警告是过去几年在多个使用 CLang 的工具链中出现的 CLang 问题,而不仅仅是在 C++Builder 中。此问题不影响 C++Builder 的“经典”(非 CLang)编译器。
&LogLevelComboBoxOnChange
创建一个指向函数的指针,除非将 Clang 置于 Microsoft 兼容模式,否则 CLang 不喜欢将其隐式转换为 void*
(这是 TMethod::Code
字段声明的内容)。
可能的解决方案是:
显式类型转换指针:
(更新: 显然 CLang doesn't like static_cast
ing 是指向函数的指针,指向 void*
,或者!)Method.Code = static_cast
Method.Code = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(&LogLevelComboBoxOnChange));
在 CLang 编译器设置中启用 -Wmicrosoft-cast
标志。
在对Method.Code
的赋值周围使用#pragma
语句禁用代码中的警告:
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wmicrosoft-cast"
Method.Code = &LogLevelComboBoxOnChange;
#pragma clang diagnostic pop
使用union
(不过,我敢肯定有些人会争辩说这可能违反了严格别名规则,但如果可行的话......):
union
void (__fastcall *func)(void*, TObject*);
void *ptr;
u;
u.func = &LogLevelComboBoxOnChange;
Method.Code = u.ptr;
【讨论】:
雷米,感谢您的快速回复。按照您的建议,我明确更改为类型转换指针 Method.Code = static_caststatic_cast
上的a different warning:“函数指针和对象指针之间的静态转换是 Microsoft 扩展我>”。显然 CLang 根本不喜欢将指向函数的指针分配给 void*
。因此,您只需要禁用警告(在您的情况下这只是一个警告,而不是错误)。或者尝试reinterpret_cast
而不是static_cast
:Method.Code = reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(&LogLevelComboBoxOnChange));
@Mike 或者,也许可以尝试使用union
来避免类型转换:union void (__fastcall *func)(void*, TObject*); void *ptr; u; u.func = &LogLevelComboBoxOnChange; Method.Code = u.ptr;
虽然有些人可能会争辩说这违反了严格的别名规则,但如果它有效......跨度>
以上是关于将事件分配给在运行时动态创建的 VCL 控件 (2)的主要内容,如果未能解决你的问题,请参考以下文章
动态地将id分配给在kotlin中单击按钮时创建的edittext [重复]