Typedef in features compared to typedef in class

I am looking at Eigen source code for educational purposes. I noticed that for every particular class X template in the hierarchy, there is internal::traits<X> . A typical example can be found in Matrix.h:

 namespace internal { template<typename _Scalar, int _Rows, int _Cols, int _Options, int _MaxRows, int _MaxCols> struct traits<Matrix<_Scalar, _Rows, _Cols, _Options, _MaxRows, _MaxCols> > { typedef _Scalar Scalar; typedef Dense StorageKind; typedef DenseIndex Index; typedef MatrixXpr XprKind; enum { RowsAtCompileTime = _Rows, ColsAtCompileTime = _Cols, MaxRowsAtCompileTime = _MaxRows, MaxColsAtCompileTime = _MaxCols, Flags = compute_matrix_flags<_Scalar, _Rows, _Cols, _Options, _MaxRows, _MaxCols>::ret, CoeffReadCost = NumTraits<Scalar>::ReadCost, Options = _Options, InnerStrideAtCompileTime = 1, OuterStrideAtCompileTime = (Options&RowMajor) ? ColsAtCompileTime : RowsAtCompileTime }; }; } 

Now I understand the features as a way to extend existing classes that you don't want to modify, with additional information related to some piece of new code. For example, a user of the class template Foo<class TAllocator> might want to use existing FastAlloc and AlignedAlloc memory FastAlloc , but Foo needs to know how to interact with the two, and as such FooTraits<AlignedAlloc>::allocate() and FooTraits<FastAlloc>::allocate() defined by the user, which in turn is used by Foo .

However, in this case, I do not see a problem with just specifying Scalar in each derived class, i.e. has a Matrix define Matrix::Scalar using typedef in the body of the class. What is the advantage of using a feature class? This is just to keep the code clean, i.e. Save all the relevant properties of each class in a feature class?

Edit according to Nicol Bolas answer: I understand that some of these typedefs may need to be kept "internal", that is, they should not be exposed to the user, which explains the feature class. This makes sense, however, some of these typedefs, such as Scalar , are available to the outside world through a typedef in the base Matrix class:

 template<typename Derived> class MatrixBase : public DenseBase<Derived> { public: typedef MatrixBase StorageBaseType; typedef typename internal::traits<Derived>::StorageKind StorageKind; typedef typename internal::traits<Derived>::Index Index; typedef typename internal::traits<Derived>::Scalar Scalar; typedef typename internal::packet_traits<Scalar>::type PacketScalar; typedef typename NumTraits<Scalar>::Real RealScalar; 

This brings us back to the original question: why isn't Scalar just a typedef in Matrix itself? Is there any reason besides the stylistic choice?

+8
c ++ templates traits eigen
source share
2 answers

I suspect that since the feature class is internal , this is the point of use for the feature class. That is, to keep these things internal. Thus, Matrix does not have a lot of oddball definitions, etc., even in its private interface.

Consider the listing in your example. These "enums" variables (aka: static constexpr before C ++ 11) are not like anything the user needs to know. This is an implementation detail, and therefore it must be hidden.


MatrixBase Problem - CRTP problem.

See, Matrix will be defined as follows:

 class Matrix : public MatrixBase<Matrix> 

This partial definition causes 2 things:

  • If Matrix is not yet declared as a class type, it becomes a legal class, the name of which can be referenced and used.

  • MatrixBase template must be created with type Matrix . Right now.

The problem here is that right now, Matrix is an incomplete class. The compiler has not yet been included in this definition, so the compiler does not know anything about its internal components. But MatrixBase must be created right now.

Therefore, MatrixBase cannot use any of the contents of the Derived class that it provides. If Matrix has some typedef in it, MatrixBase<Derived> cannot see it.

Now, the MatrixBase<Derived> member functions can search for definitions in Derived because they are defined after the definition of the full class. Even if these functions are defined within the class.

But you cannot have MatrixBase access property properties for Derived . Hence the indirectness of the devil. A feature class can use an incomplete type specialization to define MatrixBase for expansion.

+5
source share

The main reason for this traits class is to avoid recursive dependencies in CRTP . Without this, we get something like:

 template <typename T> struct Matrix : Base<Matrix<T>> { typedef T Scalar; }; template <typename Derived> struct Base { typename Derived::Scalar foo(); }; 

which cannot be compiled in some cases. Basically, this traits class allows you to fully declare Base<Matrix> without knowing the Matrix declaration.

+3
source share

All Articles