Inherit dependent typedef without using struct - c++

I have some code like this:
#include <string>
#include <map>
typedef std::less<std::string> Comparator; // simplified
typedef std::allocator<std::pair<const std::string, int>> Allocator; // simplified
template<class T>
struct Base
{
typedef std::map<std::string, T, Comparator, Allocator> type; // VERY long declaration in the actual code
};
template<class T>
struct Container : public Base<T>::type
{
Container(Allocator a) : Base<T>::type(Comparator(), a) {}
};
int main()
{
Allocator a;
Container<int> c(a);
}
Although the declarations are a bit more fancy in my actual code.
The Base struct is used so that I do not have to write the long map declaration multiple times.
I was wondering if there is a better way to inherit from the map without any Base struct?
No macros please. I hope in some way to hide the typedef in the Container class itself or something like that.
Thanks,

You can rely on templates having an injected class name. Inside the specialization of map<...>, the current specialization can be referred to simply by map. And that injected class name is also available to derived classes (and class templates). But since it's dependent, it requires a qualified name. It looks simpler than it sounds, here's how you roll the alias into Conatiner:
template<class T>
struct Container : public std::map<std::string, T, Comparator, Allocator>
{
using Base = typename Container::map;
Container(Allocator a) : Base(Comparator(), a) {}
};
Container::map is the injected class name. And the alias grabs it for convenient use.

Related

Recursive Compound structure

I have a class that looks something like this:
template<class KeyType, class... Types>
class BasicCompound
{
public:
using mapped_type = std::variant
<
ValueWrapper<BasicCompound>
, ValueWrapper<Types>...
>;
using key_type = KeyType;
// Accessors for retreiving and modifying content
// ...
private:
std::map<key_type, mapped_type> m_content;
};
ValueWrapper decides to put the content either inline or in a std::unique_ptr. Would it be possible with a similar interface, possibly through some kind of proxy, to make the recursiveness optional? By optional I mean that the user should not automatically get the possibility to store the BasicCompound inside itself, but rather specify it in the list of types.
What I have thought of:
A using directive does not work. A new type cannot be defined in it self, and a predeclaration of a following typedef is not allowed.
Adding a bool to the list of types, and use std::conditional_t for mapped_type. However, if the user wants to store an X<BasicCompound>, this approach fails.
Inject mapped_type from outside. Then I cannot hide the use of the ValueWrapper thing.
Using inheritance over a typdef like
struct MyCompound : BasicCompound<std::string, MyCompound, int> {};
This works but then the structure is not strictly recursive, as MyCompound now is a different type from BasicCompound. Maybe a CRTP-like approach could solve that problem, but then the inner compound type must be treated differently than the other types.
You can't have use a specialization of a class template as one of the template arguments of that specialization; there's no way to write it, and its name would be infinitely long. You can, however, use a wrapper to hold the recursive type:
template<class> class MaybeRecursive;
template<class T>
struct Wrapper {
using type=T;
};
struct MRWrapper {
using type=MaybeRecursive<MRWrapper>;
};
template<class T>
struct MaybeRecursive {
using type=typename T::type;
type *child;
};
void f() {
int x;
MaybeRecursive<Wrapper<int>> nonrec={&x};
MRWrapper::type rec={&rec};
}
MRWrapper can be made a class template to provide additional template arguments to MaybeRecursive.

call base class constructor without naming its class

class MyDerived: public Incredble<Difficult< And<Complicated, Long>>, And<Even, Longer>, BaseClass, Name>
{
public:
MyDerived();
}
MyDerived::MyDerived
: ???(params)
{}
Is there any way to call a base constructor without writing its full name and without typedeffing it?
The reason is clearly to avoid code duplication and introducing multiple positions to change if a detail in the base class template params changes.
Level 2 of this:
template <uint32 C>
class MyDerived: public Incredble<Difficult< And<Complicated, Long>>, And<Even, Longer>, BaseClass, Name>
{
public:
MyDerived();
}
template <uint32 C>
MyDerived::MyDerived<C>
: ???(C)
{
}
You could use injected-class-name. Incredible<...>::Incredible refers to itself, and since MyDerived isn't a class template, unqualified lookup will look in the scope of its base classes:
MyDerived::MyDerived
: Incredble(params)
{}
If Incredible is a dependent name, then you need to qualify it. You can actually simply use the derived type name to qualify the base class's injected-class-name (h/t Johannes Schaub-litb):
MyDerived::MyDerived
: MyDerived::Incredible(params)
{}
This will work in all cases.
If you don't wont to use using or typedef to avoid "polluting the enclosing namespace", you can use using inside the class/struct.
An example
#include <map>
template <typename ...>
class foo
{};
struct A : public foo<int, std::tuple<long, char, std::map<std::string, int>>>
{
using base_t = foo<int, std::tuple<long, char, std::map<std::string, int>>>;
A () : base_t{}
{ };
};
int main()
{ }

