CRTP'ed Containers

I cut my teeth in some programming patterns, and I'm very new to this. I want to implement several CRTP classes containing an STL container. Let class A{}; serves as an example for a base class (compilation time), from which class B{}; and class C{}; are "derivatives" during compilation following the CRTP style.

Now both B and C will contain containers. For the purpose of the example, let it be std::vector and a std::set respectively. Now I want to expose iterators from them using the begin() and end() function, which provides an advanced iterator. However, I do not want to disclose what the exact container is that is inside B and C , and I want to define these functions for A , so that during the call, use the correct value for B and C

Is it possible? My current plan is to have an inner Iterator class for B as well as C , which will contain the actual iterator (vector or set, as appropriate) and delegate a call to it. However, this seems to be a lot of replicated glue code, and I suspect there is a better option.

I have a couple of questions:

  • How to declare internal clans in A , B and C so that it works well with CRTP. Does it need to be replicated for A , B and C ? Could it be an empty class in A , and I mask them in B and C with specialized implementations?

  • How can I set an iterator with less glue and less duplication?

I do not want to create dependencies with external libraries, for example boost, and I want to stick only to std. Therefore, I must implement everything that I need. Thanks for the help.

+4
source share
2 answers

If I understood you correctly, you are looking for something like this. Please note: I made a simple constructor to illustrate that it works. Also, your class A is my class TWrapperBase , B is TWrapperB , C is TWrapperC . Another thing, you really do not need to have two derived classes for this specific example, but I assume that your classes B and C significantly different to justify it in your program.

EDIT: Forgot to increase lIterSet in loop.

 #include <vector> #include <set> #include <iostream> template< typename PType, typename PContainer > class TWrapperBase { public: typedef PType TType; typedef PContainer TContainer; typedef typename TContainer::iterator TIterator; protected: TContainer mContainer; public: TWrapperBase( const TContainer& pOriginal ) : mContainer( pOriginal ) { } TIterator begin( void ) { return mContainer.begin(); } TIterator end( void ) { return mContainer.end(); } }; template< typename PType, class PContainer = std::vector< PType > > class TWrapperB : public TWrapperBase< PType, PContainer > { public: TWrapperB( const TContainer& pOriginal ) : TWrapperBase( pOriginal ) { } }; template< typename PType, class PContainer = std::set< PType > > class TWrapperC : public TWrapperBase< PType, PContainer > { public: TWrapperC( const TContainer& pOriginal ) : TWrapperBase( pOriginal ) { } }; int main( void ) { int lInit[] = { 1, 2, 3 }; std::vector< int > lVec( lInit, lInit + 3 ); std::set< int > lSet( lInit, lInit + 3 ); TWrapperB< int > lB( lVec ); TWrapperC< int > lC( lSet ); std::vector< int >::iterator lIterVec = lB.begin(); std::set< int >::iterator lIterSet = lC.begin(); while( lIterVec < lB.end() ) { std::cout << "vector: " << *lIterVec << " / set: " << *lIterSet << std::endl; lIterVec++; lIterSet++; } return 0; } 
+1
source

Set iterator via CRTP:

 template <typename T, typename Iter, typename ConstIter> struct Base { Iter begin() { return static_cast<T*>(this)->begin(); } Iter end() { return static_cast<T*>(this)->end(); } ConstIter begin() const { return static_cast<const T*>(this)->begin(); } ConstIter end() const { return static_cast<const T*>(this)->end(); } }; struct B : Base<B, std::vector<int>::iterator, std::vector<int>::const_iterator> { std::vector<int>::iterator begin() { return container.begin(); } ... private: std::vector<int> container; }; 

If you have more types to display, then pass the attribute class as a template argument to Base :

 template <typename T, typename Traits> struct Base { typename Traits::iterator begin() { ... } ... }; // For this purpose, vector<int> makes a perfect traits class ! struct B : Base<B, std::vector<int> > { std::vector<int>::iterator begin() { ... } ... }; // Here is an example function taking Base as argument template <typename T, typename Traits> void foo(const Base<T, Traits>& x) { typename Traits::iterator i = x.begin(); ... } 
+2
source

Source: https://habr.com/ru/post/1369607/


All Articles