I'm trying to get familiar with C++ templates. I need to write a template of function that concatenates 2 arrays:
template<typename T, int Size>
class Array
{
public:
void push(int i, const T& t) { _elem[i] = t; }
private:
T _elem[Size];
};
For example I have 2 arrays:
Array<int,3> a1;
Array<int,4> a2;
I don't know how to write this function, that will return
Array<int,7>.
How should header of this function look like?
You should try it like this:
template<typename T, int A, int B>
Array<T, A+B> concatenate(Array<T, A> first, Array<T, B> second)
{
Array<T, A+B> result;
for (int idx = 0; idx < A; ++idx) {
result.push(idx, first[idx]);
}
for (int idx = 0; idx < B; ++idx) {
result.push(idx+A, second[idx]);
}
return result;
}
You could do it like this, as a free-function outside the class:
template <typename T, int SizeA, int SizeB>
Array<T, SizeA + SizeB> join(const Array<T, SizeA>& first,
const Array<T, SizeB>& second)
{
/* ... */
}
For what it's worth, you should probably use std::size_t from <cstddef> instead of int.
Related
i am trying to change the data type of all the elements into a nested C array, something like this.
const int a[2][3] = {
{1,2,3},
{4,5,6}
}
The arrays are "multidimensional", and i don't know how many dimension they have.
I figured out something like this:
template <class D, class T, unsigned S>
inline D& uniform(T (&t)[S]) {
D v[S];
for (int k = 0; k < S; k++) {
v[k] = D(t[k]);
}
return v;
}
auto b = uniform<float>( a );
However the previous code works (or at least it is supposed to work) only if a is 1D, is there a way to make it work over multidimensional C arrays?
So, here's one way of doing it:
#include <algorithm>
#include <array>
#include <cstddef>
#include <cstdio>
#include <type_traits>
// array_type_swap convert T[a][b][c][...] to Y[a][b][c][...] for arbitrary
// dimensions
template <class T, class Y>
struct array_type_swap {
using type = T;
};
template <class T, class Y, std::size_t N>
struct array_type_swap<T, std::array<Y, N>> {
using type = std::array<typename array_type_swap<T, Y>::type, N>;
};
template <class T, class Y, std::size_t N>
struct array_type_swap<T, Y[N]> {
using type = typename array_type_swap<T, std::array<Y, N>>::type;
};
template <class T, class Y>
using array_type_swap_t = typename array_type_swap<T, Y>::type;
template <class>
inline constexpr bool is_std_array_v = false;
template <class T, std::size_t N>
inline constexpr bool is_std_array_v<std::array<T, N>> = true;
// Get element count of a std::array, or C style array as constexpr. The value
// is 1 for all other types (as in array of 1).
template <class T>
struct contexpr_array_size {
static std::size_t constexpr size = 1;
};
template <class T, std::size_t N>
struct contexpr_array_size<T[N]> {
static std::size_t constexpr size = N;
};
template <class T, std::size_t N>
struct contexpr_array_size<std::array<T, N>> {
static std::size_t constexpr size = N;
};
template <class T>
inline auto constexpr contexpr_array_size_v = contexpr_array_size<T>::size;
template <class T, class Y>
void copy_array(T& dst, Y const& src) {
static_assert(contexpr_array_size_v<T> == contexpr_array_size_v<Y>,
"Can only copy arrays of the same size");
if constexpr (!is_std_array_v<Y> && !std::is_array_v<Y>)
dst = static_cast<T>(src);
else
for (std::size_t i = 0; i < contexpr_array_size_v<Y>; ++i)
copy_array(dst[i], src[i]);
}
template <class T, class Y>
auto uniform(Y const& arr) {
array_type_swap_t<T, Y> result;
copy_array(result, arr);
return result;
}
int main() {
std::puts("built-in array :");
const int a[2][3] = {{1, 2, 3}, {4, 5, 6}};
auto b = uniform<float>(a);
for (auto& row : b) {
for (auto& elm : row) std::printf("%.2f ", elm);
std::putchar('\n');
}
std::puts("\nstd::array :");
std::array<std::array<int, 3>, 2> stdarr = {{{6, 5, 4}, {3, 2, 1}}};
b = uniform<float>(stdarr);
for (auto& row : b) {
for (auto& elm : row) std::printf("%.2f ", elm);
std::putchar('\n');
}
}
You can't return [] arrays from a function, so the 1D version doesn't work.
You can use std::array.
template <class D, class T, std::size_t S1, std::size_t S2>
inline std::array<std::array<D, S1>, S2> uniform(std::array<std::array<T, S1>, S2> t) {
std::array<std::array<D, S1>, S2> v;
std::array<D, S1>(*inner)(std::array<T, S1>) = uniform<D, T, S1>;
std::transform(t.begin(), t.end(), v.begin(), inner);
return v;
}
template <class D, class T, std::size_t S>
inline std::array<D, S> uniform(std::array<T, S> t) {
return { t.begin(), t.end() };
}
I am just learning about templates and was trying to write a simple template class for vectors like
template <unsigned N>
struct vec {
std::array<float, N> m_buffer;
float& operator[](unsigned index) { return m_buffer[index]; }
vec(float value) { std::fill(m_buffer.begin(), m_buffer.end(), value); }
vec(float(&value)[N]) { std::copy(std::begin(value), std::end(value), std::begin(m_buffer)); }
vec(float* value) {
for (int i = 0; i < N; i++)
m_buffer[i] = value[i];
}
float getmag() {
float ret=0;
for (int i = 0; i < N; i++)
ret += m_buffer[N] * m_buffer[N];
return sqrt(ret);
}
};
int main() {
vec<3> a({ 1.0f, 2.0f, 3.0f });
return 0;
}
now I want to make a contructor that will take N number of floats and assign them to the array m_buffer but did not find any ways.
This could be done for certain values of N by specializing the template class but how will one do for any number of N?
You might use variadic template:
template <unsigned N>
struct vec {
std::array<float, N> m_buffer;
template <typename ... Ts>
vec(Ts... args) : m_buffer{{args...}} {}
// ...
};
SFINAE might be added to check arity and to restrict to float convertible parameters.
Alternative which might be appropriate is usage of index_sequence:
template <typename T, std::size_t>
using always_t = T;
template <typename Sequence> struct vec_seq;
template <std::size_t ... Is>
struct vec_seq<std::index_sequence<Is...>>
{
static constexpr std::size_t N = sizeof...(Is);
std::array<float, N> m_buffer;
vec_seq(always<float, Iss... args) : m_buffer{{args...}} {}
// ...
};
template <std::size_t N>
using vec = vec_seq<std::make_index_sequence<N>>;
In order to read and store some results from a MATLAB program, I need to use up to 6 dimensional matrices. Instead of doing something like:
typedef std::vector<double> Row;
typedef std::vector<Row> Matrix2;
typedef std::vector<Matrix2> Matrix3;
typedef std::vector<Matrix3> Matrix4;
typedef std::vector<Matrix4> Matrix5;
typedef std::vector<Matrix5> Matrix6;
I decided to go with templates, and here's what I have so far:
template <class T, int N>
class Matrix {
public:
typedef typename Matrix<T, N - 1>::type MatrixOneDimLower;
typedef std::vector<MatrixOneDimLower> type;
type _data;
template <unsigned int dn, typename ...NT>
Matrix(unsigned int dn, NT ...drest) : _data(dn, MatrixOneDimLower(drest)) {}
MatrixOneDimLower& operator[](unsigned int index)
{
return _data[index];
}
};
template <class T>
class Matrix<T, 1> {
public:
typedef std::vector<T> type;
type _data;
Matrix(unsigned int d0) : _data(d0, T(0.0)) {}
T& operator[](unsigned int index)
{
return _data[index];
}
};
Unfortunately, I'm not very adept in variadic templates and recursive templates, and this doesn't work. For example, if I try to use this as:
Matrix<double, 4> temp(n, dim[2], dim[1], dim[0]);
I get this compile time error (Visual Studio 2017):
error C2661: 'Matrix<double,4>::Matrix': no overloaded function takes 4 arguments
I would really appreciate if you can let me know what I'm doing wrong.
template<class T, std::size_t I>
struct MatrixView {
MatrixView<T, I-1> operator[](std::size_t i) {
return {ptr + i* *strides, strides+1};
}
MatrixView( T* p, std::size_t const* stride ):ptr(p), strides(stride) {}
private:
T* ptr = 0;
std::size_t const* strides = 0;
};
template<class T>
struct MatrixView<T, 1> {
T& operator[](std::size_t i) {
return ptr[i];
}
MatrixView( T* p, std::size_t const* stride ):ptr(p) {}
private:
T* ptr = 0;
};
template<class T, std::size_t N>
struct Matrix {
Matrix( std::array<std::size_t, N> sizes ) {
std::size_t accumulated = 1;
for (std::size_t i = 1; i < sizes.size(); ++i) {
accumulated *= sizes[N-i];
strides[N-i] = accumulated;
}
storage.resize( strides[0] * sizes[0] );
}
MatrixView<T, N> get() { return {storage.data(), strides.data()}; }
MatrixView<T const, N> get() const { return {storage.data(), strides.data()}; }
private:
std::vector<T> storage;
std::array<std::size_t, N-1> strides;
};
this requires doing Matrix<int, 6> m{ {5,4,2,1,3,5} }; to create a matrix with 6 dimensions.
To access it you need to do m.get()[3][0][0][0][0][0] = 4.
You get get rid of that .get() but it is a bit annoying so long as you want to support tensors of first order.
The data is stored contiguously.
I am implementing my static multi-dimentional vector class. I am using std::array as the underlying data type.
template <typename T, std::size_t N>
class Vector {
private:
std::array<T, N> data;
};
I want to make my class downwards-compatible, so I am writing this:
template <typename T, std::size_t N>
class Vector : public Vector<T, N-1>{
private:
std::array<T, N> data;
};
template <typename T>
class Vector<T, 0> {};
My goal is that when one instance is used in downwards-compatible mode, its underlying data should be able to be reliably accessed:
template<typename T, std::size_t N>
T& Vector<T, N>::operator[](int i) {
// Do boundary checking here
return this->data[i];
}
void foo(Vector<int, 3>& arg) {
arg[1] = 10;
}
Vector<int, 5> b;
foo(b);
// Now b[1] should be 10
There are two points here:
Vector<T, 5> should be accepted by foo(), Vector<T, 2> should be rejected.
Changes to b[0] through b[2] in foo() should pertain. b[3] and b[4] should not be accessible in foo().
How can I achieve that?
How about a simple read wrapper around std::array<> itself?
template<typename T, std::size_t N>
struct ArrayReader {
public:
// Intentionally implicit.
template<std::size_t SRC_LEN>
ArrayReader(std::array<T, SRC_LEN> const& src)
: data_(src.data()) {
static_assert(SRC_LEN >= N);
}
private:
T const* data_;
};
void foo(ArrayReader<float, 3>);
void bar() {
std::array<float, 4> a;
std::array<float, 2> b;
foo(a);
foo(b); //BOOM!
}
Of course, you can easily substitute std::array for your own type, this is just an example of the principle.
Have array keep the data, and then create additional non-owning class, e.g. array_view that will keep only a pointer. It will have generic constructor that accepts the array, and will have a static_assert to check the sizes.
Here's how I would approach this:
template <class Container, std::size_t size>
struct range_view
{
range_view(Container * p): container(p) { assert(size <= p->size()); }
auto & operator[](std::size_t i) { return (*container)[i]; }
private:
Container * container;
};
Then you simply define foo as:
template <class C>
void foo(range_view<C, 3> c)
{
c[1] = 1;
}
Here's something that is closest to what I think you would need.
Make Vector a viewer/user of the data, not the owner of the data.
#include <array>
template <typename T, std::size_t N, std::size_t I>
class Vector : public Vector<T, N, I-1>
{
public:
Vector(std::array<T, N>& arr) : Vector<T, N, I-1>(arr), arr_(arr) {}
T& operator[](int i) {
return arr_[i];
}
private:
std::array<T, N>& arr_;
};
template <typename T, std::size_t N>
class Vector<T, N, 0ul>
{
public:
Vector(std::array<T, N>& arr) : arr_(arr) {}
private:
std::array<T, N>& arr_;
};
void foo(Vector<int, 5, 3>& arg) {
arg[1] = 10;
// Can't find a way to make this a compile time error.
arg[3] = 10;
}
#include <iostream>
int main()
{
std::array<int, 5> arr;
Vector<int, 5, 5> b(arr);
foo(b);
std::cout << b[1] << std::endl;
}
Here's a demonstration of how to implement the Vector class that you tried in your question. At each level you only store 1 value instead of an array and that way when you compose all your N Arrays together you get space for N values. Of course then calling operator[] gets tricky, which is the meat of what I wanted to demonstrate.
#include <utility>
template <class T, std::size_t N>
struct Array : Array<T, N-1>
{
T & operator[](std::size_t i)
{
return const_cast<T&>((*const_cast<const Array*>(this))[i]);
}
const T & operator[](std::size_t i) const
{
return Get(i, std::make_index_sequence<N>());
}
template <std::size_t i>
const T & Geti() const
{
return static_cast<const Array<T, i+1>&>(*this).GetValue();
}
const T & GetValue() const { return value; }
template <std::size_t ... indices>
const T & Get(std::size_t i, std::integer_sequence<std::size_t, indices...>) const
{
using X = decltype(&Array::Geti<0>);
X getters[] = { &Array::Geti<indices>... };
return (this->*getters[i])();
}
template <std::size_t i, class = typename std::enable_if<(i <= N)>::type>
operator Array<T, i>&() { return (Array<T, i>&)*this; }
private:
T value;
};
template <class T>
struct Array<T, 0>{};
void foo(Array<float, 3> & a) { a[1] = 10; }
int main()
{
Array<float, 10> a;
foo(a);
}
I have a Matrix class template as follows:
template<typename T, std::size_t nrows, std::size_t ncols>
class Matrix
{
T data[nrows][ncols];
public:
T& operator ()(std::size_t i, std::size_t j)
{
return data[i][j];
}
};
What I want is to define a .setIdentity() function only for instantiations when nrows==ncols is true at compile time. And there will be no definition of .setIdentity() when nrows==ncols is false.
What I am trying is using enable_if idiom, but that will define the function for all cases. Isn't it?
You can do it with std::enable_if in the following mode
template <std::size_t r = nrows, std::size_t c = ncols>
typename std::enable_if<r == c>::type setIdentity ()
{ /* do something */ }
A full example
#include <type_traits>
template<typename T, std::size_t nrows, std::size_t ncols>
class Matrix
{
T data[nrows][ncols];
public:
T& operator ()(std::size_t i, std::size_t j)
{ return data[i][j]; }
template <std::size_t r = nrows, std::size_t c = ncols>
typename std::enable_if<r == c>::type setIdentity ()
{ /* do something */ }
};
int main()
{
Matrix<int, 3, 3> mi3;
Matrix<int, 3, 2> mnoi;
mi3.setIdentity();
// mnoi.setIdentity(); error
return 0;
}
--- EDIT ---
As pointed in a comment by Niall (regarding the TemplateRex's answer, but my solution suffer from the same defect) this solution can be circonvented expliciting the number of rows and columns in this way
mi3.setIdentity<4, 4>();
(but this isn't a real problem (IMHO) because mi3 is a square matrix and setIdentity() could work with real dimensions (nrows and ncols)) or even with
mnoi.setIdentity<4, 4>()
(and this is a big problem (IMHO) because mnoi isn't a square matrix).
Obviously there is the solution proposed by Niall (add a static_assert; something like
template <std::size_t r = nrows, std::size_t c = ncols>
typename std::enable_if<r == c>::type setIdentity ()
{
static_assert(r == nrows && c == ncols, "no square matrix");
/* do something else */
}
or something similar) but I propose to add the same check in std::enable_if.
I mean
template <std::size_t r = nrows, std::size_t c = ncols>
typename std::enable_if< (r == c)
&& (r == nrows)
&& (c == ncols)>::type setIdentity ()
{ /* do something */ }
The lazy and needlessly repetitive way
Just add a partial specialization:
template<typename T, std::size_t N>
class Matrix<T, N, N>
{
T data[N][N];
public:
T& operator ()(std::size_t i, std::size_t j)
{
return data[i][j];
}
void setidentity(/*whatever params*/) { std::cout << "yay!"; }
};
Live Example
For general N * M matrices, the general template will be instantiated, whereas only for N * N matrics, this specialization is a better match.
Disadvantage: code repetition of all regular code. Could use a base class, but it's actually easier to do some SFINAE magic (below)
A slightly harder but more economical way
You can also use SFINAE by adding hidden template parameters N and M that default to nrows and ncols to setidentity, and to enable_if on the condition N == M.
template<typename T, std::size_t nrows, std::size_t ncols>
class Matrix
{
T data[nrows][ncols];
public:
T& operator ()(std::size_t i, std::size_t j)
{
return data[i][j];
}
template <std::size_t N = nrows, std::size_t M = ncols, std::enable_if_t<(N == M)>* = nullptr>
void setidentity(/*whatever params*/) {
static_assert(N == nrows && M == ncols, "invalid");
std::cout << "yay!";
}
};
Or, since the question was tagged C++11, use typename std::enable_if<(N == M)>::type instead.
Live Example
Use a pseudo-CRTP to add modular support for something.
template<class T, std::size_t nrows, std::size_t ncols>
class Matrix;
template<class T, std::size_t size>
struct MatrixDiagonalSupport {
auto self() { return static_cast<Matrix<T, size, size>*>(this); }
auto self() const { return static_cast<Matrix<T, size, size> const*>(this); }
void setIdentity() {
for (std::size_t i = 0; i < size; ++i) {
for (std::size_t j = 0; j < i; ++j) {
(*self())(i,j) = {};
}
(*self())(i,i) = 1; // hope T supports this!
for (std::size_t j = i+1; j < size; ++j) {
(*self())(i,j) = {};
}
}
}
};
template<class T>
struct empty_t {};
template<bool b, class T>
using maybe= std::conditional_t<b, T, empty_t<T>>;
template<typename T, std::size_t nrows, std::size_t ncols>
class Matrix: public maybe<nrows==ncols,MatrixDiagonalSupport<T, nrows>>
{
// ...
Here we inherit from nothing if we aren't diagonal, and a class implementing set identity if we are diagonal.
Users of Matrix get .setIdentity() from its parent magically if it is right.
static_cast inside self() ends up being a zero-cost abstraction and giving the base class access to the child class.
This is pseudo-CRTP because we don't actually pass the derived class type to the parent, just enough information for the parent to reconstruct it.
This solution makes the method an actual method, and avoids any kind of SFINAE trickery.
Live example
In C++11 replace conditional_t<?> with typename conditional<?>::type:
template<bool b, class T>
using maybe=typename std::conditional<b, T, empty_t<T>>::type;
and everything should compile.
A basic, but simple solution not mentioned by any other answer: you can use std::conditional and inheritance.
It follows a minimal, working example:
#include<type_traits>
#include<cstddef>
struct HasSetIdentity {
void setIdentity() { }
};
struct HasNotSetIdentity {};
template<typename T, std::size_t nrows, std::size_t ncols>
class Matrix: public std::conditional<(nrows==ncols), HasSetIdentity, HasNotSetIdentity>::type
{
T data[nrows][ncols];
public:
T& operator ()(std::size_t i, std::size_t j)
{
return data[i][j];
}
};
int main() {
Matrix<int, 2,2> m1;
m1.setIdentity();
Matrix<int, 2,3> m2;
// Method not available
// m2.setIdentity();
}
You can still move data down the hierarchy if you need them to be shared by all the subobjects.
It mostly depends on the real problem.
skypjack and max66 have both presented simple answers to the problem. This is just an alternate way of doing it, using simple inheritance, although it means the use of a child class for square matrices:
template<typename T, std::size_t nrows, std::size_t ncols>
class Matrix
{
protected:
T data[nrows][ncols];
public:
T& operator ()(std::size_t i, std::size_t j)
{
return data[i][j];
}
};
template<typename T, std::size_t N>
class SqMatrix : public Matrix <T, N, N>
{
public:
setIdentity()
{
//Do whatever
}
}