Compile error when using template aliases, inheriting template and using a "template parent's" types

I have a data container which has following requirements:
Be fast: Hence templates and not normal inheritance
Use different implementations
Be able to extend those implementations with more methods
Data is specified via template argument and needs to be able to save pointers to data container items
The solution I have come up with is as follows:
template<typename T, template<typename> class container_t>
class data_c
{
public:
typedef data_c<T, container_t> value_type;
typedef typename container_t<value_type>::ref container_ref;
container_ref link;
T c;
};
template<typename T>
class storage_container_impl
{
public:
typedef T value_type;
typedef storage_container_impl<T>* ref;
T data;
};
template<typename _impl>
class storage_container : public _impl
{
public:
typedef typename _impl::value_type value_type;
typedef typename _impl::ref ref;
};
template<typename T>
using impl_storage_container = storage_container<storage_container_impl<T> >;
typedef impl_storage_container<data_c<int, impl_storage_container> > c_storage_container;
int main()
{
c_storage_container tmp;
tmp.data.c=5;
tmp.data.link=&tmp;
return tmp.data.c;
}
Which results in following error (gcc 4.7):
test1.cpp:6:48: error: no type named 'ref' in 'impl_storage_container<data_c<int, impl_storage_container> > {aka class storage_container<storage_container_impl<data_c<int, impl_storage_container> > >}'
It works if I use T *data instead of T data (But I do not want that indirection), or if I do not use storage_container_impl and have T data directly in storage_container. Using storage_container_impl mixin-style does not solve the problem as well. As container_ref is a pointer, there also should be no reason why it does not work, e.g. because there is a loop in the template declaration. This is a minimized version of the problem. Any help would be appreciated!
I know that it's not a perfect solution, but you can try to replace
typedef impl_storage_container<data_c<int, impl_storage_container> > c_storage_container;
with
typedef impl_storage_container<data_c<int, storage_container_impl> > c_storage_container;
It compiles and work. Otherwise you get endless type recursion.

C++: Default values for template arguments other than the last ones?

