Partial specialization and SFINAE - c++

Suppose I have the following Matrix template class and there is a requirement to represent vector as either 1 x RowSize or ColSize x 1 matrix (so that I can reuse many matrix operators which are compatible with vectors: multiplying 2 matrices, multiplying matrix by a scalar etc):
template <class T, size_t ColumnSize, size_t RowSize>
struct Matrix {
T [ColumnSize][RowSize];
}
I have two questions:
1) If I am not mistaken I can achieve that either by partial specialization or using SFINAE on Matrix methods (for example to enable 'length' method when either ColSize or RowSize is 1). What are the pros and cons of mentioned options?
2) If I choose to go with the partial specialization, is there a way to define one specialization for both row and column vectors, instead of this:
template <class T, size_t ColumnSize>
struct Matrix<T, ColumnSize, 1> {
T length() const;
T [ColumnSize][RowSize];
}
template <class T, size_t RowSize>
struct Matrix<T, 1, RowSize> {
T length() const;
T [ColumnSize][RowSize];
}

It really depends on whether the requirement is "a general Matrix must not have a length method" (then SFINAE or inheritance should be used), or "length must not be called on a general Matrix" (then a static_assert inside of the length body is applicable). A third option is to not do anything and make length applicable on generic matrices, however there are still other operations that only work on vectors.
For "a general Matrix must not have a length method". To save space, I will use int, and shorter symbol names. Instead of int_, you should use std::integral_constant. The int_ wrapper is needed because of language restrictions that forbid specializing with more complex computations if the parameter is a non-type parameter. Therefore we ḿake the paramer a type, and wrap the value into it. The following does not use SFINAE, but inheritance. With d() of the vector mixing base class, you can access the data of the vector at any time from within the mixing class.
template<int> struct int_;
template<typename D, typename S>
struct V { };
template<typename T, int A, int B>
struct M : V<M<T, A, B>, int_<A * B>> {
T data[A][B];
};
template<typename T, int A, int B>
struct V<M<T, A, B>, int_<A + B - 1>> {
int length() const { return A * B; }
M<T, A, B> *d() { return static_cast<M<T, A, B>*>(this); }
const M<T, A, B> *d() const { return static_cast<const M<T, A, B>*>(this); }
};
This is now
int main() {
M<float, 1, 3> m1; m1.length();
M<float, 3, 1> m2; m2.length();
// M<float, 3, 2> m3; m3.length(); error
}
For "length must not be called on a general Matrix", you can use "static_assert"
template<typename T, int A, int B>
struct M {
int length() const {
static_assert(A == 1 || B == 1, "must not be called on a matrix!");
return A * B;
}
T data[A][B];
};
Choose what is most appropriate

SFINAE is only able to disable a template declaration based on its own parameters. It's a bit unnatural to disable a non-template member function such as length, using the parameters of the enclosing class. The technique looks like this:
template <class T, size_t RowSize, size_t ColumnSize>
struct Matrix {
// SFINAE turns a non-template into a template.
// Introduce a fake dependency so enable_if resolves upon function call.
template< typename size_t_ = size_t >
static constexpr
// Now write the actual condition within the return type.
std::enable_if_t< RowSize == 1 || ColumnSize == 1
, size_t_ > length() const;
{ return RowSize * ColumnSize; }
T [ColumnSize][RowSize];
}
If you can stomach this ugliness, then you get exactly what you want: a function of the desired type, which completely vanishes when the condition is not met. No other support is needed.
On the other hand, partial specialization affects the entire class definition. Since it's usually poor design to duplicate the entire class in each partial specialization, inheritance is used as Johannes describes.
Just to add one alternative to his answer, SFINAE can be used within partial specialization, to avoid the clever algebra and the int_ issue.
// Add "typename = void" for idiomatic class SFINAE.
template<size_t RowSize, size_t ColumnSize, typename = void>
struct maybe_vector_interface { }; // Trivial specialization for non-vectors
// Partial specialization for vectors:
template<size_t RowSize, size_t ColumnSize>
struct maybe_vector_interface< RowSize, ColumnSize,
std::enable_if_t< RowSize == 1 || ColumnSize == 1 > > {
static constexpr int length() const
{ return RowSize * ColumnSize; }
};
template<typename T, size_t RowSize, size_t ColumnSize>
struct Matrix
: maybe_vector_interface<RowSize, ColumnSize> {
T data[RowSize][ColumnSize];
};

Related

How to disable a member function based on a class template parameter?

