Access variadic template argument of class X inside class Y<X> - c++

I have some graph classes that can inherit from each other to provide the functionality needed in different cases (directed graph, traversable graph, nested graph, etc).
In order to make it possible to use the final derived types, these are provided as template arguments to the base classes. This way, for instance Edge::GetSource can return the derived class Node, and not the base class Node.
To make things simple, the graph classes take only one template argument: GraphTypes. GraphTypes in turn takes as template arguments all the needed derived graph classes. This makes it very easy to make changes to the types used in the graph classes, as only GraphTypes need to be changed.
template<class GraphT, class NodeT, class EdgeT>
class GraphTypes {
public:
using GraphType = GraphT;
using NodeType = NodeT;
using EdgeType = EdgeT;
}; // GraphTypes
template<class GraphTypes>
class Edge {
private:
using GraphType = typename GraphTypes::GraphType;
using NodeType = typename GraphTypes::NodeType;
using EdgeType = typename GraphTypes::EdgeType;
public:
NodeType &GetSource();
protected:
NodeType *m_pSource;
}; // Edge
template<class GraphTypes>
inline NodeType &Edge<GraphTypes>::GetSource() {
return *m_pSource;
} // Edge::GetSource
This is working great! But now I want to add an optional Context argument. In some cases, a context object needs to passed around while working on the graph and be made aware, for instance, when Edge::SetSource is called.
My idea is to add a variadic template argument. This way graph classes that need no context can just omit it. But how can I add a variadic template argument to GraphTypes and then use it inside the other graph classes that have GraphTypes as an template argument?
template<class GraphT, class NodeT, class EdgeT, class ...ContextT>
class GraphTypes {
public:
// error C3520: 'ContextT': parameter pack must be expanded in this context
using ContextType = ContextT;
}; // GraphTypes
template<class GraphTypes>
class Edge {
private:
using ContextType = typename GraphTypes::ContextType;
public:
// error C3543: 'GraphTypes::ContextType': does not contain a parameter pack
void SetSource(NodeType &Source, ContextType &...Context);
}; // Edge
template<class GraphTypes>
void Edge<GraphTypes>::SetSource(NodeType &Source, ContextType &...Context) {
if(&Source == m_pSource)
return;
// I think this should work, but open to suggestions ;)
std::initializer_list<bool>{ (Context.PreSetSource(*this), true)... };
m_pSource = &Source;
std::initializer_list<bool>{ (Context.PostSetSource(*this), true)... };
} // Edge::SetSource

Related

How to use templates with Nested class in C++

I am trying to use templates for a nested class. I am not sure how to access the class type of the inner class from another class.
Sample Code below.
// I have a List class that can accept any type. It has an inner class
template <class T>
class List
{
public:
class Node
{
public:
T data;
Node* next;
Node* prev;
};
void addElement(Node& value);
private:
Node* head;
};
// Here I am making an array of Lists
template <class T>
class ListArray
{
public:
// Here is my question.
void add(Node& value); // How to give "Node" class type here ?
private:
List<T> _listArr[10];
};
// Is the below the right way to define ListArray::add, especially the way in which Node type can be passed to it ?
template <class T>
void ListArray<T>::add(List<T>::Node& value)
{
// Make a call to List::addElement and pass Node& value
_listArr[0].addElement(value);
//....
}
Could you kindly let me know how the above can be achieved ? Thanks.
Node is a nested type of a class template:
template <class T>
class ListArray
{
public:
typedef typename List<T>::Node Node_type;
void add(Node_type& value); // Refer to it as Node_type
private:
List<T> _listArr[10];
};
And:
template <class T>
void ListArray<T>::add(typename ListArray<T>::Node_type& value)
{
_listArr[0].addElement(value);
//....
}
I used typedef to define local name for node type. It is very useful - now, clients of ListArray can write code, that uses Node_type explicitly (without knowing what it actually is). This technique is used heavily in std library - usually, std:: types have tons of typedefs to allow writing flexible code.
Also, note the typename keyword - it is required in case of nested types of class templates. It indicates, that given name is the name of a type (without it, you should get a compiler error).

Nested Template Issue C++