I have my templated container class that looks like this:
template<
class KeyType,
class ValueType,
class KeyCompareFunctor = AnObnoxiouslyLongSequenceOfCharacters<KeyType>,
class ValueCompareFunctor = AnObnoxiouslyLongSequenceOfCharacters<ValueType>
>
class MyClass
{
[...]
}
Which means that when I instantiate an object of this class, I can do it several different ways:
MyClass<MyKeyType, MyValueType> myObject;
MyClass<MyKeyType, MyValueType, MyCustomKeyCompareFunctor> myObject;
MyClass<MyKeyType, MyValueType, MyCustomKeyCompareFunctor, MyCustomValueCompareFunctor> myObject;
Those are all good. The problem comes when I want to instantiate a MyClass that uses a non-default version of the ValueCompareFunctor argument, but I still want to use the default value of the KeyCompareFunctor argument. Then I have to write this:
MyClass<MyKeyType, MyValueType, AnObnoxiouslyLongSequenceOfCharacters<MyKeyType>, MyCustomValueCompareFunctor> myObject;
It would be much more convenient if I could somehow omit the third argument and just write this:
MyClass<KeyType, ValueType, MyCustomValueCompareFunctor> myObject;
Since the MyCustomValueCompareFunctor works only on objects of type MyValueType and not on objects of type MyKeyType, it seems like the compiler could at least theoretically work out what I meant here.
Is there a way to do this in C++?
In general, both in templates and functions or methods, C++ lets you use default for (and thereby omit) only trailing parameters -- no way out.
I recommend a template or macro to shorten AnObnoxiouslyLongSequenceOfCharacters<MyKeyType> to Foo<MyKeyType> -- not perfect, but better than nothing.
No. The closest you can come is to allow users to specify some sentinel type - like void - meaning "use default value here", and use template metamagic inside your class to typedef the real default if void was given to you. But this probably isn't a good idea from readability point of view.
Boost parameters and Boost graph named parameters are efforts towards naming parameters for template functions/methods. They give the opportunity to provide arguments in whichever order you prefer. Some arguments may be optional, with default values.
The same approach may be applied to template arguments. Instead of having N template arguments + P optional ones, create your class with N+1 template arguments. The last one will hold "named" parameters which can be omitted.
This answer is not complete yet, but i hope it's a good start !
An alternative option is to use Traits classes:
template <class KeyType>
class KeyTraits
{
typedef AnObnoxiouslyLongSequenceOfCharacters<KeyType> Compare;
};
template <class ValueType>
class ValueTraits
{
typedef AnObnoxiouslyLongSequenceOfCharacters<ValueType> Compare;
};
template<class KeyType class ValueType>
class MyClass
{
typedef KeyTraits<KeyType>::Compare KeyCompareFunctor;
typedef ValueTraits<ValueType>::Compare KeyCompareFunctor;
};
Then if you have a type which needs a different comparison function for Key's, then you'd explicitly specialize the KeyTraits type for that case. Here's an example where we change it for int:
template <>
class KeyTraits<int>
{
typedef SpecialCompareForInt Cmopare;
};
There is another option, which uses inheritance and which works like the following. For the last two arguments, it uses a class that inherits virtually from a class that has two member templates, that can be used to generate the needed types. Because the inheritance is virtual, the typedefs it declares are shared among the inheritance as seen below.
template<class KeyType,
class ValueType,
class Pol1 = DefaultArgument,
class Pol2 = DefaultArgument>
class MyClass {
typedef use_policies<Pol1, Pol2> policies;
typedef KeyType key_type;
typedef ValueType value_type;
typedef typename policies::
template apply_key_compare<KeyType>::type
key_compare;
typedef typename policies::
template apply_value_compare<ValueType>::type
value_compare;
};
Now, have a default argument that you use, which has typedefs for the default arguments you want provide. The member templates will be parameterized by the key and value types
struct VirtualRoot {
template<typename KeyType>
struct apply_key_compare {
typedef AnObnoxiouslyLongSequenceOfCharacters<KeyType>
type;
};
template<typename ValueType>
struct apply_value_compare {
typedef AnObnoxiouslyLongSequenceOfCharacters<ValueType>
type;
};
};
struct DefaultArgument : virtual VirtualRoot { };
template<typename T> struct KeyCompareIs : virtual VirtualRoot {
template<typename KeyType>
struct apply_key_compare {
typedef T type;
};
};
template<typename T> struct ValueCompareIs : virtual VirtualRoot {
template<typename ValueType>
struct apply_value_compare {
typedef T type;
};
};
Now, use_policies will derive from all the template arguments. Where a derived class of VirtualRoot hides a member from the base, that member of the derived class is dominant over the member of the base, and will be used, even though the base-class member can be reached by other path in the inheritance tree.
Note that you don't pay for the virtual inheritance, because you never create an object of type use_policies. You only use virtual inheritance to make use of the dominance rule.
template<typename B, int>
struct Inherit : B { };
template<class Pol1, class Pol2>
struct use_policies : Inherit<Pol1, 1>, Inherit<Pol2, 2>
{ };
Because we potentially derive from the same class more than once, we use a class template Inherit: Inheriting the same class directly twice is forbidden. But inheriting it indirectly is allowed. You can now use this all like the following:
MyClass<int, float> m;
MyClass<float, double, ValueCompareIs< less<double> > > m;

Template typedef error