To illustrate the situation, let's assume a minimal example: a Vector template class taking its dimension as a non-type template parameter. This class will provide x(), y() (etc.) accessors when the dimension allows it:
template <int N_dimension>
class Vector
{
public:
// ctors, etc.
int &x();
template <class = std::enable_if_t<(N_dimension>2)>> int &y();
private:
std::array<int, N_dimension> mData;
};
Yet, this does not work, because enable_if can only be applied on deduced template parameters.
Our current workaround looks cumbersome:
template <int N=N_dimension, class = std::enable_if_t<(N>2)>> int &y();
Moreover, it also requires a static-assert in the definition to make sure it is fool-proof (because now client code could give an explicit value to N that does not match the real dimension. Edit: Or an explicit value for the anonymous second template parameter, as pointed out by SergeyA).
Is there a more direct approach to express this in C++?
I would do away with SFINAE here, and simply split the code into interface and private implementation like that:
int& y() {
return y_impl(std::bool_constant<N > 2>{});
}
private:
int& y_impl(std::true_type ) {
// impl
}
int& y_impl(std::false_type ) {
static_assert(N > 2 /* always false */, "Wrong number of dimensions!");
}
The split here is in assumption of y not compilable when N <= 2, to reduce the clutter of error messages. If this is not the case, a single static_assert in y body would be sufficient.
In C++20, you might simply use requires to discard method:
template <int N>
class Vector
{
public:
int &x();
int &y() requires(N >= 2);
private:
std::array<int, N_dimension> mData;
};
In previous version, it is more verbose:
template <std::size_t N>
class Vector
{
public:
int &x();
template <std::size_t M = N, std::enable_if_t<(M >= 2 && M == N), int> = 0>
int &y();
private:
std::array<int, N_dimension> mData;
};

How do I get access to template parameters of a template pack parameter

I am trying to create a template class that will enable a compare function to return an integer [ 0 - equal, >0 a should come first, <0 b should come first ].
I am using Sort structs template parameters to keep track of the type that should be used, offset of the field in the string, as well as the order that this field should be kept... so compare can according return the correct value.
Assume for now that the std::string is used to represent a serialized value.
I am having trouble with extracting the information from the template. I have kept sort as a pack parameter, which would be of the type Sort. How do I access these parameters in the code? If there is a better way to refactor this. I looked at some of the other questions related to templates, but didn't see any that would solve this problem. I am using gcc 8.2 and c++17.
#include <cstdint>
#include <string>
#include <cstring>
#include <cassert>
template<typename T, uint32_t offset, char Order = 'A'>
struct Sort {};
template<uint32_t keyLength, template<typename T,uint32_t offset, char Order> class ... sort>
class Comparator {
public:
int compare(std::string & a, std::string &b) {
assert(a.length()==b.length());
// How would I sum the sizeof each T. i.e. if T is int and another T is short, then sum should be 6+keyLength?
assert(a.length()==(sizeof(T)+keyLength)); // Check that my length is equal to key length + all type lengths put together
auto r = memcmp(a.data(),b.data(),keyLength);
if(r!=0) return r;
// How do I retrieve T,offset,Order of each pack parameter.
return internal_compare<T,offset,Order>(a.data(),b.data())? internal_compare<T,offset,Order>(a.data(),b.data()) : ...;
}
private:
template<typename IT,uint32_t iOffset, char iOrder>
int internal_compare(char * a,char *b) {
if constexpr (iOrder=='A'||iOrder=='a') {
return (*(static_cast<IT *>(a+iOffset)))-(*(static_cast<IT *>(b+iOffset)));
} else {
return (*(static_cast<IT *>(b+iOffset)))-(*(static_cast<IT *>(a+iOffset)));
}
}
};
Two things I have not been able to accomplish.
One is getting the sum of sizeof(T) from the sort.
Call the internal compare operator on each sort.
Link to code on compiler explorer
This becomes substantially easier if instead of using this form:
template<typename T, uint32_t offset, char Order = 'A'>
struct Sort {};
template<uint32_t keyLength, template<typename T,uint32_t offset, char Order> class ... sort>
class Comparator;
You use this one:
template <uint32_t keyLength, class...>
class Comparator;
template <uint32_t keyLength, typename... T, uint32_t... offset, char... Order>
class Comparator<keyLength, Sort<T, offset, Order>...> {
// ...
};
First, the original didn't do what you wanted to do anyway. You wanted specific instantiations of Sort but you were actually accepting class templates... like Comparator<32, Sort, Sort, Sort>. Which presumably isn't meaningful.
But when we do it this way, we're not only accepting only instantiations of Sort but we have the parameters in the most useful form. So something like this:
// How would I sum the sizeof each T. i.e. if T is int and another T is short,
// then sum should be 6+keyLength?
Is a fold-expression:
(sizeof(T) + ... + keyLength)
And so forth.
I'll take this problem on another front: how do you extract the template parameters if T has template parameters? Here's an example:
template<typename T>
void foo(T v) {
// T is std::vector<int>, how to extract `int`?
}
int main() {
foo(std::vector{1, 2, 3, 4});
}
There's many answers to that: extraction using partial specialization, type aliases and others.
Here's how you can do it for std::vector:
template<typename>
struct extract_value_type_t {};
template<typename T>
struct extract_value_type_t<std::vector<T>> {
using type = T;
};
template<typename T>
using extract_value_type_t = typename extract_value_type<T>::type;
template<typename T>
void foo(T v) {
// with template specialization
using value_type = extract_value_type_t<T>;
// with the member alias std::vector exposes
// needs much less boilerplate!
using value_type = typename T::value_type;
}
What does doing it with T when it's a vector gives us? Well, if you can do something with a simple type T, you won't even need a template template parameter, making your interface more flexible:
template<typename>
struct sort_traits {};
template<typename T, uint32_t offset_, char order_>
struct sort_traits<Sort<T, offset_, order_>> {
using type = T
static constexpr auto offset = offset_;
static constexpr auto order = order_;
};
Then in your Comparator class, simply do something like that:
template<uint32_t keyLength, typename... sorts>
struct Comparator {
int compare(std::string const& a, std::string const& b) {
return (internal_compare<sorts>(a.data(), b.data()) && ...);
}
private:
template<typename sort>
int internal_compare(char const* a, char const* b) {
using traits = sort_traits<sort>;
using type = typename traits::type;
constexpr auto offset = traits::offset;
constexpr auto order = traits::order;
// do stuff
}
};
This also add the possibility one day to add another kind of sort that would have different template parameters or different things exposed.

