3

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.

MSalters
  • 173,980
  • 10
  • 155
  • 350
Mr. Boy
  • 60,845
  • 93
  • 320
  • 589

4 Answers4

3

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.

bshields
  • 3,563
  • 16
  • 16
  • I define this myself for convenience. Works well (and by the way MSVC has a __super keyword) – Mr. Boy Jun 09 '10 at 12:26
  • Ok if that's the way you want to do it, I would still try it without it if you're having compile errors to make sure it's not playing a part. But the second part of my answer is definitely causing you problems as well. – bshields Jun 09 '10 at 12:29
  • Hmm, specialization would kind of undo the whole point of templates. I can look at the macros but the problem is there are quite a few that all call each other... yuck. – Mr. Boy Jun 09 '10 at 12:43
  • Well you have to specialize at some point. Note that this doesn't have to occur in the same header that defines your CMyDlg template class. The only thing that's more cumbersome here than the normal case is the fact that you need to make an empty derived class instead of just a typedef since you need to be able to define methods for the class using the IMPLEMENT_DYNAMIC macro. You know you could make your own macro that defined the empty derived class and invoked the IMPLEMENT_DYNAMIC macro and you could use that wherever you would normally have put a typedef for a template instantiation. – bshields Jun 09 '10 at 12:54
  • Interesting idea, I'll bear it in mind although my preference is to get a template-friendly set of macros if possible. – Mr. Boy Jun 09 '10 at 13:32
2

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

humbagumba
  • 2,054
  • 15
  • 16
2

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); }
Mr. Boy
  • 60,845
  • 93
  • 320
  • 589
0

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.

IdahoEngr
  • 1
  • 1