开发者

Can a custom MFC window/dialog be a class template instantiation?

开发者 https://www.devze.com 2023-01-02 16:11 出处:网络
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 ac

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.

0

精彩评论

暂无评论...
验证码 换一张
取 消