Avoiding code repetition in C++ (partial) template specialization - c++

I have a class Vector which represents a mathematical vector of a given template class. I want to be able to save the vectors in two different ways:
template <class T>
class Vector
{
private:
T* elements;
};
and
template <class T, unsigned int D>
class Vector
{
private:
T elements[D];
};
In the first example I allocate and free the array with new and delete in constructor and destructor.
Now since I don't want to write all the methods twice for both classes, and since it wouldn't even compile this way because I have two classes with the same name but different template arguments, I would like to combine both classes into one class like so:
template <class T, int D = -1>
class Vector
{
public:
Vector<T, D> add(const Vector<T, D>& add) const;
private:
T elements[D];
};
template <class T>
class Vector<T, -1>
{
public:
Vector<T, D> add(const Vector<T, D>& add) const;
private:
T* elements;
};
So the second part is just a partial template specialization of the first one. If no dimensions are given the dynamically allocated option should be used (default argument for D). As an example I added a function to calculate the sum of two vectors.
Now my problem is that I have to give two implementations for what is logically just one function. Whenever I access the elements array it's exactly the same syntax in the dynamic and in the static Vector class. Can I somehow combine both implementations into just one implementation of the add function (and likewise of all similar functions)?
If I cannot solve the problem this way, do you have other ideas of how to create the Vector class with both dynamic and static memory allocation?

