How to check if template argument is a given value? - c++

I cant figure out what this line should be:
class Mtx : public std::conditional<M==2 && N == 1, _Vec2<T>,
std::conditional<N == 1 && M == 3, _Vec3<T>, _MtxMN<T, M, N>>>
"M==2 && N == 1" is not valid code here I think.
Is what I am trying to do possible? If M=2 and N=1 I want to inherit from _Vec2, If M=3 and N=1 I want to inherit from _Vec3, otherwise from _MtxMN
This is so I can write code:
Vec2u v;
v.x=1;
Larger code snippet below:
template <typename T, std::size_t M, std::size_t N>
struct _MtxMN {
public:
std::array<T, M*N> v_;
};
template <typename T>
struct _Vec2 {
union {
struct { T x_, y_; };
struct { T width_, height_; };
struct { T cols_, rows_; };
std::array<T, 2> v_;
};
};
template <typename T>
struct _Vec3 {
union {
struct { T x_, y_, z_; };
struct { T width_, height_, depth_; };
std::array<T, 3> v_;
};
};
// M rows N columns
template <typename T, std::size_t M, std::size_t N>
class Mtx : public std::conditional<constexpr(M==2 && N == 1), _Vec2<T>, std::conditional<N == 1 && M == 3, _Vec3<T>, _MtxMN<T, M, N>>>
{
...
}

There are some syntax errors in your Mtx declaration. It should be
class Mtx : public std::conditional<M==2 && N == 1, _Vec2<T>, typename std::conditional<N == 1 && M == 3, _Vec3<T>, _MtxMN<T, M, N>>::type>::type
The underlying type of std::conditional needs to be refered with ::type, and for the second std::conditional we need to add a typename before it since it's a dependant name.
Also removed the constexpr() part, that's not needed and doesn't compile on gcc or clang.

An easily readable implementation (in my eyes) could go with partial template specialization:
template <typename T, std::size_t M, std::size_t N>
class Mtx : public _MtxMN<T, M, N>
{};
template <typename T>
class Mtx<T, 2, 1> : public _Vec2<T>
{};
template <typename T>
class Mtx<T, 3, 1> : public _Vec3<T>
{};
When the compiler looks for matches of your template instantiation, it will enforce the desired inheritance structure.
Mtx<int, 3, 6> mtx1; // instantiation that inherits from _MtxMN
Mtx<int, 2, 1> mtx2; // instantiation that inherits from _Vec2
Mtx<int, 3, 1> mtx3; // instantiation that inherits from _Vec3

Related

Hide empty base class for aggregate initialization

Consider the following code:
struct A
{
// No data members
//...
};
template<typename T, size_t N>
struct B : A
{
T data[N];
}
This is how you have to initialize B: B<int, 3> b = { {}, {1, 2, 3} };
I want to avoid the unnecessary empty {} for the base class.
There is a solution proposed by Jarod42 here, however, it doesn't work with elements default initialization: B<int, 3> b = {1, 2, 3}; is fine but B<int, 3> b = {1}; is not: b.data[1] and b.data[2] aren't default initialized to 0, and a compiler error occurs.
Is there any way (or there will be with c++20) to "hide" base class from construction?
The easiest solution is to add a variadic constructor:
struct A { };
template<typename T, std::size_t N>
struct B : A {
template<class... Ts, typename = std::enable_if_t<
(std::is_convertible_v<Ts, T> && ...)>>
B(Ts&&... args) : data{std::forward<Ts>(args)...} {}
T data[N];
};
void foo() {
B<int, 3> b1 = {1, 2, 3};
B<int, 3> b2 = {1};
}
If you provide fewer elements in the {...} initializer list than N, the remaining elements in the array data will be value-initialized as by T().
Since C++20 you could use designated initializers in aggregate initialization.
B<int, 3> b = { .data {1} }; // initialize b.data with {1},
// b.data[0] is 1, b.data[1] and b.data[2] would be 0
Still with constructor, you might do something like:
template<typename T, size_t N>
struct B : A
{
public:
constexpr B() : data{} {}
template <typename ... Ts,
std::enable_if_t<(sizeof...(Ts) != 0 && sizeof...(Ts) < N)
|| !std::is_same_v<B, std::decay_t<T>>, int> = 0>
constexpr B(T&& arg, Ts&&... args) : data{std::forward<T>(arg), std::forward<Ts>(args)...}
{}
T data[N];
};
Demo
SFINAE is done mainly to avoid to create pseudo copy constructor B(B&).
You would need extra private tag to support B<std::index_sequence<0, 1>, 42> ;-)
I've found another solution that (I don't know how) works perfectly and solves the problem we were discussing under Evg's answer
struct A {};
template<typename T, size_t N>
struct B_data
{
T data[N];
};
template<typename T, size_t N>
struct B : B_data<T, N>, A
{
// ...
};

