I have template class ItemContainer that actually is facade for a whole family of containers with different capabilities like sorting, indexing, grouping etc.
Implementation details are hidden in cpp. file using pimpl idiom and explicit instantiation. Template is instantiated only with well-known limited set of implementation classes that define the actual behavior of container.
Main template implements common functions supported by all containers - IsEmpty(), GetCount(), Clear() etc.
Each specific container specializes some functions that are supported only by it, e.g. Sort() for sorted container, operator[Key&] for key indexed container etc.
The reason for such design is that class is replacement for several legacy hand-made bicycle containers written by some prehistorics in early 90th. Idea is to replace old rotting implemenation with modern STL&Boost containers keeping old interface untouched as much as possible.
The problem
Such design leads to unpleasant situation when user tries to call unsupported function from some specialization. It compiles OK, but produces error on linking stage (symbol not defined). Not very user friendly behavior.
Example:
SortedItemContainer sc; sc.IsEmpty(); // OK sc.Sort(); // OK IndexedItemContainer ic; ic.IsEmpty(); // OK ic.Sort(); // Compiles OK, but linking fails Of course, it could be completely avoided by using inheritance instead of specialization but I don't like to produce a lot of classes with 1-3 functions. Would like to keep original design.
Is there possibility to turn it into compile stage error instead of link stage one? I have a feeling that static assert could be used somehow.
Target compiler for this code is VS2008, so practical solution must be C++03 compatible and could use MS specific features. But portable C++11 solutions also are welcome.
Source Code:
// ItemContainer.h ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template <class Impl> class ItemContainer { public: // Common functions supported by all specializations void Clear(); bool IsEmpty() const; ... // Functions supported by sequenced specializations only ItemPtr operator[](size_t i_index) const; ... // Functions supported by indexed specializations only ItemPtr operator[](const PrimaryKey& i_key) const; ... // Functions supported by sorted specializations only void Sort(); ... private: boost::scoped_ptr<Impl> m_data; ///< Internal container implementation }; // class ItemContainer // Forward declarations for pimpl classes, they are defined in ItemContainer.cpp struct SequencedImpl; struct IndexedImpl; struct SortedImpl; // Typedefs for specializations that are explicitly instantiated typedef ItemContainer<SequencedImpl> SequencedItemContainer; typedef ItemContainer<IndexedImpl> IndexedItemContainer; typedef ItemContainer<SortedImpl> SortedItemContainer; // ItemContainer.cpp ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Implementation classes definition, skipped as non-relevant struct SequencedImpl { ... }; struct IndexedImpl { ... }; struct SortedImpl { ... }; // Explicit instantiation of members of SequencedItemContainer template void SequencedItemContainer::Clear(); // Common template bool SequencedItemContainer::IsEmpty() const; // Common template ItemPtr SequencedItemContainer::operator[](size_t i_index) const; // Specific // Explicit instantiation of members of IndexedItemContainer template void IndexedItemContainer::Clear(); // Common template bool IndexedItemContainer::IsEmpty() const; // Common template ItemPtr IndexedItemContainer::operator[](const PrimaryKey& i_key) const; // Specific // Explicit instantiation of members of SortedItemContainer template void SortedItemContainer::Clear(); // Common template bool SortedItemContainer::IsEmpty() const; // Common template void SortedItemContainer::Sort(); // Specific // Common functions are implemented as main template members template <class Impl> bool ItemContainer<Impl>::IsEmpty() const { return m_data->empty(); // Just sample } // Specialized functions are implemented as specialized members (partial specialization) template <> void SortedItemContaner::Sort() { std::sort(m_data.begin(), m_data.end(), SortFunctor()); // Just sample } ... // etc