Pass N-D array by reference to variadic function

I'd like to make the function multi_dimensional accept a multidimensional array by reference.
Can this be done with a variation of the syntax below which works for three_dimensional?
#include <utility>
// this works, but number of dimensions must be known (not variadic)
template <size_t x, size_t y, size_t z>
void three_dimensional(int (&nd_array)[x][y][z]) {}
// error: parameter packs not expanded with ‘...’
template <size_t... dims>
void multi_dimensional(int (&nd_array)[dims]...) {}
int main() {
int array[2][3][2] = {
{ {0,1}, {2,3}, {4,5} },
{ {6,7}, {8,9}, {10,11} }
};
three_dimensional(array); // OK
// multi_dimensional(array); // error: no matching function
return 0;
}
The main problem is that you cannot make the number of array dimensions itself variadic. So whichever way you go, you will almost certainly need a recursive approach of some sort to deal with the individual array layers. What exactly such approach should look like will mainly depend on what exactly you're planning to do with the array once it's been given to you.
If really all you want is a function that can be given any multi-dimensional array, then just write a function that can be given anything but only exists as long as that anything is an array:
template <typename T>
std::enable_if_t<std::is_array_v<T>> multi_dimensional(T& a)
{
constexpr int dimensions = std::rank_v<T>;
// ...
}
However, this by itself will most likely not get you very far. To actually do anything meaningful with the array you've been given, you will most likely need some recursive walking through subarrays. Unless you really just want to look at the topmost layer of the structure.
Another approach is to use a recursive template to peel back the individual array levels, for example:
// we've reached the bottom
template <typename T, int N>
void multi_dimensional(T (&a)[N])
{
// ...
}
// this matches any array with more than one dimension
template <typename T, int N, int M>
void multi_dimensional(T (&a)[N][M])
{
// peel off one dimension, invoke function for each element on next layer
for (int i = 0; i < N; ++i)
multi_dimensional(a[i]);
}
I would, however, suggest to at least consider using std::array<> instead of raw arrays as the syntax and special behavior of raw arrays tends to turn everything into a confusing mess in no time. In general, it might be worth to implement your own multi-dimensional array type, like an NDArray<int, 2, 3, 2> which internally works with a flattened representation and just maps multi-dimensional indices to a linear index. One advantage of this approach (besides the cleaner syntax) would be that you can easily change the mapping, e.g., to switch from row-major to column-major layout, e.g., for performance optimization…
To implement a general nD array with static dimensions, I would introduce a helper class to encapsulate the recursive computation of a linear index from an nD index:
template <std::size_t... D>
struct row_major;
template <std::size_t D_n>
struct row_major<D_n>
{
static constexpr std::size_t SIZE = D_n;
std::size_t operator ()(std::size_t i_n) const
{
return i_n;
}
};
template <std::size_t D_1, std::size_t... D_n>
struct row_major<D_1, D_n...> : private row_major<D_n...>
{
static constexpr std::size_t SIZE = D_1 * row_major<D_n...>::SIZE;
template <typename... Tail>
std::size_t operator ()(std::size_t i_1, Tail&&... tail) const
{
return i_1 + D_1 * row_major<D_n...>::operator ()(std::forward<Tail>(tail)...);
}
};
And then:
template <typename T, std::size_t... D>
class NDArray
{
using memory_layout_t = row_major<D...>;
T data[memory_layout_t::SIZE];
public:
template <typename... Args>
T& operator ()(Args&&... args)
{
memory_layout_t memory_layout;
return data[memory_layout(std::forward<Args>(args)...)];
}
};
NDArray<int, 2, 3, 5> arr;
int main()
{
int x = arr(1, 2, 3);
}

