Understanding template code bloat - c++

I'm reading Scott Meyers C++ and came across with the concept of so-called code bloat. He provided an example of how to reduce it with inheritance:
template <typename T>
class SquareMatrixBase{
protected:
void invert(std::size_t matrixSize); // <------------ HERE
}
template <typename T, std::size_t n>
class SquareMatrix : private SquareMatrixBase<T>{
private:
using SquareMatrix<T>::invert;
public:
void invert(){ invert(n) }
}
Now, in the summary of the item he stated that
Templates generate multiple classes and multiple functions, so any
template code not dependent on a template parameter causes bloat.
So, in the example we have SquareMatrixBase<T>::invert(std::size_t) which is not dependent on the template parameter. So, it causes code bloat. Isn't that the thing we tried to eliminate? What did I miss?

So, in the example we have SquareMatrixBase<T>::invert(std::size_t) which is not dependent on the template parameter. So, it causes code bloat. Isn't that the thing we tried to eliminate? What did I miss?
No. You missed that the operations invert performs on the matrix will be dependent on T, so it's appropriate to have one instantiation of invert for each T.
It is not desirable to have one instantiation for every combination of type T and matrix size n, which is why the derived SquareMatrix class - that is templated on both those parameters - defers to the base class' implementation. It's effectively just injecting the matrix size constant as a runtime value.