I have a templated node class declared as
template <class T_>
class Node : public std::iterator<std::bidirectional_iterator_tag, T_, T_>
{
...
}
Which works fine, and I'm trying to take this existing class and apply it to a new linked list class.
I'd like to create it by calling something along the lines of
LinkedList<float> nodeList;
where it implicitly takes the type given, and within the structure of the class handles it as Node of type T_.
Is there a way to do this without making the node a sub-class of LinkedList?
Is this what you are asking?
template <class T_>
class LinkedList
{
public:
typedef Node<T_> node_type;
void AddNode(node_type *node)
{ /*...*/ }
};
Technically, the typedef is not necessary, but IME it is a good pratice to typedef the dependent types in this way.
You can provide the iterator as a default template argument:
template<
class T,
class Iterator = Node<T>
> class LinkedList
{
// ...
};
If i understood your question correct, you need to instantiate a class template using templated class type. Use this syntax:
LinkedList<Node<float> > nodeList;
(notice a space between > >; it is always a good idea to add it, because old versions of some compilers have troubles on distinguishing such constructs from shift operators >>)

Visitor Implementation: Constant versus Mutable Visitor

Given that the difference between a constant visitor and a mutable visitor is that the methods in a constant visitor are not allowed to modify the visited object.
class Integer_Field;
class Boolean_Field;
class Text_Field;
class Visitor_Constant
{
public:
virtual void visit(const Integer_Field& f) = 0;
virtual void visit(const Boolean_Field& f) = 0;
virtual void visit(const Text_Field& f) = 0;
};
class Visitor_Mutable
{
public:
virtual void visit(Integer_Field& f) = 0;
virtual void visit(Boolean_Field& f) = 0;
virtual void visit(Text_Field& f) = 0;
};
I would like to minimize support for these visitors. For example, if I come up with a class Blob_Field, I need to modify both classes. I would prefer to have something where I only have to modify one class or stencil.
The maintenance issue fans out when there are many classes defined from these parent visitors. This is the main reason I want to simplify the maintenance.
My questions:
(Note: This must be resolved without using C++11 features as my development environment does not support C++11 and I'm not allowed to upgrade at this time.)
Is there a way to use the template mechanism to merge the two
(such as supply 'const' as a parameter to the template)?
How can I set up these visitors so that I can pass a
Visitor_Constant to methods using a Visitor_Mutable?
Note: Combining these classes via a parent class, doubles the visitor methods that must be implemented and maintained.
Edit 1: Class relationships
class Component; // Base class for fields and records.
class Field : public Component; // Base class for all fields
class Record : public Component // Base class for all records
{
std::vector< boost::shared_ptr<Component> > component_container;
};
class Integer_Field : public Field;
class Boolean_Field : public Field;
class Text_Field : public Field;
Edit 2: Rationality of fields
One rationality for the fields being treated specifically is the case of generating an SQL statement for creating a table.
Another is for loading fields from a database table.
Is there a way to use the template mechanism to merge the two (such as supply 'const' as a parameter to the template)?
You can supply it through a template template parameter, and also keep the pure visitor implementation generic. I originally did this in C++11, but since you say you don't have it, I'll present one with type lists instead.
Here is the type list implementation.
/* Our empty node for our type list. */
class Empty {};
/* Cons for our type list. */
template <typename First_, typename Rest_>
class Cons {
public:
/* First type */
typedef First_ First;
/* Rest. */
typedef Rest_ Rest;
}; // Cons<First_, Rest_>
Here is the generic visitor implementation.
/* Forward declaration. */
template <template <typename> class Decorator, typename Members>
class VisitorRecur;
/* Base case. */
template <template <typename> class Decorator, typename Member>
class VisitorRecur<Decorator, Cons<Member, Empty> > {
public:
/* Pure virtual for each of the members. */
virtual void operator()(
typename Decorator<Member>::Type that) const = 0;
}; // VisitorRecur<Decorator, Member>
/* Recursive case. */
template <template <typename> class Decorator, typename Members>
class VisitorRecur
: public VisitorRecur<Decorator, typename Members::Rest> {
public:
/* Bring the operator()s into scope. */
using VisitorRecur<Decorator, typename Members::Rest>::operator();
/* Pure virtual for each of the members. */
virtual void operator()(
typename Decorator<typename Members::First>::Type that) const = 0;
}; // VisitorRecur<Decorator, typename Members::Rest>
/* Final visitor. */
template <template <typename> class Decorator, typename Members>
class Visitor : public VisitorRecur<Decorator, Members> {
public:
/* Bring the operator()s into scope. */
using VisitorRecur<Decorator, Members>::operator();
}; // Visitor<Decorator, Members>
Here are the two decorators that we'll define.
/* ConstRef. */
template <typename T>
class ConstRef {
public:
typedef const T & Type;
}; // ConstRef<T>
/* Ref. */
template <typename T>
class Ref {
public:
typedef T & Type;
}; // Ref<T>
Here is the use case for it.
/* Forward declarations. */
class Circle;
class Square;
class Triangle;
/* Add the members into a type list. */
typedef Cons<Circle, Cons<Square, Cons<Triangle, Empty> > > Members;
/* Our const visitor which accepts the members by const-ref. */
typedef Visitor<ConstRef, Members> ConstVisitor;
/* Our mutating visitor which accepts the members by ref. */
typedef Visitor<Ref, Members> MutatingVisitor;

How to avoid a "template template template" template in c++

I have tried to implement a "template template template" - template class to fullfill my needs ( I am quite new in using template metaprogramming). Unfortunately, I have found the following topic too late:
Template Template Parameters
Nevertheless, I need to implement something like listed below.
According to the compiler the last typedef is not working. I am not sure, but I think this is due to the limitation of 3x template restriction. Is there any possibility to bypass a 3xtemplate definition in this simple example?
template < typename TValueType >
class ITTranslator
{
public:
ITTranslator() = 0;
virtual ~ITTranslator() = 0;
virtual void doSomething() = 0;
}
template < typename TValueType >
class TConcreteTranslator1 : public ITTranslator<TValueType>
{
public:
TConcreteTranslator1(){}
~TConcreteTranslator1(){}
void doSomething() {}
}
template < typename TValueType >
class TConcreteTranslator2 : public ITTranslator<TValueType>
{
public:
TConcreteTranslator2(){}
~TConcreteTranslator2(){}
void doSomething() {}
}
template <
typename TValueType,
template < typename TValueType > class TTranslatorValueType
>
class ITClassifier
{
public:
ITClassifier() = 0;
virtual ~ITClassifier() = 0;
}
template <
typename TValueType,
template < typename TValueType > class TTranslatorValueType
>
class TConcreteClassifier1 : public ITClassifier<TValueType,TTranslatorValueType >
{
public:
TConcreteClassifier1() {}
~TConcreteClassifier1() {}
void dodo(){}
}
template <
typename TValueType,
template <typename TValueType> class TTranslatorValueType,
template <template<typename TValueType> class TTranslatorValueType> class TClassifierValueType
>
class ITAlgorithm
{
public:
ITAlgorithm()=0;
virtual ~TAlgorithm()=0;
virtual run() = 0;
}
template <
typename TValueType,
template <typename TValueType> class TTranslatorValueType,
template <template<typename TValueType> class TTranslatorValueType> class TClassifierValueType
>
class TConcreteAlgorithm1 : public ITAlgorithm<TValueType,TTranslatorValueType,TTranslatorValueType>
{
public:
TConcreteAlgorithm1 (){}
~TConcreteAlgorithm1 (){}
run()
{
TClassifierValueType< TTranslatorValueType>* l_classifier_pt = new TClassifierValueType< TTranslatorValueType>( );
// add this object to a internal list...
}
}
int main()
{
typedef TConcreteTranslator1< cvbase::uint32_t > translator_t;
typedef TConcreteClassifier1< cvbase::uint32_t, TConcreteTranslator1> classifier_t;
typedef TConcreteAlgorithm1 < cvbase::uint32_t, TConcreteTranslator1, TConcreteClassifier1> algorithm_t; // not possible
return 0;
}
Thanks a lot, I really appreciate any help!
EDIT:
I have extended my listing (I am pretty sure it will not compile :)) to show the motivation why I am using my weird concept :)
There is really no need to pass template template parameter around
here. Usually you can just take a normal template argument and provide
a reasonable default:
template<typename ValueType>
struct translator {};
template<typename ValueType, typename Translator = translator<ValueType>>
struct Classifier {};
template<typename ValueType,
typename Translator = translator<ValueType>,
typename Classifier = classifier<ValueType, Translator>
>
struct Algorithm {};
This is done the same way for allocator aware containers.
And please do away with the horrible hungarian-notation prefixes.
NB: It seems from your usage of constructors and destructors that you
don't really have a grasp of basic C++. You might want to stay away
from templates before you have understood easier concepts.
Yes it is possible to avoid template template parameters (of any level).
A template is basically a type-level function. You feed it a type, and get another type back.
A template template parameter is itself a type-level function, and a template that accepts such parameter is a higher-order type-level function.
It is possible to implement higher-order type-level functions with member templates, without ever using template template parameters. I'm not really sure you need it for your design, but here's a quick and dirty example:
// regular type, a.k.a. zeroth-order type-level function,
// a.k.a. "type of kind *"
struct N
{
int a;
};
// a first-order type-level function, a.k.a. "type of kind *->*"
// it is wrapped in a regular type
struct B
{
template <class A> struct Impl
{
void foo(A a)
{
int aa = a.a;
}
};
};
// a second-order type-level function
// that accepts a (wrapped) first-order type function
// and also a regular type. the kind of it would be (*->*)->*->*
// it applies its first argument to its second argument
struct Z
{
template <class X, class Y> struct Impl
{
typename X::template Impl<Y> ya;
void bar()
{
ya.foo(Y());
}
};
};
// now this is something: a third-order type-level function
// that accepts a (wrapped) second-order type-level function
// and a (wrapped) first-order type-level function
// and a zeroth-order type-level function
// it applies its first argument to its second and third arguments
// it is also wrapped in a regular type for consistency
// try to figure out its kind
struct T
{
template <class P, class Q, class R> struct Impl
{
typename P::template Impl<Q, R> yb;
void baz()
{
yb.bar();
}
};
};
T::Impl<Z, B, N> tt;
In this case you don't really need to have template parameters, basically the only variable type is TValueType right?
The other types can be resolved on the class body using TValueType.
Something like this:
template <
typename TValueType
>
class TAlgorithm
{
public:
// TTranslator <TValueType> whatever
// TTranslatorValueType <TValueType> whatever
TAlgorithm(){}
~TAlgorithm(){}
}