Can anyone explain why this code gives the error:
error C2039: 'RT' : is not a member of 'ConcreteTable'
(at least when compiled with VS2008 SP1)
class Record
{
};
template <class T>
class Table
{
public:
typedef typename T::RT Zot; // << error occurs here
};
class ConcreteTable : public Table<ConcreteTable>
{
public:
typedef Record RT;
};
What can be done to fix it up. Thanks!
Update:
Thanks pointing out the issue, and for all the suggestions. This snippet was based on code that was providing an extensibility point within an existing code base, and the primary design goal was reducing the amount of work (typing) required to add new extensions using this mechanism.
A separate 'type traits' style class actually fits into the solution best. Especially as I could even wrap it in a C style macro if the style police aren't looking!
That's because the class ConcreteTable is not yet instantiated when instantiating Table, so the compiler doesn't see T::RT yet. I'm not really sure how exactly C++ standard handles this kind of recursion (I suspect it's undefined), but it doesn't work how you'd expect (and this is probably good, otherwise things would be much more complicated - you could express a logical paradox with it - like a const bool which is false iff it is true).
Fixing
With typedefs, I think you cannot hope for more than passing RT as additional template parameter, like this
template <class T, class RT>
class Table
{
public:
typedef typename RT Zot;
};
class ConcreteTable : public Table<ConcreteTable, Record>
{
public:
typedef Record RT;
};
If you don't insist on RT being available as Table<>::Zot, you can put it inside a nested struct
template <class T>
class Table
{
public:
struct S {
typedef typename RT Zot;
};
};
class ConcreteTable : public Table<ConcreteTable>
{
public:
typedef Record RT;
};
Or even external traits struct
template <class T>
struct TableTraits<T>;
template <class T>
struct TableTraits<Table<T> > {
typedef typename T::RT Zot;
};
If you only want the type be argument/return type of a method, you can do it by templatizing this method, eg.
void f(typename T::RT*); // this won't work
template <class U>
void f(U*); // this will
The point of all these manipulations is to postpone the need for T::RT as late as possible, particularly till after ConcreteTable is a complete class.
Why not just do something like this?
class Record
{
};
template <class T>
class Table
{
public:
typedef typename T Zot;
};
class ConcreteTable : public Table<Record>
{
public:
typedef Record RT; //You may not even need this line any more
};
The problem is that ConcreteTable is defined in terms of Table, but you can't define Table without a definition of ConcreteTable, so you've created a circular definition.
It also looks like there may be an underlying problem in the way you are designing your class hierarchy. I am guessing what you are trying to do is provide ways of manipulating a generic record type in your definition of Table, but leaving it up to ConcreteTable to define what the record type is. A better solution would be to make the record type a parameter of the Table template, and ConcreteTable a direct subclass:
class Record {};
template <class T>
class Table {
public:
typedef T RT;
};
class ConcreteTable : public Table<Record> {
};
Now you eliminate the circular dependency, and Table is still abstracted based on the type of record.
Notice that what you want to do can't be allowed by the compiler. If it was possible, you would be able to do this:
template <class T>
class Table
{
public:
typedef typename T::RT Zot;
};
class ConcreteTable : public Table<ConcreteTable>
{
public:
typedef Zot RT;
};
Which would be a kind of type-definition-infinite-loop.
The compiler blocks this possibility by requiring a class to be fully defined when one of its members needs to be used; in this case, the point of template instatiation for Table (the ancestors list in ConcreteTable) is before the definition of RT, so RT can't be used inside Table.
The workaround requires having an intermediate class to avoid the mutual dependence, as others already stated.
When Table<ConcreteTable> is instantiated, ConcreteTable is still an incomplete type. Assuming you want to stick with CRTP you could just pass Record as an additional template parameter like:
class Record
{
};
template <class T, class U>
struct Table
{
typedef U RT;
};
struct ConcreteTable : Table<ConcreteTable, Record>
{
};
Also note that you can access ConcreteTable as a complete type in any member functions in Table because they are instantiated only later when used. So this would be ok:
struct Record
{
};
template <class T>
struct Table
{
void foo()
{
typedef typename T::RT Zot;
Zot a; // ...
}
};
struct ConcreteTable : Table<ConcreteTable>
{
typedef Record RT;
};
int main()
{
ConcreteTable tab;
tab.foo();
}
I think everyone else has covered it pretty well, I just wanted to add that I think it's bad practice to inherit from a template of self and then try to patch things round to make it work. I would take a different approach and have the record type (RT) as the parameters instead of ConcreteTable itself. If you've ever looked at the std::iterator class, it uses this exact approach:
template <class Category, class T, class Distance = ptrdiff_t,
class Pointer = T*, class Reference = T&>
struct iterator {
typedef T value_type;
typedef Distance difference_type;
typedef Pointer pointer;
typedef Reference reference;
typedef Category iterator_category;
};
When a subclass inherits from iterator, it does this:
struct ExampleIterator : std::iterator<std::forward_iterator_tag, Example>
Which is exactly what you want to do. Notice that the 'RecordType' fields are actually in the superclass, and passed in through template parameters. This is the best way to do it, it's in the standard library because of it.
If you want to do more specialisation of the ConcreteTable subclass, you can always override methods from Table, as well as using the template parameters.
you are trying to use class CponcreateTable as a template parameter before the class is fully defined.
The following equivalent code would work just fine:
class Record
{
};
template <class T> Table
{
public:
typedef typename T::RT Zot; // << error occurs here
};
class ConcreteTableParent
{
public:
typedef Record RT;
};
class ConcreteTable: public Table<ConcreteTableParent>
{
public:
...
};