I have a C++ library (with over 50 source files) which uses a lot of STL routines with primary containers being list and vector. This has caused a huge code bloat and I would like to reduce the code bloat by creating a wrapper over the list and vector.
Shown below is my wrapper over std:: and the wrapped instances.
template<typename T>
class wlist
{
private:
std::list<T> m_list;
public:
// new iterator set.
typedef typename std::list<T>::iterator iterator;
typedef typename std::list<T>::const_iterator cIterator;
typedef typename std::list<T>::reverse_iterator reverse_iterator;
unsigned int size () { return m_list.size(); }
bool empty () { return m_list.empty(); }
void pop_back () { m_list.pop_back(); }
void pop_front () { m_list.pop_front(); }
void push_front (T& item) { m_list.push_front(item); }
void push_back (T item) { m_list.push_back(item); }
iterator insert(iterator position, T item) {m_list.insert(position,item);}
bool delete_item (T& item);
T back () { return (m_list.empty()) ? NULL : m_list.back();}
T front () { return (m_list.empty()) ? NULL : m_list.front();}
iterator erase(iterator item ) { return m_list.erase(item); }
iterator begin() { return m_list.begin(); }
iterator end() { return m_list.end(); }
reverse_iterator rbegin() { return m_list.rbegin(); }
};
File A:
class label {
public:
int getPosition(void);
setPosition(int x);
private:
wlist<text*> _elementText; // used in place of list<text> _elementText;
}
File B:
class image {
private:
void draw image() {
wlist<label*>::iterator currentElement = _elementText.begin();
((label*)(*currentElement))->getPosition();
currentElement ++;
}
}
My belief was that by wrapping the STL container, I would be able to reduce the code bloat but the reduction in code size seems to be insignificant while my motive to wrap the STL was to achieve a code reduction of roughly 20%.
1) By exposing the "wrapped" iterator, have I in-turn embedded STL into my client code thereby negating all the code saving that I was trying to do ????
2) Have I chosen the right profiling method ????
Size before modification:
$ size libWrap.so
text: 813115
data: 99436
bss: 132704
dec : 1045255
hex: ff307
Size after modification:
$ size libWrap.so
text: 806607
data: 98780
bss: 132704
dec : 1038091
hex: fd70b
Firstly, the interface offered by your wrapper is completely and totally disgusting. There's a reason that iterators exist, and it's because your implementation flat out doesn't work for non-pointer types. Returning and taking by value instead of by reference? A terrible design.
Secondly, you can never reduce the size of your program by introducing more code. Your wrapper still uses the STL list under the hood, so you're still instantiating all of those types. Most likely, the compiler just completely removed the whole lot.
Thirdly, you're not even doing an equivalent replacement, because you've replaced what used to be a list of values wth a list of pointers, introducing six million lifetime headaches and other problems.
Fourthly, even the idea of code bloat is quite ridiculous on the vast majority of platforms. I, of course, cannot psychically know that you are not working on some embedded platform with hardly any memory (although I doubt you would use many lists on such a platform) but on virtually every system, the size of the code itself is meaningless compared to other assets needed for the program to execute.
What you can do is try something like SCARY iterators or partial specializations for T*.
I am trying to imagine why you are concerned with this. Why is it a problem?
My guess is that you have many ( hundreds? ) of different classes, and each one generates a copy of the templated containers.
If this is so and if it is necessary, then sit back and let the compiler do the tedious work for you.
If it is not necessary, then the problem seems likely to be that all your different classes are not necessary. There is a problem with your class design. You might have many different classes that differ only slightly. If the difference is so slight that the extra code generated to handle the difference seems out of proportion, then the different behavior might be better handled by code inside a single class.
It seems that you want to pre-compile your templated wrapper once only in your library rather than have the compiler figure out the templated class every time it gets called. You can do this by moving your declaration from the header file (where it normally is for templated code) into your .cpp file. This also has the advantage that it reduces compilation times. There is a price in flexability in this approach, however, you have know from the beginnings the types that you want your class to work for (but you don't want the compiler to figure it out for you, anyway)
Putting templated code into a .cpp file will usually result in linker errors. To avoid these you need to expliciltly declaire the templates that you want the compiler to compile in the cpp file:
At the end of the .cpp file, you write something like
template class wlist<double>;
template class wlist<int>;
etc.
This instructs the compiler to compiler to compile these version of the class (and only these versions).
This of cause reduces the flexibility of your library - if you call a wlist<complex>
then you would get the linker errors.
See here for more info: http://www.parashift.com/c++-faq-lite/templates.html#faq-35.12
I believe this is usually done to reduce compilation times - I imagine it will reduce code bloat too, but I have never used the technique for this reason and so never checked the size of my executable....