Can you use C++ templates to specify a collection type and the specialization of that type?

Example, I want to specialize a class to have a member variable that is an stl container, say a vector or a list, so I need something like:
template <class CollectionType, class ItemType>
class Test
{
public:
CollectionType<ItemType> m_collection;
};
So I can do:
Test t = Test<vector, int>();
t.m_collection<vector<int>> = vector<int>();
But this generates
test.cpp:12: error: `CollectionType' is not a template
What you want is a template template parameter:
template <template <typename> class CollectionType, class ItemType>
class Test
{
public:
CollectionType<ItemType> m_collection;
};
What we did here is specifying that the first template parameter, i.e. CollectionType, is a type template. Therefore, Test can only be instantiated with a type that is itself a template.
However, as #Binary Worrier pointed in the comments, this won't work with STL containers since they have 2 template parameters: one for the elements type, the other one for the type of the allocator used for managing storage allocation (which has a default value).
Consequently, you need to change the first template parameter so that it has two parameters:
template <template <typename,typename> class CollectionType, class ItemType>
class Test
{
public:
CollectionType<ItemType> m_collection;
};
But wait, that won't work either! Indeed, CollectionType awaits another parameter, the allocator... So now you have two solutions:
1 . Enforce the use of a particular allocator:
CollectionType<ItemType, std::allocator<ItemType> > m_collection
2 . Add a template parameter for the allocator to your class:
template <template <typename,typename> class CollectionType,
class ItemType,
class Allocator = std::allocator<ItemType> >
class Test
{
public:
CollectionType<ItemType, Allocator> m_collection;
};
So as you see, you end up with something rather complicated, which seems really twisted to deal with STL containers...
My advice: see Greg Rogers' answer for a better approach :)!
Why not do it like this?
template <class CollectionType>
class Test
{
public:
CollectionType m_collection;
};
Test t = Test<vector<int> >();
t.m_collection = vector<int>();
If you need the itemtype you can use CollectionType::value_type.
EDIT: in response to your question about creating a member function returning the value_type, you do it like this:
typename CollectionType::value_type foo();
You add the typename because CollectionType has not been bound to an actual type yet. So there isn't a value_type it could look up.
Comeau online likes this:
#include <vector>
template <template <class T, class A = std::allocator<T> > class CollectionType, class ItemType>
class Test
{
public:
CollectionType<ItemType> m_collection;
};
void foo()
{
using std::vector;
Test<vector,int> t = Test<vector, int>();
t.m_collection = vector<int>();
}