As the SquareMatrix template is parameterized on two things T and n, then even though you might have the same type T, if you were to have a different n and invert was a member function of SquareMatrix, it results in multiple versions of the function (a number depending on how many SquareMatrix's of the same T but different sizes we instantiate).
Scott is demonstrating how we can avoid the potential bloat caused by these multiple copies, by simply inheriting from a type that is only parameterized on T and so can share an implementation of invert regardless of the value of n.
He passes the size value at runtime to the base class function and so all the same information is available to that function.

Related

Using Concepts to create static polymorphic interface

Hello Stackoverflow community,
I've been really confused on the concepts syntax and am having a hard time getting started.
I would like to create a polymorphic interface for two types of operator types: unary and binary and opted to try out the concept feature in c++20.
Not sure if it matters, but I used a CRTP create my unary functor compatible with binary functors, however I would like to get rid of that. Here's what I have so far:
template <typename T>
concept UnaryMatrixOperatable = requires(T _op) {
_op.template operate(std::unique_ptr<Matrix::Representation>{});
{_op.template operate() } -> same_as<std::unique_ptr<Matrix::Representation>>;
};
class ReLU : public UnaryAdapter<ReLU> {
public:
std::unique_ptr<Matrix::Representation> operate(
const std::unique_ptr<Matrix::Representation>& m);
};
static_assert(UnaryMatrixOperatable<ReLU>);
However, I am getting a compilation error, presumably because I am not doing some sort of template specialization for a const matrix & type?
include/m_algorithms.h:122:13: error: static_assert failed
static_assert(UnaryMatrixOperatable<ReLU>);
^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/m_algorithms.h:122:27: note: because 'Matrix::Operations::Unary::ReLU' does not satisfy 'UnaryMatrixOperatable'
static_assert(UnaryMatrixOperatable<ReLU>);
^
include/m_algorithms.h:53:26: note: because '_op.template operate(std::unique_ptr<Matrix::Representation>{})' would be invalid: 'operate' following the 'template' keyword does not refer to a template
_op.template operate(std::unique_ptr<Matrix::Representation>{});
^
Thanks for all the help in advance, this design in my code has been problematic for over a week so I'm determined to find a clean way to fix it! Thanks.
Concepts are not base classes, and you should not treat concept requirements like base class interfaces. Base classes specify exact function signatures that derived classes must implement.
Concepts specify behavior that must be provided. So you explain what that behavior is.
The behavior you seem to want is that you can pass an rvalue of a unique pointer to an operate member function. So... say that.
template <typename T>
concept UnaryMatrixOperatable = requires(T _op, std::unique_ptr<Matrix::Representation> mtx)
{
_op.operate(std::move(mtx));
};
There's no need for template here because you do not care if operate is a template function. It's not important in the slightest to your code if any particular T happens to implement operate as a template function or not. You're going to call it this way, so the user must specify some function interface that can be called a such.
The same goes for the zero-argument version. Though your interface should probably make it much more clear that you're moving from the unique pointer in question:
template <typename T>
concept UnaryMatrixOperatable = requires(T _op, std::unique_ptr<Matrix::Representation> mtx)
{
_op.operate(std::move(mtx));
{ std::move(_op).operate() } -> std::same_as<decltype(mtx)>;
};
In any case, the other reason you'll get a compile error is that your interface requires two functions: one that gets called with an object and one that does not. Your ReLu class only provides one function that pretends to do both.

How to access the template argument's own template arguments?

Say you have a class template (e.g. second below). Say that class template's template arguments are instantiations of another class template (e.g. first below). How do you access the template argument's own template arguments? Is that possible?
Here's some code:
template<size_t n>
class first {
};
template<typename instantiated_first>
class second {
// would like to be able to access the "n"
// of instantiated_first
};
int main() {
second<first<3>> thing;
return 0;
}
Three possibilities come to mind:
rewrite first to store n as a data member, or
redesign second to inherit from an instantiated class from first, and
ask for the exact same template arguments in both class templates.
I ask because I would rather not edit pre-existing code (option 1), and in my particular application, it doesn't really make sense (to me, at least) to think of second as being a type of first (option 2). I will be instantiating many objects of type specialized_first, and so it makes more sense for second to "own" these objects and store them in a container. Option 3 doesn't seem very stylish, either.
Are there any fancy template features that I don't know about to accomplish this in a nice way?
The more sensible way is not to store n as a data member but rather as static constexpr size_t so it doesn't allocate any additional runtime space. This is the standard and most flexible mechanism - allowing for various other templates to access this parameter without mush fuss. E.g., you can make a completely different version of first and allow your second to utilize the other version all the while supporting the original as long as all version satisfy the concept interface.
Similarly, every typename T_ template parameter can be forwarded to users of the template via using T = T_;. In general, template parameters are inaccessible unless you allow them to be by declaring them to be types or storing their value as constexpr or otherwise.
You can remodel second so it accepts the same template parameter but it becomes harder the more other templates you have that use first and this value/type or when you suddenly want to use second for an alternative version of first. Better address such problems on the root. For example, in std all their template classes have all the necessary types declared inside the class in a similar manner.
You declare a partial specialization and pick the arguments from there.
template<typename T>
class second<first<T>> {
// T is available here for use.
};
Or even more generally do this
template<typename T, typename U
class second<U<T>> {
// T is available here for use.
};
This will match all instantiations with one template argument.

Avoiding proliferation of templates

I am working on a fairly tightly coupled library which up until now has explicitly assumed all computations are done with doubles. I'm in the process of converting some of the core classes to templates so that we can start computing with std::complex<double>. I've templated about 10 of our classes so far have noticed a tendency toward proliferation of templates. As one class becomes templated, any other class that uses the templated class appears to need templating as well. I think I can avoid some of this proliferation by defining abstract base classes for my templates so that other classes can just use pointers to the abstract class and then refer to either a double or std::complex<double> version of the derived class. This seems to work on at the header level, but when I dive into the source files, the templated class will often have functions which compute a value or container of values of type double or std::complex<double>. It seems like a waste to template a whole class just because a couple of lines in the source file are different because of some other classes return type.
The use of auto seems like a possible way to fix this, but I'm not 100% sure it would work. Suppose I have an abstract base class AbstractFunction from which Function<Scalar> derives, where Scalar can be double or std::complex<double>. Now suppose we have two member functions:
virtual Scalar Function<Scalar>::value(double x);
virtual void Function<Scalar>::values(std::vector<Scalar> &values, std::vector<double> x);
And suppose I have some other class (that I don't want to template) with a member function that calls one of these.
// populate double x and std::vector<double> xs
auto value = functionPtr->value(x);
std::vector<auto> values;
functionPtr->values(values, xs);
// do something with value and values
where functionPtr is of type std::shared_ptr<AbstractFunction>.
I could see auto working for the first case, but I don't believe I could construct a vector of auto to be filled with the second one. Does this necessitate making the calling class a template? Can someone recommend another strategy to cut down on the proliferation of templates?
I think you are already wrong in assuming that the first use-case is going to work. If you have an abstract base class, then either value is a member of it and you can call it through std::shared_ptr<AbstractFunction> or value is not a member of it and only available if you know the derived class' type. In the first case, the AbstractFunction::value method must have a fixed return type, it can not depend on Scalar, which is the template parameter of the derived class.
That said: In my experience the two concept often don't mix well. You either want to create an abstract base class with the full interface or you want a template. In the latter case, there is often no need / no benefit for having an abstract base class. It then follows that also the code using your template works with templates.
What might help you is to "export" the template parameter from Function, i.e.
template<typename T>
class Function
{
public:
using value_type = T;
value_type value() const;
// ...
};
and in other parts of the code, use a template which takes any T which behaves like Function if you don't want to directly write out (and limit yourself) to Function:
template<typename T>
void something( const std::shared_ptr<T>& functionPtr )
{
// ignoring where x comes from...
using V = typename T::value_type;
V value = functionPtr->value(x);
std::vector<V> values;
functionPtr->values(values, xs);
}
Note that this is just one option, I don't know if it is the best option for your use-case.

Template class with no use of template argument

I have stumbled many times on classes defined like
class PureVirtualClass
{
virtual int foo() = 0;
virtual bool bar() = 0;
}
template <class T> class ImplClass : public virtual PureVirtualClass
{
virtual ~ImplClass(){};
int foo() { return 42;}
bool bar() { return true;}
//several other method having nothing to do with T
}
This "design" appears so often I want to think the original developer knew what he was doing by defining ImplClass as template class but without any reference to the template argument T anywhere. My own c++ template knowledge is kinda limited.
Is there a benefit to this or is it just a confused programmer?
There can be a benefit for classes being templated but not depending on the argument. Most often you see such things to define (empty) tag-structures for template metaprogramming:
template <class X>
struct some_tag {};
The benefit of classes like yours in general is that while you have the same functionality in each class, they are different classes and you can't copy one into the other, i.e. an object of type ImplClass<int> is not compatible with another object of type ImplCalss<float>.
There are many useful cases of the idea mentioned by Arne. For instance, looking at Very basic tuple implementation, this is how a single tuple element is defined:
template <size_t N, typename T>
class TupleElem
{
T elem;
public:
T& get() { return elem; }
const T& get() const { return elem; }
};
It is templated on N, without depending on it. Why? Because the tuple implementation
template <size_t... N, typename... T>
class TupleImpl <sizes <N...>, T...> : TupleElem <N, T>...
{
//..
};
derives multiple such elements, each with a unique N, serving as an identifier. Without it, TupleImpl would be deriving the same class twice, had two element types been identical within parameter pack T.... Neither random access to elements would work in this case (via an explicit call of function get() of the appropriate TupleElem base class, which would be ambiguous), nor empty base optimization (via specializing TupleElem for empty types T to not have a data member of type T).
This is a real use case, and exactly how std::tuple is implemented by clang. Of course, a class like TupleElem would be a hidden implementation detail, and not part of the interface. For instance, gcc follows an entirely different recursive class design.
In general, you will need to study the context where classes are used to understand the intent of the designer.
maybe that developer simply is too lazy to split the classes into .h and .cpp files?
Without using templates, linker errors would occur if the classes are used in multiple compilations units. When using templates, the linker usually discards duplicate instantiations of a template at link time (or handles the problem in a different way).
While this may be an answer to "why did the developer do this", I would not recommend this if the question was "when should I introduce template arguments which are never used" (see the other answers for this). Even though it is annoying to split code into .h and .cpp (especially when used to languages like Java or C#), it's the usual C++ way. And it is definitely easier to read/understand than using templates only for this purpose. Also, it makes the use of the classes less readable.

Inline calls to non-inline base class functions (what does that mean exactly)?

I'm reading through a C++ book and I'm in a section about reducing the object code generated by templates (Effective C++ III By Scott Meyers). One of the examples it gives is:
template <typename T, std::size_t n>
class SquareMatrix: private SquareMatrixBase<T> {
public:
SquareMatrix()
: SquareMatrixBase<T>(n, 0),
pData(new T[n*n])
{ this->setDataPtr(pData.get()); }
... functions ...
private:
boost::scoped_array<T> pData;
};
where the base class, SquareMatrixBase has a function called:
void invert(std::size_t matrixSize);
"The book moves on to say "Regardless of where the data is stored, the
key result from a bloat point of view is that now many -- maybe all --
of SquareMatrix's member functions can be simple inline calls to
non-inline base class versions that are shared with all other matrices
holding the same type of data, regardless of their size."
What does it mean by "inline calls to non-inline base class versions..."? If its an inline call I would have thought it would have put the whole base class version of any function into the place where inline was used, but this would result in the same code bloat I would have thought. It says this like it's a benefit against code-bloat though.
If you need more background information let me know, the chapters long though and I tried pretty hard to provide background information but I might have missed something.
||EDIT - Additional Information||
The purpose of having Square Matrix and Square matrix base in this passage was:
SquareMatrix was originally a standalone template (not derived). It contained a series of functions which did operations based on the template parameter n's value. So, there was essentially a copy of every function for every value of n used (or for every pair of n, T used) as a new template with those functions was instantiated for each parameter pair. SquareMatrixBase was created to move the functions dependent on the size parameter to the base class. Since the base class is only instantiated with a type parameter (and not the size) the functions in the base class can be called by passing in the value for size the derived class passes into the base class constructor. This means there is only one version of the functions for each typename T passed into the SquareMatrix template regardless of the std::size_t n passed in (as opposed to one version of each function for each combination of {T, n}.
The point is that SquareMatrix::invert() is inlined, so the function doesn't even appear in the resulting code, and instead the protected base function SquareMatrixBase::invert(n) is called directly.
Now since that function is not inlined, there is only one single instance of that function (for each type T), rather than one copy for each size n. This stands in stark contrast to the single-class design, where one invert() function would be instantiated for each value of n.
It means that (many of) the functions in SquareMatrix class will be simple inline functions; and these inline functions will consist of a non-inline call to the base-class function that implements the functionality. For example, SquareMatrix may have an invert() member, implemented inline as:
void invert() {SquareMatrixBase<T>::invert(n);}
Calling this should generate exactly the same code as a direct call to SquareMatrixBase::invert(n), but with the convenience that the value of n is provided as a compile-time constant by the template, saving both the calling code and the implementation from having to keep track of it as a run-time variable.
It means the derived classes would contain inline functions, that would call the (heavy) non-inline functions in the base class doing actual work. The two variants compared are:
template <typename T, std::size_t n>
class SquareMatrix {
...
void invert();
...
};
template <typename T, std::size_t n>
void SquareMatrix::invert() {
... heavy code ...
}
and
template <typename T, std::size_t n>
class SquareMatrix : SquareMatrixBase<T> {
...
void invert() { SquareMatrixBase<T>::invert(pData.get(), n); }
...
};
template <typename T>
void SquareMatrixBase::invert(T* data, int n){
... heavy code ...
}
Now the second one emits the heavy code once per T, whereas the first one emits the heavy code once per T and n. That means more (pretty much identical) code in the second case.
Note that using inheritance for this trick is not mandatory, and I personally would use just free functions (maybe in a private namespace) instead.
Edit: the inlining is basically replacement of the function call by the function body (beta reduction, ha!). From
my_matrix.invert()
you get eg.
SquareMatrixBase<float>::invert(my_matrix.pData.get(), 3); <-- pseudocode
in the calling code.
Without reading the passage, it's a little hard to say exactly what's going on here, but if SquareMatrix is a templated "wrapper" class around a template base-class SquareMatrixBase, and the purpose of SquareMatrix is to basically pass the appropriate template parameters to the base-class, as well as provide a couple non-static data-members for the functions of the base-class to operate on, then you can, since the member-functions of SquareMatrix will basically be simple functions that are just calling the functions defined in the base-class, inline all the member functions for SquareMatrix. The base-class SquareMatrixBase on the other-hand will have all the "heavy-lifting" functions that are not inlined, but are rather contained in another compiled code-module.
For instance, you mentioned that SquareMatrixBase had a function called:
void SquareMatrixBase<T>::invert(std::size_t matrixSize);
In SquareMatrix you could simply call this function like:
template<typename T, std::size_t n>
inline void SquareMatrix<T, std::size_t n>::invert() { SquareMatrixBase<T>::invert(n); }
The inlined code version, even though it may be instantiated by many different types and sizes, will compile to a single function call ... the "heavy" base-class function will only need to be instantiated for each different type, not for every permutation of each various size of that type's matrix may need to be instantiated with ... so that will definitely cut down on the code bloat. So SquareMatrixBase<T> will only need to be instantiated for types such as int, double, etc. It contains all the "major" functions that use up a lot of code. The SquareMatrix<T, std::size_t n> on the other-hand class can be instantiated for not only int, but for int and size {1, 2, 3, 4, ...}. Each different template argument for the size of the matrix will instantiate a new version of SquareMatrix<T, std::size_t n>, but will only use a single instantiation for an int version of SquareMatrixBase<T>.