Needing several variations of a simple modal dialog with an MFC project, I wrote a simple class, CDialogDlg
, which extends the standard MFC CDialog
class and contains hooks for implementation specific callbacks that are normally implemented as methods in the class extending CDialog
. These callbacks, if provided, are used when processing some events such as dialog initialization, OK button processing, and the DoDataExchange()
function which provides the method for interfacing the dialog class variables and the actual dialog controls.
My first version of this class used standard functions for the callbacks. So the callback setting methods of the extended class set a function pointer with the address of the standard function.
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; }
Then I realized I should be able to use a lambda instead of a standard function. The first version of the lambdas, which used the same parameters as the standard functions, that did not capture any variables compiled fine and worked fine with the existing methods and function pointers in the extended class.
However when I tried to capture a variable in the lambda specified in the callback setter method, I had compile errors.
Reviewing Passing capturing lambda as function pointer and C++ lambda with captures as a function pointer I get that trying to do a lambda with capture will not compile with the definitions of function pointer and callback setting function that I am using.
So I decided that I should add an additional lambda specific function pointer within the class along with an override of the existing methods with an additional method setting the callbacks with the lambda specific function pointer within the class.
Question: What should the function pointer definition and the parameter definition in the function that sets the callback for accepting a lambda with captured variables look like? I would like to capture the local variable CPCSampleDoc *pDoc
in the method CPCSampleView::OnDisplayReportList ()
. This variable contains a pointer to the CDocument
derived object for the view that I would like to capture rather than using the method of storing it and doing the static_cast
to get it back in the current version of the lambdas that do not capture.
Source Code
The CDialogDlg
class which extends CDialog
looks like the following:
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();
}
The CView
method which is triggered by a menu selection displays a dialog with a list of items to choose from. The dialog presented is a CDialogDlg
object with a specific dialog template ID. For special processing, I am using two different callbacks which are currently using a lambda that does not capture. The result is the following:
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();
}