Repeating CRTP?

I am developing a series of "self aware" object classes that read input from various file formats (such as XML) to gather the data necessary to create the objects they are responsible for producing (based on tags found in parsing). To do this I am using a rather complicated network of Curiously Recurring Template Patterns. I may have additional questions about doing this but my basic question is can a CRTP use a CRTP as a base class? Here is an example (with some class stuff to help understand how it will be used):

The lowest level CRTP:
1
2
3
4
5
6
7
8
9
10
11
template<class DerivedClass, typename FactoryProducedType>
class ContentElement
{
protected:
	static TypeTag _typeTag;
public:
	static const char* classTagName() { return DerivedClass::classTagName(); } 
	FactoryProducedType object() = 0; // pure virtual function in base class
};
template<class DerivedClass, typename FactoryClass> TypeTag 
ContentElement<class DerivedClass, typename FactoryClass>::_typeTag = DerivedClass::initClass();


For many complex types where there is no single implementation that fits all tags, the class above is enough to keep things as simple as possible. However, for simple types such as boolean, there I can simply this even more. (I realize this may seem overly "complex" but these are incomplete classes and there is a lot more here than meets the eye... doing this or something similar will keep the code much shorter and much more simple). Here is where I am thinking I should make use of a CRTP of a CRTP. Example of the boolean:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
template <typename BooleanElementClass>
class BooleanElement : public ContentElement<BooleanElementClass, bool>
{
private:
	bool _data;
protected:
	friend class ContentElement<BooleanElementClass, bool>;
	inline static TypeTag initClass() { return BooleanElementClass::initClass(); }
public:
	inline static const char* classTagName() { return BooleanElementClass::classTagName(); }
	bool object() override;
};

// cpp:
template <typename BooleanElementClass>
bool BooleanElement<BooleanElementClass>::object() { return _data; }


and finally... a real example of an "end product" class:
1
2
3
4
5
class MyBoolTagClass : BooleanElement<MyBoolTagClass>
{
	inline static const char* classTagName() { return "MyBoolTag"; }
 	static TypeTag initClass(); // inits class and returns tag enum
};


Is this an acceptable approach?
Last edited on
can a CRTP use a CRTP as a base class?
Sure, why not.

CRTP is good if you want for a certain derived class a common base class even if different base classes are used.

base1 -> crtp<base1> -> derived1
base2 -> crtp<base2> -> derived2

Is this an acceptable approach?
It depends.
To do this I am using a rather complicated network of Curiously Recurring Template Patterns.
'complicated' is usually not a good term. When you feel that it is complicated it is usually better to think whether there is a more simple solution.

For instance: Is there any reason why the tag isn't a std::string?
'complicated' is usually not a good term

"Complicated" refers to the network of classes, not the classes themselves. Actually, the "complexity' will make it much easier to work with in the long run because this code must be extremely flexible due to its compartmentalized, cross platorm, and cross API nature. The complex "network" reduces the complexity and upkeep of the classes and at the same time makes the user-friendliness of the app easier to implement.

For instance: Is there any reason why the tag isn't a std::string

Because I am mixing libraries, three of which have their own string class (Qt being one of them). I want to perform as few deep copies as possible when doing string lookups in a hash table. For all single byte characters, (QByteArray in Qt), I am using the type they all have in common: char*.
Last edited on
when doing string lookups in a hash table.
For this problem I suggest to create a comparator that deals with the different type.

Like so:
1
2
3
bool Compare(const QString &s1, const std::string &s2);
bool Compare(const QByteArray &s1, const std::string &s2);
...


Complicated structures tends to remain complicated. So i would still suggest that you stick with the standard and having a small interface with the external stuff.
Topic archived. No new replies allowed.