C++ template metafunction on instantiated object of a template class

I hope that this question isn't overly convoluted. I realize that meta-programming acts on types rather than on the objects of those types; however, I am still trying to achieve the same result, by 1) retrieving the type information from the class and then 2) meta-functions on that type information.
An explanation of my situation is as follows with simplified code excerpts:
I have a template class for matrices, which I am calling Matrix_Base. Somewhat similar to the approach taken by Eigen, I am allowing for two possibilities for the size of a matrix -- either fixed at compile-time or fixed at run-time. Simplified declaration of Matrix_Base is:
template <typename Type, uint32_t rows_ = 1, uint32_t cols_ = 1,>
class Matrix_Base{
/*
...
*/
};
Run-time sized matrix is denoted by an argument of 0.
The check for run-time vs compile-time sizing of the matrix is fairly simple (using boost::mpl):
typedef typename mpl::if_<
typename mpl::or_<
typename mpl::equal_to<
typename mpl::int_<rows_>::type,
mpl::int_<0>
>::type,
typename mpl::equal_to <
typename mpl::int_<cols_>::type,
mpl::int_<0>
>
>::type,
mpl::true_,
mpl::false_
>::type runtime_size_type;
This has been tested and works fine. My trouble starts about here...
Presently, I am using the above boost::mpl code in the following manner:
namespace internal {
template <uint32_t rows = 1, uint32_t cols_ = 1>
struct Runtime_Size_Helper {
typedef typename mpl::if_<
// REST OF THE BOOST::MPL code here //
>::type runtime_size_t
bool value() { return runtime_size_t::value;}
};
} // namespace
template <typename Type, uint32_t rows_ = 1, uint32_t cols_ = 1>
class Matrix_Base{
// ...
static constexpr uint32_t rows = rows_;
static constexpr uint32_t cols = cols_;
bool is_runtime_sized;
// ...
};
template <typename T, uint32_t R, uint32_t C>
bool Matrix_Base<T,R,C>::is_runtime_sized = internal::Runtime_Size_Helper<R,C>::value();
This makes the result of that mpl function into a member of the Matrix_Base class. So far so good.
I'd like to use some form of indirection to determine the value of runtime_size_type by passing it the instantiated object. As per the example code, the only required information to determine this is the uint32_t parameters for col and row.
For the instantiated Matrix_Base object, the relevant information will never change from its compile-type values. The size of the matrix will be immutable; the size will either be set from the template arguments or -- for runtime-sized matrices -- through the constructor. In both cases, the template arguments are fixed and part of the type information. I am saving this information as static variables in the class, and I have even tried to add a typedef with all of the template parameters as my_type typename typename Matrix_Base<T,rows_, cols_, ...> my_type but I cannot seem to figure out how to write a metafunction to which I can pass a Matrix_Base object (obviously as a reference or pointer) and re-extract the relevant information.
I am fully open to incorporating (other) boost libraries, if they would provide the necessary functionality.
Hope that this is clear. Please let me know if there's something here that's unclear or that's just plain stupid.
Best regards,
Shmuel
edited the text to provide a bit more clarity as to the issue
The quickest way to do what you seem to want (you didn't really specify it in detail) is to let your matrix class template inherit from a storage class template, and to specialize the storage class depending on whether your MPL predicate returns true or false
#include <array>
#include <iostream>
#include <vector>
#include <boost/mpl/bool.hpp>
#include <boost/mpl/comparison.hpp>
#include <boost/mpl/if.hpp>
#include <boost/mpl/int.hpp>
#include <boost/mpl/logical.hpp>
using namespace boost;
// in C++98, use
// template<int R, int C>
// struct is_runtime_sized: mpl::if<
// ...
// >::type {};
template<int R, int C>
using is_runtime_sized = typename mpl::if_<
mpl::or_<
mpl::equal_to<mpl::int_<R>, mpl::int_<0>>,
mpl::equal_to<mpl::int_<C>, mpl::int_<0>>
>,
mpl::true_, mpl::false_
>::type;
Note that I've eleminated some unnecessary typename occurances to make the MPL predicate more readable.
template<class T, int R, int C, bool = is_runtime_sized<R, C>::value>
struct MatrixStorage
{
MatrixStorage() = default;
MatrixStorage(int r, int c): data_(r * c) {} // zero-initializes
protected:
std::vector<T> data_;
};
template<class T, int R, int C>
struct MatrixStorage<T, R, C, false>
{
MatrixStorage() = default;
MatrixStorage(int, int): data_{} {} // zero-initializes
protected:
std::array<T, R * C> data_;
};
Here, I've split the implementation of the dynamically and statically stored matrices. The former uses a std::vector and the latter a std:array. Similar to Eigen, both have a default constructor, and both also have a constructor taking the matrix dimensions that zero-initializes.
template<class T, int R = 0, int C = 0>
struct Matrix: public MatrixStorage<T, R, C>
{
Matrix() = default;
// In C++98, write:
// Matrix(int r, int c): MatrixStorage<T, R, C>(r, c) {}
using MatrixStorage<T, R, C>::MatrixStorage;
int size() const { return this->data_.size(); }
};
The actual Matrix class inherits from the MatrixStorage, and returns a size() depending on the currently stored data.
int main()
{
Matrix<int> m_dyn(3, 3);
std::cout << m_dyn.size() << "\n"; // 9
Matrix<int, 2, 2> m_stat;
std::cout << m_stat.size() << "\n"; // 4
}
Live Example. As you can see, the dynamically allocated matrix has size 9, and the statically sized one has size 4. Note that there are two small C++11 features in the above code, that you can easily work-around if you are required to use C++11.

C++ template argument reference/value trait

I am not sure if title makes sense.
let me explain what I want to do:
I have template constructor, the argument generally must be reference, but can be value in certain cases.
Ultimately, I would like to have something like:
matrix_adapter(typename adaptable<T,A>::type);
where adaptable<T,A>::type can either be value or reference defending on type of A.
I am not able to do so (template instantiation does not happen), and unsure why.
Right now, my solution is to enable/disable constructor using Sfinae:
matrix_adapter(A a, typename adaptable<T,A>::pass_by_value = 0)
however, that requires two constructors which are essentially the same.
Is it possible to do it using one generic constructor?
more code:
template<typename T>
struct adaptable<T, boost::detail::multi_array::const_sub_array<T, 2, const T*> >
{
typedef void* pass_by_value;
typedef boost::detail::multi_array::const_sub_array<T, 2, const T*> type;
static size_t size1(const type &A) { return A.shape()[0]; }
static size_t size2(const type &A) { return A.shape()[1]; }
static const T* data(const type &A) { return A.origin(); }
};
template<typename T, class L = ublas::column_major>
struct matrix_adapter
: ublas::matrix<T, L, ublas::array_adaptor<T> >
{
typedef ublas::array_adaptor<T> array_type;
typedef ublas::matrix<T, L, array_type> base;
matrix_adapter(size_t size1, size_t size2, T *data)
: base(size1, size2, array_type(size1*size2, pointer()))
{
initialize(data);
}
template<class A>
matrix_adapter(A a, typename adaptable<T,A>::pass_by_value = 0)
: base(adaptable<T,A>::size1(a),
adaptable<T,A>::size2(a),
array_type(adaptable<T,A>::size1(a)*
adaptable<T,A>::size2(a),
pointer()))
{
initialize(adaptable<T,A>::data(a));
}
template<class A>
matrix_adapter(A &a)
: base(adaptable<T,A>::size1(a),
adaptable<T,A>::size2(a),
array_type(adaptable<T,A>::size1(a)*
adaptable<T,A>::size2(a),
pointer()))
{
initialize(adaptable<T,A>::data(a));
}
Your constructor is itself a template, but with a signature of matrix_adapter(typename adaptable<T,A>::type) the compiler cannot deduce the type of A from the call.
By using SFINAE you enable the compiler to deduce A from the first constructor argument, and then the second argument prevents one or other from being considered in the overload set.
I don't believe it is possible to eliminate either constructor.