enable_if on constructor

I have the following code. I want to templatize class and class constructor on enum type. However, this code does not work? How can I achieve what I want?
#include < iostream >
#include < type_traits >
enum class MyType
{
Positive,
Negative
};
template < MyType T >
struct B {
int val = 0;
template<typename U = T>
B(int n, typename std::enable_if<U==MyType::Positive>::type* = 0) : val(n) { };
template<typename U = T>
B(int n, typename std::enable_if<U==MyType::Negative>::type* = 0) : val(-n) { };
};
int main() {
B<MyType::Positive> y(10);
B<MyType::Negative> n(10);
}
Your template has a typename parameter, but you want your enum as parameter. Let's fix that:
#include <iostream>
#include <type_traits>
enum class MyType
{
Positive,
Negative
};
template <MyType T>
struct B {
int val = 0;
template<MyType U = T>
B(int n, typename std::enable_if<U==MyType::Positive>::type* = 0) : val(n) { };
template<MyType U = T>
B(int n, typename std::enable_if<U==MyType::Negative>::type* = 0) : val(-n) { };
};
int main() {
B<MyType::Positive> y(10);
B<MyType::Negative> n(10);
}
Also, you can put the SFINAE expression inside the template parameters to unclutter the constructor parameters:
template<MyType U = T, typename std::enable_if<U == MyType::Positive, int>::type = 0>
B(int n) : val(n) { };
template<MyType U = T, typename std::enable_if<U == MyType::Negative, int>::type = 0>
B(int n) : val(-n) { };
Your problem is that T is a non-type template parameter, so you cannot do typename U = T because you want U, a type template parameter, to default to T, which is a value from MyType.
The name T is very poorly chosen, which is probably why you made this mistake in the first place. Change typename U = T by MyType U = T and your code will compile.
In C++20, it would even be simpler with requires:
enum class MyType
{
Positive,
Negative
};
template <MyType E>
struct B
{
int val = 0;
B(int n) requires(E == MyType::Positive) : val(n) {}
B(int n) requires(E == MyType::Negative) : val(-n) {}
};

How to specialize a template for 2 different values?

Given the code below, I was wondering if it is possible to have a specialization for a set of values. In my example, I want to create an specialization for N=3 or N=4 to use an array of known size. Is it possible to avoid code duplication in this case?
template <typename T, unsigned int N>
class A
{
public:
T* data;
};
template <typename T>
class A<T, 3>
{
public:
T data[3];
};
template <typename T>
class A<T, 4>
{
public:
T data[4];
};
int main()
{
A<int, 1> u;
std::cout << sizeof(u.data) << std::endl; // Size of pointer
A<int, 3> v;
std::cout << sizeof(v.data) << std::endl; // Size of data
A<int, 4> w;
std::cout << sizeof(w.data) << std::endl; // Size of data
return 0;
}
You can use std::enable_if by introducing a default void template parameter in the general case.
template <typename T, unsigned int N, typename = void>
class A
{
public:
T* data;
};
template <typename T, unsigned int N>
class A<T, N, typename std::enable_if<N == 3 || N == 4>::type>
{
public:
T data[N];
};
live wandbox example
If N == 3 || N == 4 is true, then typename std::enable_if<N == 3 || N == 4>::type is well-formed and evaluates to void. The specialization is then chosen.
If N == 3 || N == 4 is false, then typename std::enable_if<N == 3 || N == 4>::type is ill-formed and the specialization is "SFINAEd away". The general case is then chosen.

