Click to See Complete Forum and Search --> : Templates increase code size, a way around this ?
Yves M
November 28th, 2002, 10:16 AM
I like templates and even if I didn't I would still have to use them since ATL is mostly based on them.
But the problem I face (I think, please correct me if I'm wrong) is that whenever a template class is instantiated with a different parameter, all of its functions are "written out in full" by the compiler. This is usually fine, but the problem comes from the functions in the class which don't depend on the template parameters. They should not need to get get duplicated and unneccessarily increase the code size.
My concrete problem is that I have defined a templated "boiler-plate" windowed ActiveX object derived from about 15 ATL templates and a few of my own classes that does the basic window management, forwards the messages appropriately and does some other funky stuff.
The only reason I have to make this class templated is that otherwise I can't derive it from the basic ATL templates, don't have access to the required ATL variables (like m_bWindowOnly e.g.), can't call base functions (in only a very few of my functions) and can't expect ATL to call my overridden virtual functions.
Most of the overridden virtual functions don't require any template arguments, so having them in the templated class unfortunately duplicates them in the compiler-generated code and increases the size :/
What could I do ?
jfaust
November 28th, 2002, 10:26 AM
This is usually fine, but the problem comes from the functions in the class which don't depend on the template parameters.
These don't need to be inlined. Put them in an implementation file.
Jeff
Gabriel Fleseriu
November 28th, 2002, 10:26 AM
Originally posted by Yves M
But the problem I face (I think, please correct me if I'm wrong) is that whenever a template class is instantiated with a different parameter, all of its functions are "written out in full" by the compiler.
AFAIK, only functions that are actually used get instantiated.
Axter
November 28th, 2002, 10:58 AM
What you can do is put the code that doesn't need the template parameters, in a parrent class, and have your template parameter class inherit this parent class. That way the parent class can have the function implementation inside a *.cpp file, and the functions that need the template parameters can be put in the template class.
Example:
class foo
{
public:
void SomeFunctionThatDoesntNeedTemplatePar();
};
template<class T>
class TemplateClass : public foo
{
public:
T FunctionThatNeedsTemplate(){
return T();
}
};
//*.cpp
void SomeFunctionThatDoesntNeedTemplatePar()
{
//Code
}
jfaust
November 28th, 2002, 11:01 AM
You don't need a parent class. Simply put the methods that don't use the template parameter in the .cpp file.
Jeff
Yves M
November 28th, 2002, 11:22 AM
Thanks for the help :)
AFAIK, only functions that are actually used get instantiated.
Sure, but all the functions do get used ;) They are mostly overrides for the standard handling that ATL does, so they will get called.
Axter and Jeff:
Unfortunately most of the functions do depend on stuff provided by the base ATL classes. To use a simple description, here is what it looks like:
// An ATL class that provides the basic functionality
// I derive from about 15 of these
template <class Base>
class ATLBaseComObject
{
public:
HWND GetHwnd() {/*code*/}
HRESULT IOleObject_SetClientSite() {/*code*/}
// ...
};
// My "boiler-plate" implementation
// (has actually 5 template parameters)
template <class Base>
class BoilerPlate : public ATLBaseComObject<Base>
{
public:
// STDMETHOD stands for "virtual HRESULT __stdcall"
// This method does not need the template parameters
// But does need the base class functionality
STDMETHOD(Refresh)()
{
HWND hwnd = GetHwnd();
::RedrawWindow(hwnd, 0, 0, RDW_UPDATENOW);
return S_OK;
}
// This method needs the template parameter
STDMETHOD(IOleObject_SetClientSite)()
{
HRESULT hr = ATLBaseComObject<Base>::IOleObject_SetClientSite();
Refresh();
return hr;
}
};
class CFinal : public BoilerPlate<CFinal>
{
public:
STDMETHOD(SomeStuff)()
{
// Update the data and refresh the view
RefreshView();
}
};
Simply put the methods that don't use the template parameter in the .cpp file.
This I don't understand :/ Do you mean to write it in Final.cpp ?That would defeat the point of the Boilerplate class :/
jfaust
November 28th, 2002, 12:01 PM
I mean write a BoilerPlate.cpp, and methods like the following, which don't rely on the template parameter Base can be implemented in BoilerPlate.cpp.
STDMETHOD(Refresh)()
{
HWND hwnd = GetHwnd();
::RedrawWindow(hwnd, 0, 0, RDW_UPDATENOW);
return S_OK;
}
Then again, I don't use ATL, and there may be more going on here than I realize. However, the only reason you need to inline template class methods is that each different template parameter creates a different version of that method. If the template parameter is not used, that method does not need to be inlined.
Jeff
Yves M
November 28th, 2002, 12:17 PM
The problem is just that GetHwnd() is in this case a call to ATLBaseComObject<Base>::GetHwnd(), but the qualfier is not needed since I don't care which one of the base classes implements this as long as one of them does it. If I would write this method in a non-templated class then only one copy of this function would need to be present in the final executable, not one of every template parameter :/
However, the only reason you need to inline template class methods is that each different template parameter creates a different version of that method. If the template parameter is not used, that method does not need to be inlined.
Do you mean that writing something along these lines would work ? I don't think it does :/
// BoilerPlate.h
template <class Base>
class BoilerPlate : public ATLBaseComObject<Base>
{
public:
// STDMETHOD stands for "virtual HRESULT __stdcall"
// This method does not need the template parameters
// But does need the base class functionality
STDMETHOD(Refresh)();
// This method needs the template parameter
STDMETHOD(IOleObject_SetClientSite)()
{
HRESULT hr = ATLBaseComObject<Base>::IOleObject_SetClientSite();
Refresh();
return hr;
}
};
// BoilerPlate.cpp
STDMETHOD(Refresh)();
{
HWND hwnd = GetHwnd();
::RedrawWindow(hwnd, 0, 0, RDW_UPDATENOW);
return S_OK;
}
jfaust
November 28th, 2002, 12:33 PM
well, STDMETHOD won't work. You need className::, and you also don't want 'virtual'.
Jeff
Yves M
November 28th, 2002, 12:46 PM
Sure, but how ?
HRESULT BoilerPlate::Refresh()
{
HWND hwnd = GetHwnd();
::RedrawWindow(hwnd, 0, 0, RDW_UPDATENOW);
return S_OK;
}
Of course this won't work either, but I don't see what you are getting at.
jfaust
November 28th, 2002, 01:26 PM
I'm completely wrong. Sorry about that. I thought you could do:
template<typename T>
HRESULT BoilerPlate<T>::Refresh()
{
// never uses T...
}
in the implementation file, but you can't. What I got mixed up with is that you can do this with specific types, such as
HRESULT BoilerPlate<double>::Refresh()
{
// ...
}
Jeff
Yves M
November 28th, 2002, 01:37 PM
Oh ok :)
So I still don't know what I could do about this :(
jfaust
November 28th, 2002, 01:47 PM
STDMETHOD(Refresh)()
{
HWND hwnd = GetHwnd();
::RedrawWindow(hwnd, 0, 0, RDW_UPDATENOW);
return S_OK;
}
the call to GetHwnd() is actually a call to ATLBaseComObject<Base>::GetHwnd(), which does use the template parameter, so I believe the template parameter is really needed.
I don't see any way away from this.
Is this question academic, or do you really have a problem with executable size?
Jeff
Yves M
November 28th, 2002, 01:57 PM
Part and part :/ The thing is that for the moment I only have 4 COM objects which use this, but I'm going to have about 20 to 30. The size of the OCX increases quite a bit each time I add a new one, so I'm trying different avenues of preventing this.
the call to GetHwnd() is actually a call to ATLBaseComObject<Base>::GetHwnd(), which does use the template parameter, so I believe the template parameter is really needed.
Yes and no. I could actually derive a non-templated class CIntermediate from ATLBaseComObject<Base> and implement Refresh() in there, then derive CFinal from CIntermediate. Unfortunately ATL expects the parameter Base to be the final class in many cases :/
Hum, or maybe I could find out which these cases are and deal with this. Then write all the stuff I can into a non-templated class and derive from the two. This could work :)
codeguru.com
Copyright Internet.com Inc., All Rights Reserved.