Can a custom MFC window/dialog be a class template instantiation?
There's a bunch of special macros that MFC uses when creating dialogs, and in my quick tests I'm getting weird errors trying to compile a template dialog class. Is this likely to be a big pain to achieve?
Here's what 开发者_如何转开发I tried:
MyDlg.h
template <class W>
class CMyDlg : public CDialog
{
typedef CDialog super;
DECLARE_DYNAMIC(CMyDlg <W>)
public:
CMyDlg (CWnd* pParent); // standard constructor
virtual ~CMyDlg ();
// Dialog Data
enum { IDD = IDD_MYDLG };
protected:
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV support
DECLARE_MESSAGE_MAP()
private:
W *m_pWidget; //W will always be a CDialog
};
IMPLEMENT_DYNAMIC(CMyDlg<W>, super) <-------------------
template <class W>
CMyDlg<W>::CMyDlg(CWnd* pParent)
: super(CMyDlg::IDD, pParent)
{
m_pWidget = new W(this);
}
I get a whole bunch of errors but main one appears to be:
error C2955: 'CMyDlg' : use of class template requires template argument list
I tried using some specialised template versions of macros but it doesn't help much, other errors change but this one remains. Note my code is all in one file, since C++ templates don't like .h/.cpp like normal.
I'm assuming someone must have done this in the past, possibly creating custom versions of macros, but I can't find it by searching, since 'template' has other meanings.
You may have other problems as well, but one thing has got to be your use of super
. That's a java thing not a C++ thing. Instead of super
you need to use CDialog
.
After looking into IMPLEMENT_DYNAMIC
the macro definition is not compatible with templates, it doesn't use the template <class T>
syntax before the function definitions. What you need to do is define derived class specializations of your template and then use the macro on them. So you could do this:
class MyDlgA : public CMyDlg<A>
{
};
IMPLEMENT_DYNAMIC(MyDlgA, CDialog);
And then do that for all specializations that you want. If that's not feasible, look at the macro and make your own templatized version of it.
Edit: Following up on my comment, you could make a macro like this:
#define INSTANTIATE_DLG_TEMPLATE(T) \
class MyDlg##T : public CMyDlg<T> \
{ \
}; \
\
IMPLEMENT_DYNAMIC(MyDlg##T, CDialog);
And then just use this wherever you would normally have defined the template specialization in a header file with a typedef.
I haven't done this for a dialog, only for some custom controls, but I see no reason why It wouldn't work.
I know that there's at least a template version for defining message maps, BEGIN_TEMPLATE_MESSAGE_MAP.
Check out http://msdn.microsoft.com/en-us/library/aa991537(VS.80).aspx
Here's a working solution, though ugly... I didn't get round to rewriting as a macro after expanding the existing one and fixing for templates:
//Template-enabled expansion of IMPLEMENT_DYNAMIC(CMyDlg,super)
template <class W> CRuntimeClass* PASCAL CMyDlg<W>::_GetBaseClass(){ return RUNTIME_CLASS(super); }
template <class W> AFX_COMDAT const CRuntimeClass CMyDlg<W>::CMyDlg= {
"CMyDlg", sizeof(CMyDlg<W>), 0xFFFF, NULL,&CMyDlg<W>::_GetBaseClass, NULL, NULL };
template <class W> CRuntimeClass* PASCAL CMyDlg<W>::GetThisClass() { return _RUNTIME_CLASS(CMyDlg); }
template <class W> CRuntimeClass* CMyDlg<W>::GetRuntimeClass() const { return _RUNTIME_CLASS(CMyDlg); }
There are a number of issues that arise because of the DECLARE_DYNAMIC macro. If you trace the macros you will find that a member variable and three functions have to be defined.
template<typename T>
class CTemplateDialogDlg : public CDialogEx
{
// Construction
public:
// standard constructor
CTemplateDialogDlg( CWnd* pParent = nullptr )
: CDialogEx( IDD_TEMPLATEDIALOGAPP_DIALOG, pParent )
{}
// Dialog Data
#ifdef AFX_DESIGN_TIME
enum
{
IDD = IDD_TEMPLATEDIALOGAPP_DIALOG
};
#endif
public:
T m_tMemberValue;
protected:
// DDX/DDV support
virtual void DoDataExchange( CDataExchange* pDX )
{
CDialogEx::DoDataExchange( pDX );
}
protected:
// Member & Functions from IMPLEMENT_DYNAMIC( CUnitTypeCurvePointDlg, CDialogEx )
static const CRuntimeClass classCTemplateDialogDlg;
static CRuntimeClass* PASCAL _GetBaseClass()
{
return RUNTIME_CLASS( CDialogEx );
}
static CRuntimeClass* PASCAL GetThisClass()
{
return (CRuntimeClass*)&classCTemplateDialogDlg;
}
virtual CRuntimeClass* GetRuntimeClass() const
{
return (CRuntimeClass*)&classCTemplateDialogDlg;
}
};
Then the member variable must be created (once only)
typedef CTemplateDialogDlg<int> CTemplateDialogIntDlg;
const CRuntimeClass CTemplateDialogIntDlg::classCTemplateDialogDlg;
And then the template dialog can be used
CTemplateDialogIntDlg Dlg;
However, since this bypasses the MFC macros, you will be responsible to keep the member variable and functions properly defined.
I haven't addressed the DECLARE_MESSAGE_MAP issues, but they would be similar.
精彩评论