Defining conversion operator for specialized template class only

I want to define conversion to float for matrix<1, 1>. I have trouble figuring out how to actually define it. If I make it a global function
template<typename T>
inline operator T(const matrix<T, 1, 1> &m){ return m(0, 0); }
I get "operator.. must be a non static member function"
I can of course define it as member for the generic matrix template but then it will be defined for all matrices - which is not what I want. I want it to be defined only for the specific case of 1x1 matrix.
You have to specialize a class for that, for example:
template <typename Base, typename T, std::size_t W, std::size_t H>
struct MatrixConversion
{ /*Empty*/ };
template <typename Base, typename T> struct MatrixConversion<T, 1u, 1u>
{
operator const T&() const { return static_cast<const Base&>(*this).m[0][0]; }
};
template <typename T, std::size_t W, std::size_t H>
struct Matrix : MatrixConversion<Matrix<T, W, H>, T, W, H>
{
// Your code
};
composition plus specialisation would be the most maintainable approach.
You did not specify the number of dimensions in your matrix template class, so I have assumed it can be variadic.
#include <cstdint>
#include <utility>
//
// forward-declare class template for convenience.
//
template<class T, std::size_t...Dimensions>
struct matrix;
//
// classes to figure out the storage requirements of a multi-dimensional
// matrix
//
template<class T, std::size_t...Dimensions> struct storage;
template<class T, std::size_t N>
struct storage<T, N>
{
using type = T[N];
};
template<class T, std::size_t...Rest, std::size_t N>
struct storage<T, N, Rest...>
{
using less_dimension_type = typename storage<T, Rest...>::type;
using type = less_dimension_type[N];
};
//
// functions for dereferencing multi-dimensional arrays
//
template<class Array, class Arg>
decltype(auto) deref(Array& array, Arg&& arg)
{
return array[arg];
}
template<class Array, class Arg, class Arg2>
decltype(auto) deref(Array& array, Arg&& arg, Arg2&& arg2)
{
return array[arg][arg2];
}
template<class Array, class Arg, class...Args>
decltype(auto) deref(Array& array, Arg&& arg, Args&&...args)
{
return deref(deref(array, arg), std::forward<Args>(args)...);
}
//
// prototype for operations we want to conditionally apply
//
template<class Matrix>
struct matrix_conditional_ops
{
// in the general case, none
};
//
// compose the matrix class from conditional_ops<>
//
template<class T, std::size_t...Dimensions>
struct matrix
: matrix_conditional_ops<matrix<T, Dimensions...>>
{
template<class...Dims>
decltype(auto) at(Dims&&...ds)
{
return deref(_data, std::forward<Dims>(ds)...);
}
template<class...Dims>
decltype(auto) at(Dims&&...ds) const
{
return deref(_data, std::forward<Dims>(ds)...);
}
typename storage<T, Dimensions...>::type _data;
};
//
// define the condition operations for the <T, 1, 1> case
//
template<class T>
struct matrix_conditional_ops<matrix<T, 1, 1>>
{
using matrix_type = matrix<T, 1, 1>;
operator T const() { return static_cast<matrix_type const&>(*this).at(0,0); }
};
int main()
{
matrix<double, 1, 1> m11;
m11.at(0,0) = 6.0;
double d = m11;
matrix<double, 2, 2> m22;
// compile error:
// double d2 = m22;
// bonus points:
matrix<double, 3, 5, 2, 7> mxx;
mxx.at(2, 4, 1, 6) = 4.3; // probably needs some compile-time checking...
}
someone may want to check my logic for the array packing/dereferencing...
Jarod and Richard already gave you the best answers in my opinion, they scale well to any number of operators with all kinds of restrictions.
However, if you cannot afford to redesign your class, or all you need is a quick and dirty opertor T() you can get away with the following
template<typename T, std::size_t N1, std::size_t N2>
struct Matrix
{
T m[N1][N1];
operator T()
{
static_assert(N1 == 1 && N2 == 1, "Only applicable to scalars");
return m[0][0];
}
};
Which is live here.