I'd go for a policy-based design, similar to the way how std::vector handles allocation.
In your case it means:
Define a class which stores the vector elements but only provides a minimal interface. (Policy)
Define your vector interface independent of the way the elements are stored in the policy. It accesses the elements in a way independent of that implementation. The policy class should be added as a template type parameter (which can have a default value), so the user of your vector class can choose which policy to use. Inherit from the policy class or add a member of its type (privately if you don't want to expose the policy interface in the public interface).
Example (here with aggregation instead of inheritance):
// The policy default implementation:
template <class T, int D>
class VectorStorage
{
T elements[D];
public:
T& operator[](int x) {
return elements[x];
}
const T& operator[](int x) const {
return elements[x];
}
};
class VectorStorage<T, -1>
{
T* elements; // (for allocation, see below)
public:
T& operator[](int x) {
return elements[x];
}
const T& operator[](int x) const {
return elements[x];
}
};
// The vector implementation, independent of the storage,
// but defaulting to the one above:
template <class T, int D = -1, class Storage = VectorStorage<T,D>>
class Vector
{
Storage storage;
public:
Vector<T, D> add(const Vector<T, D>& add) const {
// Access your elements using "storage[x]"
}
};
Note that you need a suitable constructor for your policy class (as in the case of the dynamic storage type, you need the size during construction). Provide a unique constructor interface to all of your specializations, not only for the one which need it; and call the constructor in the vector's constructor appropriately:
// within class VectorStorage<T,-1>:
VectorStorage(int size) : elements(new T[size]) {}
~VectorStorage() { delete[] elements; }
// within class VectorStorage<T,D>:
VectorStorage(int /* ignored */) {}
// within class Vector:
Vector(int size) : storage(size) {}
Alternatively, to support client code like Vector<int,5> myVector; (i.e. default constructor on the static size version), provide a default constructor which is only allowed to be called for the static size version:
Vector() : storage(D) {
static_assert(D != -1, "The default constructor is only allowed for the static-sized version of Vector.");
}
Now a user can even use Vector with std::vector as the storage back-end: Vector<int, -1, std::vector<int>> or Vector<int, 5, std::vector<int>>. Or even Vector<int, 5, std::array<int,5>>.

I would do something like the following, i.e specialize only the data part:
template <class T, int D = -1>
class VectorData
{
public:
int size() const { return D; }
protected:
T elements[D];
};
template <class T>
class VectorData<T, -1>
{
public:
explicit VectorData(int size) : elements(size) {}
int size() const { return elements.size(); }
protected:
std::vector<T> elements;
};
template <class T, int D = -1>
class Vector : protected VectorData<T, D>
{
public:
using VectorData<T, D>::VectorData;
Vector add(const Vector& add) const
{
Vector res(*this);
for (int i = 0; i != this->size(); ++i) {
res.elements[i] += add.elements[i];
}
return res;
}
};

Related

Generic function to get item of any std::vector containing in a given class by index

If I have a class containing only std::vector of any type as data member and I would like to make a generic function allowing me to retrieve elements by index in any vector.
What can I do to create such a function. I tried to do it using pointers to my getters and it worked but I don't know if it is really a good solution.
Is there an example of such class
class S
{
private:
std::vector<int> someIntegers;
std::vector<double> someDoubles;
std::vector<double> someDoubles2;
std::vector<int> someIntergers2;
std::vector<B> someBs;
public:
const std::vector<int>& getSomeIntegers1() const { return someIntergers; }
// and so on ...
}
My solution is to do that:
template<typename T>
std::optional<T> getByIndex(const S& s, const std::vector<T>& (S::*getCnt)() const , size_t index = 0)
{
if (const auto& cnt = (s.*getCnt)();
index >= 0 && index < cnt.size())
return cnt.at(index);
return {};
}
I'm not sure if it's a great solution due to the lack of readability.
If you want to have a generic access that routes to individual members based on the template parameter, you must define the mapping manually. However, using the template argument as selector will not support cases with multiple member vectors of the same type, for example the someDoubles and someDoubles2.
Here is a sample code to retrieve the correct vector
template <class T>
std::vector<T>& getContainer();
template <>
std::vector<int>& getContainer() {
return someIntegers;
}
template <>
std::vector<double>& getContainer() {
return someDoubles;
}
// Do so for every type you want to support

Using the allocator for a structure fields of different types

I have a custom allocator which instances share a memory buffer of type TrivialSpace:
class TrivialSpace
{
public:
uint8_t * Allocate(std::size_t memory_size)
{
...
return ptr_to_mem;
}
...
};
template <class T>
class TrivialAllocator
{
public:
using value_type = T;
TrivialAllocator(TrivialSpace & space) : m_space(space)
{
}
template <class Q>
TrivialAllocator(const TrivialAllocator<Q> & other) : m_space(other.m_space)
{
}
T * allocate(std::size_t n)
{
return reinterpret_cast<T*>(m_space.Allocate(n * sizeof(T)));
}
void deallocate(T* p, std::size_t n)
{
}
private:
TrivialSpace & m_space;
template <class Q>
friend class TestAllocator;
};
and a structure containing std::string and std::vector:
using String = std::basic_string<char, std::char_traits<char>, TrivialAllocator<char>>;
template <class T>
using Vector = std::vector<T, TrivialAllocator<T>>;
struct A
{
String a;
Vector<int> b;
Vector<String> c;
};
1) what is the right way to create an instance of A structure my TrivialAllocator?
2) And how to make the code work with both TrivialAllocator and std::allocator? When the field types are defined as above or as follows (depending on some #ifdef probably):
using String = std::string;
template <class T>
using Vector = std::vector<T>;
My idea was to pass the instance of Allocator to the structure constructor, but it is not clear what specialization of Allocator should I pass - Allocator<char>, Allocator<int> or something else.
I would specifically take 3 allocators (for example, if you used a polymorphic allocator, you might want a specific one for each object).
You could also have an overloaded constructor that takes one allocator and constructs the other three from it (Only if each allocator is rebound from the other 2, which in this case or with a polymorphic allocator they are)
A(Allocator<char> a_alloc, Allocator<int> b_alloc, Allocator<String> c_alloc) : a(a_alloc), b(b_alloc), c(c_alloc) {}
template<class Alloc,
std::enable_if_t<
std::is_same_v<std::allocator_traits<Alloc>::template rebind_alloc<char>, Allocator<char>> &&
// Next 2 maybe redundant based on what you are doing
std::is_same_v<std::allocator_traits<Alloc>::template rebind_alloc<int>, Allocator<int>> &&
std::is_same_v<std::allocator_traits<Alloc>::template rebind_alloc<String>, Allocator<String>>,
int> = 0>
A(Alloc alloc) : A(Allocator<char>(alloc), Allocator<int>(alloc), Allocator<String>(alloc)) {}
Or you could pass the issue to the constructor of the class and move from a string and two vectors (Or leave it as an aggregate and initialize with allocators where the type is used)

c++: can i process std::_Container_base without knowing if it is a map or a vector?

guess I have a
class C1 : public B { /*...*/ };
class C2 : public B { /*...*/ };
std::map<std::string, C1> myMap;
std::vector<C2> myVector;
Is there a way (and what would be the syntax) to call a function foo that…
just needs to process the functionalities of B
just needs to process them on all elements of map and vector without caring how they are organized?
std::vector and std::map are both std::_Container_base's but i have no clue how to write the syntax for (pseudocode):
void foo(std::_Container_base-of-Bs)
EDIT: it's _Container_base, not _Tee
The C++ way is to use templates and iterators.
template <typename ForwardIterator>
void process_bs(ForwardIterator first, ForwardIterator last) {
std::for_each(first, last, [](B& b) {
// do something to b here
});
}
For vector, list, deque and set, you can trivially call this using begin and end:
process_bs(v.begin(), v.end());
For map, the element type is pair<const Key, Value>, so you have to adapt the iterators. You can use this with Boost.Range, for example:
#include <boost/range/adaptor/map.hpp>
auto values = m | boost::adaptors::map_values;
process_bs(values.begin(), values.end());
EDIT: The below is a survey on the workarounds, whereas the actual question is not answered therein. So here is the answer: I don't know whether one can process std::_Container_base without knowing if it is a map or a vector.
I couldn't find anything reasonable on the web regarding std::_Container_base, and particularly no C++ standard things, so I would guess it stems from a specific compiler implementation.
vector and map are completely different storage schemes. I suggest you to not use them generically in the same context. That is, from the first you could write a function template
template<typename T> foo(T&& t) { /* takes a vector and a map */ }
but at least when you access operator[], they'll behave differently. That would be unintuitive and error-prone.
However, this doesn't mean you cannot combine the two approaches -- and abstract on size(), operator[](int) and possibly other things like some insertion mechanism.
For example, in some recent code of mine, I have vector-storage scheme (which uses std::vector under the hood), as well as a piecewise constant vector (which uses a std::map). If you want to do this, you can derive those two from a common base class
template<typename T>
struct ContainerBase
{
virtual int size() const = 0;
virtual T operator[](int) const = 0;
virtual void insert(int, T) = 0; //if required
};
and then set up the required functionality in the derived classes Vector and Map.
template<typename T>
struct Vector
{
virtual T operator[](int i) const { return _v[i]; }
virtual T size() const { return _v.size(); }
// ... insert and so on
std::vector<T> _v;
};
template<typename T>
struct Map
{
virtual T operator[](int i) const
{
return *std::lower_bound(i); //add further checks if nothing is found
}
virtual T size() const { return _v.rbegin()->first; // return highest index }
// ... insert and so on
std::map<int, T> _v;
};
The Map implementation is just a sketch. You should choose some reasonable behaviour for it.
With this, it is easy to set up a function foo(ContainerBase&) which works for both Vector and Map.
To use transparently B subclasses into the foo function, you can do this way:
#include <iostream>
#include <map>
#include <vector>
#include <string>
#include <utility>
struct B{
int b_member;
};
class C1 : public B { /*...*/ };
class C2 : public B { /*...*/ };
std::map<std::string, C1> myMap;
std::vector<C2> myVector;
// all the magic is into get_B specializations
template<typename E, typename std::enable_if<std::is_base_of<B, E>::value>::type* a = nullptr>
B& get_B(E& elem)
{
return elem;
}
template<typename E, typename std::enable_if<std::is_base_of<B, typename E::second_type>::value>::type* a = nullptr>
B& get_B(E& elem)
{
return elem.second;
}
// foo can call get_B to hide implementation details of the container
template<typename T>
void foo( T& container)
{
for(auto& elem : container)
{
std::cout << get_B(elem).b_member << '\n';
}
}
int main()
{
myVector.resize(10);
myMap["one"] = {};
foo(myMap);
foo(myVector);
}
Thanks to SFINAE, foo uses the correct specialization of get_B to get a reference to the B subclass you want to process.

container of template class in c++

I have a template, which may be used with different stl map (map<int, int>, map<int, char>, etc.), as follows.
template <typename Map> struct TriMaps
{
Map& next;
Map& prev;
Map& curr;
};
so, TriMaps<init, int> intTriMaps; TriMaps<int, char> charTriMaps;
Then, is it possible to have a container of the above TriMaps, in different type? e.g.
vector <TriMaps> vecMaps;
which contain intTriMaps and charTriMaps? Usually container requires the same type. But I really need a container for my case. No boost or third library available.
First determine and finalize what this class template TriMaps is expected to do. In C++, you must initialize a reference to something. You probably need a non-reference type (or a pointer type). Consider:
template <typename Map>
struct TriMaps
{
Map next;
Map prev;
Map curr;
};
Usage:
TriMaps<int> IntMaps;
vector<TriMaps<int>> iv;
If you need two template type argument, you can do this way:
template <typename MapType1, typename MapType2>
struct TriMaps
{
MapType1 a;
Maptype2 b;
};
Usage:
TriMaps<int, float> IntMaps;
vector<TriMaps<int, float>> iv;
EDIT:
Carefully understand that following simple class will not compile.
class TriMap
{
int & ref_int;
};
One approach is to refer a global variable (just to make it compile), or take an int& via a constructor.
// Approach 1
int global;
class TriMap
{
int & ref_int;
public:
TriMap() : ref_int(global) {}
};
// Approach 2
class TriMap
{
int & ref_int;
public:
TriMap(int & outer) : ref_int(outer) {}
};
You can't have a std::vector of different type
but if you can have base class
struct TriMapBase { virtual ~TriMapBase() = default; };
template <typename Map> struct TriMaps : TriMapBase { /* Your implementation */ };
Then you may have:
TriMapBase<std::map<int, int>> mapint;
TriMapBase<std::map<int, char>> mapchar;
std::vector<TriMapBase*> v = {&mapint, &mapchar};
but you have to use virtual functions
or dynamic_cast to retrieve the correct type afterward...
or use

partial specialization of function in template class

I have a template class representing an array of numerical values.
I want this class to work for any type of numerical value (e.g. int, double, etc.) and three types of container (std::vector, std::deque, and std::list).
Here are the relevant bits of the implementation for my specific problem :
template < typename Numeric_t, typename Container = std::vector<Numeric_t> >
class Array {
// field member
Container m_data;
// other stuff here
// ...
// random element access for std::vector and std::deque
Numeric_t & operator[] (unsigned int index) { return m_data[index]; }
// random element access for std::list
Numeric_t & operator [] (unsigned int index) {
std::list<Numeric_t> :: iterator it = m_data.begin();
std::advance(it, index);
return *it;
}
}
Of course, the compiler doesn't allow me to overload the operator [].
What I would need is a kind of partial specialization for operator [] specific for std::list, but partial template function specialization is not allowed either in C++.
(I know that random element access is not efficient for a list, but that's not the point here).
Ideally, in the client code I would like to use the Array class like this :
Array < int, std::vector<int> > vec;
Array < int, std::list<int> > lst;
// fill arrays here
// ...
std::cout << vec[0] << std::endl;
std::cout << lst[0] << std::endl;
After lot of research I was not able to find a working solution.
What would be the most elegant way to solve this problem ?
Thanks for your help.
A clean solution is to use full-class template specialization. The different specializations can be derived form one common base class, in order to share common code.
Write a class ArrayBase containing all the code that does not depend on the particular container type and that grants access to the container, by making it protected or making Array a friend class.
template <class Numeric_t, class Container>
class Array
: public ArrayBase<Numeric_t, Container>
{
// Container specific code, generic version that works for all containers.
};
template <class Numeric_t>
class Array<Numeric_t, std::vector<Numeric_t>>
: public ArrayBase<Numeric_t, std::vector<Numeric_t>>
{
// Optimized code for std::vector.
}
Another approach: You can also write a static member function containing code to access the idx-th entry of the container and specialize that function:
template <class Numeric_t, class Container>
class Array
{
template <class Cont>
static Numeric_t get(Cont const& container, unsigned int idx)
{
std::list<Numeric_t>::iterator it = container.begin();
std::advance(it, idx);
return *it;
}
template <>
static Numeric_t get(std::vector<Numeric_t> const& container, unsigned int idx)
{
return container[idx];
}
public:
Numeric_t operator[](unsigned int idx) const { return get(m_data, idx); }
};
I am sorry, this does not work. I forgot that you can't specialize static member functions ... again.
Another alternative is to use SFINAE, but it is a non-idiomatic use of it and I would not recommend it in this case.