如何定义与将 Lambda 与捕获作为回调一起使用兼容的函数指针
how to define function pointer compatible with using lambda with capture as call back
需要与MFC项目进行简单模式对话框的几种变体,我编写了一个简单的类CDialogDlg
,它扩展了标准MFCCDialog
类,并包含用于实现特定回调的钩子,这些回调通常作为扩展CDialog
类中的方法实现。这些回调(如果提供(在处理某些事件(如对话框初始化、OK 按钮处理和DoDataExchange()
函数(时使用,该函数提供用于连接对话框类变量和实际对话框控件的方法。
我的第一个版本对回调使用了标准函数。所以扩展类的回调设置方法设置了一个带有标准函数地址的函数指针。
void SetDataExchangeCallBack(void(*f)(CDataExchange* pDX)) { funcDX = f; };
void SetInitCallBack(void(*f)(CWnd *dlgWnd)) { funcInit = f; }
void SetOnOkCallBack(void(*f)(CWnd *dlgWnd, CDocument *pDoc)) { funcOK = f; }
然后我意识到我应该能够使用 lambda 而不是标准函数。lambda 的第一个版本使用与标准函数相同的参数,它没有捕获任何编译良好的变量,并且可以与扩展类中的现有方法和函数指针配合良好。
但是,当我尝试在回调 setter 方法中指定的 lambda 中捕获变量时,我遇到了编译错误。
回顾将捕获 lambda 作为函数指针传递,并将捕获C++ lambda 作为函数指针,我得到尝试使用捕获进行 lambda 不会使用我正在使用的函数指针和回调设置函数的定义进行编译。
因此,我决定应该在类中添加一个额外的 lambda 特定函数指针,以及使用一个额外的方法覆盖现有方法,使用类中的 lambda 特定函数指针设置回调。
问:函数中的函数指针定义和参数定义应该是什么样子的,该函数设置了接受具有捕获变量的 lambda 的回调?我想捕获方法CPCSampleView::OnDisplayReportList ()
中的局部变量CPCSampleDoc *pDoc
。此变量包含一个指向我想要捕获的视图的CDocument
派生对象的指针,而不是使用存储它的方法并执行static_cast
以将其恢复到当前版本的 lambda 中,而这些 lambda 未捕获。
源代码
扩展CDialog
的CDialogDlg
类如下所示:
class CDialogDlg : public CDialog
{
// Construction
void(*funcDX)(CDataExchange* pDX);
void(*funcDXpDoc)(CDataExchange* pDX, CDocument *pDoc);
void(*funcInit)(CWnd *dlgWnd);
void(*funcOK)(CWnd *dlgWnd, CDocument *pDoc);
CDocument *m_pDoc;
public:
CDialogDlg(UINT nIDTemplate, CWnd* pParentWnd = NULL, void(*f)(CDataExchange* pDX) = NULL) : CDialog(nIDTemplate, pParentWnd) { funcDX = f; funcInit = NULL; }
CDialogDlg(UINT nIDTemplate, CDocument *pDoc, CWnd* pParentWnd = NULL, void(*f)(CDataExchange* pDX, CDocument *pDoc) = NULL) : CDialog(nIDTemplate, pParentWnd) {
funcDX = NULL; funcDXpDoc = f; funcInit = NULL; m_pDoc = pDoc;
}
void SetDataExchangeCallBack(void(*f)(CDataExchange* pDX)) { funcDX = f; };
void SetInitCallBack(void(*f)(CWnd *dlgWnd)) { funcInit = f; }
void SetOnOkCallBack(void(*f)(CWnd *dlgWnd, CDocument *pDoc)) { funcOK = f; }
// Dialog Data
//{{AFX_DATA(CCashierNoDlg)
//}}AFX_DATA
// Overrides
// ClassWizard generated virtual function overrides
//{{AFX_VIRTUAL(CCashierNoDlg)
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
//}}AFX_VIRTUAL
protected:
// Generated message map functions
//{{AFX_MSG(CCashierNoDlg)
virtual BOOL OnInitDialog();
virtual void OnOK();
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
};
BEGIN_MESSAGE_MAP(CDialogDlg, CDialog)
//{{AFX_MSG_MAP(CCashierNoDlg)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
void CDialogDlg::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
if (funcDX) funcDX(pDX);
if (funcDXpDoc) funcDXpDoc(pDX, m_pDoc);
//{{AFX_DATA_MAP(CCashierNoDlg)
//}}AFX_DATA_MAP
}
BOOL CDialogDlg::OnInitDialog()
{
CDialog::OnInitDialog();
if (funcInit) funcInit(this);
return TRUE; // return TRUE unless you set the focus to a control
// EXCEPTION: OCX Property Pages should return FALSE
}
void CDialogDlg::OnOK()
{
if (funcOK) funcOK(this, m_pDoc);
CDialog::OnOK();
}
由菜单选择触发的CView
方法显示一个对话框,其中包含可供选择的项目列表。显示的对话框是具有特定对话框模板 ID 的CDialogDlg
对象。对于特殊处理,我正在使用两个不同的回调,它们当前使用的是无法捕获的 lambda。结果如下:
void CPCSampleView::OnDisplayReportList ()
{
CPCSampleDoc *pDoc = GetDocument();
CDialogDlg myDialog(IDD_DIALOG_REPORTLIST, pDoc, this, [](CDataExchange* pDX, CDocument *pDoc) {
if (pDX->m_bSaveAndValidate) {
}
else {
CPCSampleDoc *pDocDoc = static_cast<CPCSampleDoc *>(pDoc);
POSITION pos = NULL;
do {
CPCSampleDoc::ListReportList sectionHeader;
CListBox *x = static_cast<CListBox *>(pDX->m_pDlgWnd->GetDlgItem(IDC_LIST1));
pos = pDocDoc->GetReportSectionHeader(pos, sectionHeader);
x->AddString(sectionHeader.m_SectionTitle);
} while (pos);
}
});
myDialog.SetOnOkCallBack([](CWnd *dlgWnd, CDocument *pDoc) {
CPCSampleDoc *pDocDoc = static_cast<CPCSampleDoc *>(pDoc);
CListBox *x = static_cast<CListBox *>(dlgWnd->GetDlgItem(IDC_LIST1));
int iPtr = x->GetCurSel();
POSITION pos = NULL;
do {
CPCSampleDoc::ListReportList sectionHeader;
pos = pDocDoc->GetReportSectionHeader(pos, sectionHeader);
if (iPtr < 1) {
pDocDoc->MoveToReportSectionHeader(sectionHeader.m_ListOffset);
break;
}
iPtr--;
} while (pos);
});
myDialog.DoModal();
}
您可以使用std::function
来允许捕获 lambda 和其他函子以及常规函数指针:
void(*funcDX)(CDataExchange* pDX);
void(*funcDXpDoc)(CDataExchange* pDX, CDocument *pDoc);
void(*funcInit)(CWnd *dlgWnd);
void(*funcOK)(CWnd *dlgWnd, CDocument *pDoc);
成为
std::function<void(CDataExchange*)> funcDX;
std::function<void(CDataExchange*, CDocument*)> funcDXpDoc;
std::function<void(CWnd*)> funcInit;
std::function<void(CWnd*, CDocument*)> funcOK;
你 setter/constructor 也应该将指针函数更改为std::function
:
void SetInitCallBack(std::function<void(CWnd*)> f) { funcInit = f; }
否则,您的用法是相同的:
if (funcDX) funcDX(pDX);
或
funcInit = nullptr;
- 架构决策:返回std::future还是提供回调
- 正在为Xtensa simcall函数编写回调函数
- 如何在C++中使用非静态成员函数作为回调函数
- FLTK:按下哪个按钮 - 将数字传递给按钮的回调 (lambda)
- 在简单示例中,Python3 + ctypes 回调会导致内存泄漏
- 用于在回调中调用解析器的设计模式
- 如何使用C++对象的成员函数作为 C 样式回调?
- Java从C++回调到C++回调
- 如何将成员函数作为回调参数传递给需要"typedef-ed"自由函数指针的函数?
- 从不同的 cpp 调用回调函数会导致bad_function_call
- pcap_handler回调仅在使用 NPCAP v0.9991 时包含空数据包
- 不带轮询的 SDL2 事件回调
- C++存储带有可变参数的回调
- 如何使用 Node-addon-API 实现 node-nan 回调
- MSVC __debugbreak() 与 openGL 错误回调一起使用时不会产生调用堆栈
- 如何定义与将 Lambda 与捕获作为回调一起使用兼容的函数指针
- libcurl进度回调无法与Multi一起使用
- Boost Asio和Co_await-与任何第三方回调一起使用
- 将libuv与函数结构一起使用,而不是函数回调
- 将函子/lambdas与C回调一起使用