C++ Template Function Instantiation Code Size - c++

I'm trying to reduce the number of template function instantiations, but am running into a snag.
Suppose we have the following class (I know it's not optimized: this is done on purpose to illustrate the issue):
//class no_inherit is implemented the same way as class base (below).
//This is done to illustrate the issue I'm seeing.
template<typename T, size_t SIZE>
class no_inherit
{
private:
T m_data[SIZE];
const size_t m_size;
public:
no_inherit() :m_size(SIZE){}
T& operator[](size_t i)
{return m_data[i];}
inline size_t size() const
{return m_size;}
};
The following function:
template<typename T>
void huge_func(T& v)
{
//..do lots of stuff with v. For example
for(size_t i = 0; i < v.size(); ++i)
v[i] = v[i] + i;
//...do lots more with v
}
And the following code:
int main()
{
no_inherit<int, 4> v1;
no_inherit<int, 2> v2;
huge_func(v1);
huge_func(v2);
}
huge_func() would get instantiated twice:
void huge_func(no_inherit<int, 4>& v);
void huge_func(no_inherit<int, 2>& v);
Since huge_func() is, well, huge, I'm trying to reduce the instantiation count by taking one of the template parameters and turning it into a dynamic parameter by creating the following class hierarchy:
//Base class only has 1 template parameter.
template<typename T>
class base
{
private:
T *m_data;
const size_t m_size; //hold child's templated size parameter.
protected:
inline base(T* data, size_t size): m_data(data), m_size(size){}
public:
T& operator[](size_t i)
{return m_data[i];}
inline size_t size() const
{return m_size;}
};
//Child class has two template parameters
template<typename T, size_t SIZE>
class inherit: public base<T>
{
private:
T m_data[SIZE];
public:
//Pass template parameter to base class
inherit() : base<T>(m_data, SIZE){}
};
And I call huge_func() as follows:
int main()
{
inherit<int, 4> v1;
inherit<int, 2> v2;
//make sure only one instantiation of huge_func() is made
//by using the same type.
base<int> &v1b = v1;
base<int> &v2b = v2;
huge_func(v1b);
huge_func(v2b);
}
This would only instantiate a single huge_func() function:
void huge_func(base<int>& v);
And thus would decrease the code size.
But ALAS! The code size increases when I use the class hierarchy. How is this possible?
Even more bizzare, if I have the following code.
int main()
{
inherit<int, 4> v1;
inherit<int, 2> v2;
huge_func(v1);
huge_func(v2);
}
The code size is the same as calling huge_func(v1b) and huge_func(v2b).
What is the compiler doing?

First of all, if huge_func is indeed "huge", you likely would benefit from splitting it up into several reusable smaller functions.
That aside, you can also template-ize it:
template<typename T, int SIZE> void huge_func(no_inherit<T, SIZE>& v)
{
// function implementation goes here
}
Then you are implementing it once, and you maintain your flat class structure.

Related

Computing the linear index of a multi-dimensional array when using the curiously recurring template pattern (CRTP)

I am writing a multi-dimensional array class in C++ and would like to have static and dynamic memory versions of it. Based on an answer from one of my previous posts, I used CRTP to define a base class which then has access to the respective data containers (std::array and std::vector) with contiguous memory in the derived classes.
I wish to implement the multi-dimensional subscript operator (e.g. to access the (1, 2) entry of a 4x3 matrix whose 12 entries are stored contiguously in memory) in the base class itself so as to avoid code duplication. Note that I just picked the subscript operator as it is the simplest class method that illustrates what I wish to achieve. I have attempted to adapt the multi-dimensional subscript linearisation solution given here by #Jarod42 to my case with CRTP. However, I am unsure about how to store the array dimensions in the derived classes in order to infer them in the base class when computing the linearised index. I hope the following code demonstrates my issue clearly (I am compiling with C++20):
#include <array>
#include <cassert>
#include <vector>
/** Multi-dimensional Array Base Class with CRTP. */
template <class derived, class T>
class MultiDimArray
{
private:
constexpr derived& Derived() noexcept { return static_cast<derived&>(*this); }
constexpr const derived& Derived() const noexcept { return static_cast<const derived&>(*this); }
protected:
constexpr MultiDimArray() {}
public:
// I've butchered the syntax in the following subscript operators, but I hope it captures what I intend to do.
constexpr const T& operator()(HookTypeToPack<Derived().Dimensions, std::size_t> ...Is) const {
return *(this->Derived()[ComputeIndex<Derived().Dimensions...>(Is...)]);
}
constexpr T& operator()(HookTypeToPack<Derived().Dimensions, std::size_t> ...Is) {
return *(this->Derived()[ComputeIndex<Derived().Dimensions...>(Is...)]);
}
};
/** Static Multi-dimensional Array Class */
template <class T, std::size_t ...Dims>
class StaticMultiDimArray : public std::array<T, (Dims * ...)>,
public MultiDimArray<StaticMultiDimArray<T, (Dims * ...)>, T>
{
private:
constexpr static std::size_t nEntries = (Dims * ...);
constexpr static std::tuple<HookTypeToPack<Dims, std::size_t>...> Dimensions = std::make_tuple(Dims...);
friend MultiDimArray<StaticMultiDimArray<T, (Dims * ...)>, T>;
public:
constexpr StaticMultiDimArray() : std::array<T, nEntries>(InitStaticArray<T, nEntries>(0.0)) {}
};
/** Dynamic Multi-dimensional Array Class */
template <class T>
class DynamicMultiDimArray : public std::vector<T>, public MultiDimArray<DynamicMultiDimArray<T>, T>
{
private:
std::size_t nEntries;
std::tuple<...> Dimensions; // Not sure what to use here considering a tuple is immutable
friend MultiDimArray<DynamicMultiDimArray<T>, T>;
public:
DynamicMultiDimArray() : std::vector<T>() {}
template <typename ...Dims>
DynamicMultiDimArray(Dims ...dimensions) : std::vector<T>((dimensions * ...), 0.0) { Resize(dimensions...); }
template <typename ...Dims>
void Resize(Dims ...dimensions)
{
nEntries = (dimensions * ...);
this->resize(nEntries);
// store dimensions in Dimensions...
}
};
The support functions InitStaticArray, HookTypeToPack, and ComputeIndex used above are defined as follows:
template <class T, std::size_t size>
constexpr auto InitStaticArray(const T& value)
{
std::array<T, size> arr;
std::fill(arr.begin(), arr.end(), value);
return arr;
}
template <std::size_t, typename T>
using HookTypeToPack = T;
template <std::size_t ...Dims>
constexpr std::size_t ComputeIndex(HookTypeToPack<Dims, std::size_t> ...multi_index)
{
constexpr std::size_t dims_arr[] = {Dims...};
std::size_t multi_index_arr[] = {multi_index...};
std::size_t index(0), factor(1);
for(int i = 0; i < sizeof...(Dims); i++)
{
assert(0 <= multi_index_arr[i] && multi_index_arr[i] < dims_arr[i]);
index += factor * multi_index_arr[i];
factor *= dims_arr[i];
}
assert(0 <= index && index < (Dims * ...));
return index;
}
Can anyone give me some advice on how to store the array dimensions in StaticMultiDimArray and DynamicMultiDimArray so they can be appropriately invoked in the operator() overloads in MultiDimArray?
There are several options. One you are already hinting at: store the dimensions in the derived class. However, don't use a std::tuple; that allows every element to have a different type, while we just want the size of every dimension to be a std::size_t. Use a std::array for the static array, std::vector for the dynamic one:
template <class T, std::size_t ...Dims>
class StaticMultiDimArray :
public std::array<T, ...>,
public MultiDimArray<...>
{
constexpr static std::array dimensions{Dims...};
...
};
template <class T>
class DynamicMultiDimArray :
public std::vector<T>,
public MultiDimArray<...>
{
std::vector<std::size_t> dimensions;
...
};
You also don't need to make the constructor of DynamicMultiDimArray a template, instead you can have it take a std:initializer_list as an argument, like so:
DynamicMultiDimArray(std::initializer_list<std::size_t> dimensions) :
std::vector<T>(std::accumulate(dimensions.begin(), dimensions.end(), std::size_t{1}, std::multiplies<std::size_t>())),
Dimensions(dimensions) {}
Yeah, the initialization of the actual vector of elements looks very ugly now. I would recommend not using inheritance for this, but composition. Then you can initialize the dimensions first, then the array/vector of data, and can make a member variable in MultiDimArray that calculates the number of entries:
template <class derived, class T>
class MultiDimArray {
...
constexpr std::size_t nEntries() const {
const auto &dimensions = Derived().dimensions;
return std::accumulate(dimensions.begin(), dimensions.end(), std::size_t{1}, std::multiplies<std::size_t>());
}
...
};
template <class T>
class DynamicMultiDimArray :
public MultiDimArray<...>
{
std::vector<std::size_t> dimensions;
std::vector<T> data;
...
public:
DynamicMultiDimArray(std::initializer_list<std::size_t> dimensions) :
Dimensions(dimensions),
data(nEntries()) {}
};
Similarly, you can make operator() take a std::initializer_list<std::size_t> for the indices. Code using this looks like so:
DynamicMultiDimArray dimarr({3, 5, 7}); // create new array
...
std::cout << dimarr({1, 2, 3}) << '\n'; // print element at index 1, 2, 3
This avoids a lot of template hassle. You could even consider creating a custom multi-dimensional index type instead of a std::initializer_list<std::size_t>, making it easier to store indices in a variable and pass them around.

Overloading operator [] for N-dimensional structure

I made an N-dimensional structure with vectors and templates:
//----------------N-dimensional vector--------------------------------
template<int dim,typename T> class n_dim_vector {
public:
typedef std::vector<typename n_dim_vector<dim - 1, T>::vector> vector;
};
template<typename T> class n_dim_vector <0, T> {
public:
typedef T vector;
};
It can be instatiaated with different dimnsion-counts and is prt of a class that represent a search space.
template<int dim, typename T> class n_dim_ssc {
private:
typename n_dim_vector<dim, T>::vector searchspace;
};
My problem: I cannot get operator[] right to access searchspace properly, specifically the return type.
I tried:
template<typename V> std::vector<V>& operator[](unsigned i) {
return searchspace[i];
}
T& operator[](unsigned i) {
return searchspace[i];
}
at first, thinking the compiler would derive typename V as whatever type searchspace contained at all but the last level. Thats what T& operator[](unsigned i) was for.
But alas, doen't work this way. And I cannot work out how it would
EDIT Don't fear, I do not access empty memory, the structure is initialized and filled, I just didn't include the code for clarity's sake.
Also, I don't intend to access it with a single integer, I wanted to use searchspace[i][j]..[k]
The way to let compiler deduces the return type is auto:
In C++14:
auto operator[](unsigned i) { return searchspace[i]; }
In C++11:
auto operator[](unsigned i) -> decltype(searchspace[i]) { return searchspace[i]; }
I'm answering to your comment
Feel free to recommend something better, I'd appreciate it.
The following code shows one way to handle the multidimensional vector at once, i.e. non-recursively. It could be improved in several ways which I didn't consider for now (for instance, I wouldn't want to use and pass that many arrays but rather use variadic parameter lists. This however requires much more and more diffcult code, so I'll let it be.)
#include <numeric>
template<size_t Dim, typename T>
struct MultiDimVector
{
std::array<size_t, Dim> Ndim;
std::array<size_t, Dim> stride;
std::vector<T> container;
MultiDimVector(std::array<size_t, Dim> const& _Ndim) : Ndim(_Ndim), container(size())
{
stride[0] = 1;
for (size_t i = 1; i<Dim; ++i)
{
stride[i] = stride[i - 1] * Ndim[i - 1];
}
}
size_t size() const
{
return std::accumulate(Ndim.begin(), Ndim.end(), 1, std::multiplies<size_t>());
}
size_t get_index(std::array<size_t, Dim> const& indices) const
{
//here one could also use some STL algorithm ...
size_t ret = 0;
for (size_t i = 0; i<Dim; ++i)
{
ret += stride[i] * indices[i];
}
return ret;
}
T const& operator()(std::array<size_t, Dim> const& indices) const
{
return container[get_index(indices)];
}
};
You can use it like
MultiDimVector<3, double> v({ 3, 2, 5 }); //initialize vector of dimension 3x2x5
auto a = v({0,1,0}); //get element 0,1,0
But as I wrote, the curly brackets suck, so I'd rewrite the whole thing using variadic templates.
The problem with your approach is that you're not initializing any memory inside the vector and just trying to return non-existent memory spots. Something on the line of the following (WARNING: uncleaned and unrefactored code ahead):
#include <iostream>
#include <vector>
template<int dim,typename T> class n_dim_vector {
public:
typedef std::vector<typename n_dim_vector<dim - 1, T>::vector> vector;
};
template<typename T> class n_dim_vector <0, T> {
public:
typedef T vector;
};
template<int dim, typename T> class n_dim_ssc {
public:
typename n_dim_vector<dim, T>::vector searchspace;
n_dim_ssc() {}
n_dim_ssc(typename n_dim_vector<dim, T>::vector space) : searchspace(space) {}
n_dim_ssc<dim-1, T> operator[](std::size_t i) {
if(searchspace.size() < ++i)
searchspace.resize(i);
return n_dim_ssc<dim-1, T>(searchspace[--i]);
}
typename n_dim_vector<dim, T>::vector get() {
return searchspace;
}
};
template<typename T> class n_dim_ssc<0,T> {
public:
typename n_dim_vector<0, T>::vector searchspace;
n_dim_ssc() {}
n_dim_ssc(typename n_dim_vector<0, T>::vector space) : searchspace(space) {}
typename n_dim_vector<0, T>::vector get() {
return searchspace;
}
};
int main(int argc, char** argv) {
n_dim_ssc<0, int> ea;
int a = ea.get();
n_dim_ssc<1, int> ea2;
auto dd2 = ea2[0].get();
n_dim_ssc<2, int> ea3;
auto dd3 = ea3[0][0].get();
}
Try it out
will work with an accessor method (you can modify this as you want).
Anyway I strongly have to agree with Kerrek: a contiguous memory space accessed in a multi-dimensional array fashion will both prove to be faster and definitely more maintainable/easier to use and read.

Specialization of single template argument

Consider the following code:
/* aclass.h */
class AClass
{
public:
template<size_t N, class Vector>
void aMethod(const Vector &);
};
/* aclass.inl */
// method for any N
template<size_t N, class Vector>
void AClass::aMethod(const Vector &) { ... }
// method for N = 1
template<class Vector>
void AClass::aMethod<1>(const Vector &) { ... }
/* main.cpp */
int main()
{
AClass inst;
std::vector<float> data;
// calls method for any N
inst.aMethod<20>(data);
// calls method for N = 1
inst.aMethod<1>(data);
}
I can't seem to find the correct syntax for specializing a single template argument of integer type - and not even sure if it is legal. I looked around a bit, but didn't find anyone with this problem...
These are the errors i get (from msvc):
error C2244 : 'AClass::aMethod' : unable to match function definition to an existing declaration
error C2768 : 'AClass::aMethod' : illegal use of explicit template arguments
How would i go on about solving this problem?
There ain't no such thing as a partial specialization of a function template.
To achieve your goal, you could go through a helper class, which can be partially specialized:
class AClass;
template<size_t N, class Vector>
struct AMethodHelper {
static void Do(AClass* pThis, const Vector&) {
// General for N != 1
}
};
template<class Vector>
struct AMethodHelper<1, Vector> {
static void Do(AClass* pThis, const Vector&) {
// Special for N == 1
}
};
class AClass
{
public:
template<size_t N, class Vector>
void aMethod(const Vector & v) {
AMethodHelper<N, Vector>::Do(this, v);
}
};
Or, you could use two overloads selected with SFINAE:
class AClass
{
public:
template<size_t N, class Vector>
typename enable_if<N!=1>::type aMethod(const Vector &) {
// General for N != 1
}
template<size_t N, class Vector>
typename enable_if<N==1>::type aMethod(const Vector &) {
// Special for N == 1
}
};
Or, I suppose, you could just have one method with if (N == 1) inside. I suspect any decent compiler would be smart enough to eliminate dead code in any given instantiation.

C++ - specialising member function template via templated functor does not compile

I wish to create a class that can convert between arrays of floats and doubles polymorphically. That is, the instance concerned (parameterised by <double> or <float>) and the decision to pass a float* or double* is decided at runtime, not statically.
As a proposed answer to another question, but modified according to this answer (because I understand it's not possible to fully specialise a member function template inside a class), a pure virtual base class BaseDest that provides simple overloaded member functions is sub-classed to define DestImpl<T>. I use this base class to maintain a dynamic collection of DestImpl<T> instances, with varying T. This class provides explicit overloads of the assign() member function; one for a double *, and another for a float *. The idea is that at run-time, BaseDest::assign() is called via a polymorphic pointer or reference, and this in turn calls the correct virtual assign() member function in DestImpl<T>.
Now, it is important that then the non-pointer type of the array matches T in DestImpl<T>, that a fast_copy() function is called (perhaps a memcpy), and when the types do not match a slower statically-cast-item-by-item copy is performed. So the assign() member function offloads this to a templated functor. There are two specialisations for this functor - one where the type parameter of the functor matches the type of DestImpl<T> (and therefore invokes a fast copy), and a fall-back one that catches all other cases (and invokes a slow copy).
However, I am unable to get the following code to compile. The comments show where the compiler error and warning appear - I suspect they are related. What I don't understand is why the second specialisation of apply_helper is unable to be instantiated as apply_helper<double>.
class BaseDest {
public:
virtual ~BaseDest() {}
virtual void assign(const double * v, size_t cnt) = 0;
virtual void assign(const float * v, size_t cnt) = 0;
};
template <typename T>
class DestImpl : public BaseDest {
public:
void assign(const double * v, size_t cnt) {
assign_helper<T>()(v, cnt);
}
void assign(const float * v, size_t cnt) {
assign_helper<T>()(v, cnt); // ERROR: no matching function for call to object of type 'assign_helper<double>'
}
protected:
template <typename U>
struct assign_helper {
void operator()(const U * v, size_t cnt) {
for (size_t i = 0; i < cnt; ++i) {
//slow_copy(v[i]);
}
}
};
template <typename U>
struct assign_helper<T> { // WARNING: Class template partial specialization contains a template parameter that can not be deduced; this partial specialization will never be used
void operator()(const T * v, size_t cnt) {
//fast_copy(v, cnt);
}
};
};
void test() {
DestImpl<double> d; // error mentioned above appears when this is present
}
EDIT: here's something that does seem to work - moving the assign_helper struct (now a class) out of the DestImpl<T> class definition. I'm not sure this is the right way to do it, but it does seem to work so far:
// slow copy between different types
template <typename T, typename U>
class assign_helper {
public:
void operator()(const U *v, size_t cnt) {
// slow copy
}
};
// fast copy between same types
template <typename T>
class assign_helper<T, T> {
public:
void operator()(const T * v, size_t cnt) {
// fast copy
}
};
class BaseDest {
public:
virtual ~BaseDest() {}
virtual void assign(const double * v, size_t cnt) = 0;
virtual void assign(const float * v, size_t cnt) = 0;
};
template <typename T>
class DestImpl : public BaseDest {
public:
virtual void assign(const double * v, size_t cnt) {
assign_helper<T, double>()(v, cnt);
}
virtual void assign(const float * v, size_t cnt) {
assign_helper<T, float>()(v, cnt);
}
};
template <typename U>
struct assign_helper<T> { // WARNING: Class template partial specialization contains a template parameter that can not be deduced; this partial specialization will never be used
The above is the cause of your error. The warning explicitly tells you that this definition will never be used. What you want instead of template < typename U > is template <>.

Casting templated class to more general specialization

I have a templated class with the template argument the number of dimensions of some datapoints the class shall save. This class has a specialized version MyClass<-1> that allows for dimensions not known at compile time.
How can I cast a specific class (say MyClass<2>) to this more general form?
To be a bit more concrete, here is some artificial example that shows the situation. (I use the Eigen library, but I suppose for the general principle this should not matter)
using namespace Eigen;
template <std::size_t dim>
class MyClass {
public:
// Some constructors...
// A sample function:
Matrix<double, dim, 1> returnPoint();
// Some more functions here
private:
Matrix<double, dim, 1> point;
}
Now, suppose I have the following code segment:
MyClass<2> *foo;
MyClass<Dynamic> *bar; // Dynamic is a Eigen constant, being defined as -1
// Do something here
// How to do this:
bar = some_cast<MyClass<Dynamic> *>(foo);
Thinking about the problem I suppose what I want is impossible to archive without actually copying the values of point. Anybody able to prove me wrong or confirm this assumption?
It is possible to achieve the casting without actually copying the values but only if you have been careful make it work.
When you instantiate a class template with two different sets of arguments you get two distinct classes that are not related. Unless you specifically define one to inherit from the other, for example:
namespace with_inheritance {
template <class T, long sz>
class vector : public vector<T,-1> {
typedef vector<T,-1> base_t;
public:
vector() : base_t (sz) { }
};
template <class T>
class vector<T, -1> {
T* v_;
size_t sz_;
public:
vector(size_t sz) : v_ (new T[sz]), sz_ (sz) { }
~vector() { delete [] v_; }
T& operator[](size_t i)
{
if (i >= sz_) throw i;
return v_[i];
}
};
} // with_inheritance
So in this case you can cast as in:
namespace wi = with_inheritance;
wi::vector<double, 10> v;
wi::vector<double, -1>* p = &v;
std::cout << (*p)[1] << '\n';
Without the inheritance relationship casting between them will not be permitted. You can, however, use reinterpret_cast to get around the type system when you want to. But you have be very careful that the objects have identical layout and invariants to make sure everthing will work ok. As in:
namespace with_lots_of_care {
template <class T, long sz>
class vector {
T* v_;
size_t sz_;
public:
vector() : v_ (new T[sz]), sz_ (sz) { }
~vector() { delete [] v_; }
T& operator[](size_t i)
{
if (i >= sz_) throw i;
return v_[i];
}
};
template <class T>
class vector<T, -1> {
T* v_;
size_t sz_;
public:
vector(size_t sz) : v_ (new T[sz]), sz_ (sz) { }
~vector() { delete [] v_; }
T& operator[](size_t i)
{
if (i >= sz_) throw i;
return v_[i];
}
};
} // with_lots_of_care
And then cast as in:
namespace wc = with_lots_of_care;
wc::vector<double, 10> v;
wc::vector<double, -1>* p = reinterpret_cast<wc::vector<double, -1>*>(&v);
std::cout << (*p)[1] << '\n';