Typecasting from class to integral type

In order to deal with vectors and fixed/dynamic allocation in some linear algebra problems, I built the following classes (which I would prefer not to modify ):
// Traits : n is the size of the vector, if -1 : dynamic allocation
template<typename RealType, int n = -1>
struct ClassAVectorTraits
{
typedef typename LinearAlgebraLibrary::FixedSizeVector<RealType, n> type;
};
template<typename RealType>
struct ClassAVectorTraits<T, -1>
{
typedef typename LinearAlgebraLibrary::DynamicSizeVector<RealType> type;
};
template<typename RealType>
struct ClassAVectorTraits<T, 1>
{
typedef typename RealType type;
};
// Implementation
template<typename RealType, int n = -1>
struct ClassA
{
typedef typename ClassAVectorTraits<RealType, n>::type vector;
void doSomething( const vector& vec )
{
...
}
};
Moreover I have an interface class (which I can modify) :
template<typename RealType, int n = -1>
struct UserClass
{
typedef typename ClassAVectorTraits<RealType, n>::type vector;
ClassA ca;
void doSomething( const vector& vec )
{
ca.doSomething( vec );
}
};
Now I want the user to be able to give STL vectors in input instead of LinearAlgebraLibrary vectors, so I did the following :
// A class to do automatic conversion
template<typename RealType, int n = -1>
struct Field : public ClassAVectorTraits<RealType,n>::type
{
// Constructor NOT explicit
Field( const std::vector<RealType>& vec )
{
// Copy vec into this (a LinearAlgebraLibrary vector)
}
}
template<typename RealType>
struct Field<RealType, 1> { }; // Can't derive from integral type : RealType !
// And to classes to tag the solution
template<typename RealType, int n>
struct USE_LINEAR_ALGEBRA_LIBRARY
{
typedef typename ClassAVectorTraits<RealType,n>::type vector;
};
template<typename RealType, int n>
struct USE_STL
{
typedef typename Field<RealType,n> vector;
};
And finally :
template<typename RealType, int n = -1, class VectorTag = USE_LINEAR_ALGEBRA_LIBRARY<RealType, n> >
struct UserClass
{
typedef typename VectorTag::vector vector;
ClassA ca;
void doSomething( const vector& vec )
{
ca.doSomething( vec );
}
};
And therefore, if we use the USE_STL tag, there is an automatic conversion :
UserClass<double, 2, USE_STL<double, 2> > userClass;
std::vector<double> vecSTL(2, 12.0);
userClass.doSomething( vecSTL ); // vecSTL is converted to LinearAlgebraLibrary::FixedSizeVector<double, 2>
My question : how can I deal with the n=1 case in which the vector type is an integral type. How can I define an implicit conversion between STL vector of double (size == 1) with a double ?
Any suggestions ? Thanks.
You don't have to follow the scheme of generic template when you are about to specialize. Hope this helps:
template<typename RealType>
struct Field<RealType, 1> {
typename ClassAVectorTraits<RealType,1>::type value;
operator ClassAVectorTraits<RealType,1>::type () const { return value; }
Field(const std::vector<RealType>& vec)
{
if (vec.size() == 1) value = vec[0];
else throw runtime_error("wrong size");
}
};