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.
Related
I have a wrapper struct around a vector and a shape, like this:
template <std::size_t N>
struct array_type {
std::array<std::size_t, N> shape;
std::vector<float> data;
};
I would like to be able to construct an array_type from any array float[N], float[N][M], etc.
Currently, I have a function for each rank, e.g.
template <std::size_t N>
array_type<1> _1d(const deel::formal::float_type (&values)[N]) {
return {{N},
std::vector<float_type>(
reinterpret_cast<const float_type*>(values),
reinterpret_cast<const float_type*>(values) + N)};
}
template <std::size_t N>
array_type<2> _2d(const deel::formal::float_type (&values)[N][M]) {
return {{N, M},
std::vector<float_type>(
reinterpret_cast<const float_type*>(values),
reinterpret_cast<const float_type*>(values) + N * M)};
}
I would like to write something like:
template <class Array>
array_type<std::rank_v<Array>> make_array(Array const&);
...but that does not work for initializer list:
auto arr1 = _1d({1, 2, 3}); // Ok
auto arr2 = make_array({1, 2, 3}); // Ko
Is there a way to have this make_array? Or (since I think it's not possible), to have at least something like make_array<1>({1, 2, 3}) where the rank is explicitly specified?
array_type::shape can be generated with std::extent with a bit of template meta programming:
template<typename Array, std::size_t... I>
auto extents_impl(const Array& a, std::index_sequence<I...>)
{
return std::array{std::extent_v<Array, I>...};
}
template<typename Array>
auto extents(const Array& a)
{
return extents_impl(a, std::make_index_sequence<std::rank_v<Array>>());
}
With this, make_array can be written as:
template <class Array, std::enable_if_t<std::is_same_v<std::remove_cv_t<std::remove_all_extents_t<Array>>, float>, int> = 0> // magic incantation :)
array_type<std::rank_v<Array>> make_array(Array const& a)
{
array_type<std::rank_v<Array>> ret {
.shape = extents(a),
.data = std::vector<float>(sizeof a / sizeof(float)),
};
std::memcpy(ret.data.data(), &a, sizeof a);
return ret;
}
I use memcpy to avoid potential pointer type aliasing violations and the technicality of iterating outside the bounds of sub array being UB according to strict interpretation of the standard.
...but that does not work for initializer list:
Add one overload:
template <std::size_t N>
array_type<1> make_array(float const (&a)[N])
{
return make_array<float[N]>(a);
}
Alternatively, you can specify the array type in the call:
make_array<float[2][3]>({{1, 2, 3},{4, 5, 6}});
This requires no overloads.
T (&&)[N] is suggested when you want a function template to accept a braced-init-list and deduce its length, furthermore, you can use T (&&)[M][N], T (&&)[M][N][O] ... to deduce the lengths of each rank (sadly, there is nothing like T (&&)[N]...[Z] to accept array with arbitrary rank).
so you can provide overloading function like this:
// attention that `T (&&)[N][M]` is `Y (&&)[N]` where `Y` is `T[M]`.
template<typename T, size_t N>
using Array1D_t = T[N];
template<typename T, size_t M, size_t N>
using Array2D_t = Array1D_t<Array1D_t<T, N>, M>;
template<typename T, size_t M, size_t N, size_t O>
using Array3D_t = Array1D_t<Array2D_t<T, N, O>, M>;
// ...
// assume the max rank is X.
template<typename T, size_t N>
array_type<1> make_array(Array1D_t<T, N>&& v);
template<typename T, size_t M, size_t N>
array_type<2> make_array(Array2D_t<T, M, N>&& v);
template<typename T, size_t M, size_t N, size_t O>
array_type<3> make_array(Array3D_t<T, M, N, O>&& v);
// ...
and then, it will be okay for make_array({1, 2, 3}), make_array({{1, 2, 3}, {4, 5, 6}}) ..., except the rank is larger than X.
Is there a way to have this make_array?
I don't think so (but, to be honest, I'm not able to demonstrate it's impossible).
Or (since I think it's not possible), to have at least something like make_array<1>({1, 2, 3}) where the rank is explicitly specified?
I don't see a way also in this case.
But... if you accept that the sizes are explicitly specified... you can write a recursive custom type traits to construct the final type
// declararion and ground case
template <typename T, std::size_t ...>
struct get_ranked_array
{ using type = T; };
// recursive case
template <typename T, std::size_t Dim0, std::size_t ... Dims>
struct get_ranked_array<T, Dim0, Dims...>
: public get_ranked_array<T[Dim0], Dims...>
{ };
template <typename T, std::size_t ... Dims>
using get_ranked_array_t = typename get_ranked_array<T, Dims...>::type;
and the make function simply become
template <std::size_t ... Dims>
auto make_ranked_array (get_ranked_array_t<float, Dims...> const & values)
-> array_type<sizeof...(Dims)>
{
return {{Dims...},
std::vector<float>(
reinterpret_cast<float const *>(values),
reinterpret_cast<float const *>(values) + (Dims * ...))};
}
You can use it as follows
auto arr1 = make_ranked_array<3u>({1.0f, 2.0f, 3.0f});
auto arr2 = make_ranked_array<3u, 2u>({ {1.0f, 2.0f, 3.0f},
{4.0f, 5.0f, 6.0f} });
If you want, you can maintain the deduction of the last size, but I don't know if it's a good idea.
The following is a full compiling C++17 example
#include <array>
#include <vector>
template <std::size_t N>
struct array_type
{
std::array<std::size_t, N> shape;
std::vector<float> data;
};
// declararion and ground case
template <typename T, std::size_t ...>
struct get_ranked_array
{ using type = T; };
// recursive case
template <typename T, std::size_t Dim0, std::size_t ... Dims>
struct get_ranked_array<T, Dim0, Dims...>
: public get_ranked_array<T[Dim0], Dims...>
{ };
template <typename T, std::size_t ... Dims>
using get_ranked_array_t = typename get_ranked_array<T, Dims...>::type;
template <std::size_t ... Dims>
auto make_ranked_array (get_ranked_array_t<float, Dims...> const & values)
-> array_type<sizeof...(Dims)>
{
return {{Dims...},
std::vector<float>(
reinterpret_cast<float const *>(values),
reinterpret_cast<float const *>(values) + (Dims * ...))};
}
int main ()
{
auto arr1 = make_ranked_array<3u>({1.0f, 2.0f, 3.0f});
auto arr2 = make_ranked_array<3u, 2u>({ {1.0f, 2.0f, 3.0f},
{4.0f, 5.0f, 6.0f} });
static_assert ( std::is_same_v<decltype(arr1), array_type<1>> );
static_assert ( std::is_same_v<decltype(arr2), array_type<2>> );
}
I'm struggling with some template programming and I hope you can give me some help. I coded a C++11 interface that, given some structs like:
struct Inner{
double a;
};
struct Outer{
double x, y, z, r;
Inner in;
};
Implements a getter/setter to the real data that is customized to the specified struct members:
MyData<Outer, double, &Outer::x,
&Outer::y,
&Outer::z,
&Outer::in::a //This one is not working
> state();
Outer foo = state.get();
//...
state.set(foo);
I managed to implement this for simple structs in the following way:
template <typename T, typename U, U T::* ... Ms>
class MyData{
std::vector<U *> var;
public:
explicit MyData();
void set(T const& var_);
T get() const;
};
template <typename T, typename U, U T::* ... Ms>
MyData<T, U, Ms ... >::Struct():var(sizeof...(Ms))
{
}
template <typename T, typename U, U T::* ... Ms>
void MyData<T, U, Ms ...>::set(T const& var_){
unsigned i = 0;
for ( auto&& d : {Ms ...} ){
*var[i++] = var_.*d;
}
}
template <typename T, typename U, U T::* ... Ms>
T MyData<T, U, Ms ...>::get() const{
T var_;
unsigned i = 0;
for ( auto&& d : {Ms ...} ){
var_.*d = *var[i++];
}
return var_;
}
But it fails when I pass a member of a nested struct. Ideally, I'd like to implement a generic pointer to member type that allows me to be compatible with several levels of scope resolutions. I found this approach, but I'm not sure if this should be applied to my problem or if there exists some implementation ready to use. Thanks in advance!
Related posts:
Implicit template parameters
Pointer to inner struct
You might wrap member pointer into struct to allow easier chaining:
template <typename...> struct Accessor;
template <typename T, typename C, T (C::*m)>
struct Accessor<std::integral_constant<T (C::*), m>>
{
const T& get(const C& c) { return c.*m; }
T& get(C& c) { return c.*m; }
};
template <typename T, typename C, T (C::*m), typename ...Ts>
struct Accessor<std::integral_constant<T (C::*), m>, Ts...>
{
auto get(const C& c) -> decltype(Accessor<Ts...>().get(c.*m))
{ return Accessor<Ts...>().get(c.*m); }
auto get(C& c) -> decltype(Accessor<Ts...>().get(c.*m))
{ return Accessor<Ts...>().get(c.*m); }
};
template <typename T, typename U, typename ...Ts>
class MyData
{
std::vector<U> vars{sizeof...(Ts)};
template <std::size_t ... Is>
T get(std::index_sequence<Is...>) const
{
T res;
((Ts{}.get(res) = vars[Is]), ...); // Fold expression C++17
return res;
}
template <std::size_t ... Is>
void set(std::index_sequence<Is...>, T const& t)
{
((vars[Is] = Ts{}.get(t)), ...); // Fold expression C++17
}
public:
MyData() = default;
T get() const { return get(std::index_sequence_for<Ts...>()); }
void set(const T& t) { return set(std::index_sequence_for<Ts...>(), t); }
};
With usage similar to
template <auto ...ms> // C++17 too
using Member = Accessor<std::integral_constant<decltype(ms), ms>...>;
MyData<Outer, double, Member<&Outer::x>,
Member<&Outer::y>,
Member<&Outer::z>,
Member<&Outer::in, &Inner::a>
> state;
std::index_sequence is C++14 but can be implemented in C++11.
Folding expression from C++17 can be simulated too in C++11.
typename <auto> (C++17) should be replaced by typename <typename T, T value>.
Demo
A generalization of a member pointer is a function that can map T to X& at compile time.
In c++17 it isn't hard to wire things up thanks to auto. In c++11 it gets harder. But the basic idea is that you don't actually pass member pointers, you pass types, and those types know how to take your class and get a reference out of them.
template<class T, class D, class...Fs>
struct MyData {
std::array<D*, sizeof...(Fs)> var = {};
explicit MyData()=default;
void set(T const& var_) {
var = {{ Fs{}(std::addressof(var_))... }};
}
T get() {
T var_;
std::size_t index = 0;
using discard=int[];
(void)discard{ 0, (void(
*Fs{}(std::addressof(var_)) = *var[index++]
),0)... };
return var_;
}
};
it remains to write a utility that makes writing the Fs... easy for the member pointer case
template<class X, X M>
struct get_ptr_to_member_t;
template<class T, class D, D T::* M>
struct get_ptr_to_member_t< D T::*, M > {
D const* operator()( T const* t )const{
return std::addressof( t->*M );
}
};
#define TYPE_N_VAL(...) \
decltype(__VA_ARGS__), __VA_ARGS__
#define MEM_PTR(...) get_ptr_to_member_t< TYPE_N_VAL(__VA_ARGS__) >
now the basic case is
MyData< Outer, double, MEM_PTR(&Outer::x), MEM_PTR(&Outer::y) >
The more complex case can now be handled.
An approach would be to teach get_ptr_to_member to compose. This is annoying work, but nothing fundamental. Arrange is so that decltype(ptr_to_member_t * ptr_to_member_t) returns a type that instances right, applies it, then takes that pointer and runs the left hand side on it.
template<class First, class Second>
struct composed;
template<class D>
struct composes {};
#define RETURNS(...) \
noexcept(noexcept(__VA_ARGS__)) \
decltype(__VA_ARGS__) \
{ return __VA_ARGS__; }
template<class First, class Second>
struct composed:composes<composed<First, Second>> {
template<class In>
auto operator()(In&& in) const
RETURNS( Second{}( First{}( std::forward<In>(in) ) ) )
};
template<class First, class Second>
composed<First, Second> operator*( composes<Second> const&, composes<First> const& ) {
return {};
}
then we upgrade:
template<class X, X M>
struct get_ptr_to_member_t;
template<class T, class D, D T::* M>
struct get_ptr_to_member_t< D T::*, M >:
composes<get_ptr_to_member_t< D T::*, M >>
{
D const* operator()( T const* t )const{
return std::addressof( t->*M );
}
};
and now * composes them.
MyData<TestStruct, double, MEM_PTR(&Outer::x),
MEM_PTR(&Outer::y),
MEM_PTR(&Outer::z),
decltype(MEM_PTR(&Inner::a){} * MEM_PTR(&Outer::in){})
> state();
answre probably contains many typos, but design is sound.
In c++17 most of the garbage evaporates, like the macros.
I would use lambda approach to implement similar functionalities in C++17(C++14 is also ok, just change the fold expression):
auto access_by() {
return [] (auto &&t) -> decltype(auto) {
return decltype(t)(t);
};
}
template<class Ptr0, class... Ptrs>
auto access_by(Ptr0 ptr0, Ptrs... ptrs) {
return [=] (auto &&t) -> decltype(auto) {
return access_by(ptrs...)(decltype(t)(t).*ptr0);
};
}
auto data_assigner_from = [] (auto... accessors) {
return [=] (auto... data) {
return [accessors..., data...] (auto &&t) {
((accessors(decltype(t)(t)) = data), ...);
};
};
};
Let's see how to use these lambdas:
struct A {
int x, y;
};
struct B {
A a;
int z;
};
access_by function can be used like:
auto bax_accessor = access_by(&B::a, &A::x);
auto bz_accessor = access_by(&B::z);
Then for B b;, bax_accessor(b) is b.a.x; bz_accessor(b) is b.z. Value category is also preserved, so you can assign: bax_accessor(b) = 4.
data_assigner_from() will construct an assigner to assign a B instance with given accessors:
auto data_assigner = data_assigner_from(
access_by(&B::a, &A::x),
access_by(&B::z)
);
data_assigner(12, 3)(b);
assert(b.z == 3 && b.a.x == 12);
I am trying to implement a C++ matrix library (https://github.com/statslabs/matrix) according to Ch29 "A matrix design" of book "The C++ Programming Language". And find that I am not able to move exactly the same member function in Matrix and Matrix_ref back to Matrix_base.
The base class is a pure abstract class (https://github.com/statslabs/matrix/blob/master/include/slab/matrix/matrix_base.h):
template<typename T, size_t N>
class Matrix_base {
public:
// common stuff
virtual T *data() = 0;
virtual const T *data() const = 0;
private:
Matrix_slice<N> desc; // the shape of the matrix
};
The first derived class is (https://github.com/statslabs/matrix/blob/master/include/slab/matrix/matrix.h):
template<typename T, size_t N>
class Matrix : public Matrix_base<T,N> {
// special to Matrix
public:
T *data() { return elements.data(); } // "flat" element access
const T *data() const { return elements.data(); }
private:
vector<T> elements;
};
and the second derived class is (https://github.com/statslabs/matrix/blob/master/include/slab/matrix/matrix_ref.h):
template<typename T, size_t N>
class Matrix_ref : public Matrix_base<T,N> {
// special to Matrix_ref
public:
T *data() { return ptr_; } // "flat" element access
const T *data() const { return ptr_; }
private:
T* ptr; // the first element in the Matrix
};
The member functions in Matrix and Matrix_ref are exactly the same:
// m(i,j,k) subscripting with integers
template<typename T, std::size_t N>
template<typename... Args>
Enable_if<matrix_impl::Requesting_element<Args...>(), T &>
Matrix<T, N>::operator()(Args... args) {
assert(matrix_impl::check_bounds(this->desc_, args...));
return *(data() + this->desc_(args...));
}
template<typename T, std::size_t N>
template<typename... Args>
Enable_if<matrix_impl::Requesting_element<Args...>(), const T &>
Matrix<T, N>::operator()(Args... args) const {
assert(matrix_impl::check_bounds(this->desc_, args...));
return *(data() + this->desc_(args...));
}
where matrix_impl::Requesting_element<Args...>() make sure that the integers in Args... can be converted to std::size_t:
template<bool B, typename T = void>
using Enable_if = typename std::enable_if<B, T>::type;
template<typename X, typename Y>
constexpr bool Convertible() { return std::is_convertible<X, Y>::value; }
constexpr bool All() { return true; }
template<typename... Args>
constexpr bool All(bool b, Args... args) { return b && All(args...); }
template<typename... Args>
constexpr bool Requesting_element() {
return All(Convertible<Args, size_t>()...);
}
Everything is okay when the mentioned member functions are in Matrix and Matrix_ref. However when I move all the codes to Matrix_base, it is seems that somehow the checking of Arg... no longer works properly by giving the following message:
/Users/pany/statslabs-project/matrix/examples/dgemm_example.cpp:33:7: error: no matching function for call to object of type 'slab::mat' (aka 'Matrix<double, 2>')
A(i, j) = (double) (i * k + j + 1);
^
/Users/pany/statslabs-project/matrix/include/slab/matrix/traits.h:24:43: note: candidate template ignored: disabled by 'enable_if' [with Args = <int, int>]
using Enable_if = typename std::enable_if<B, T>::type;
^
/Users/pany/statslabs-project/matrix/include/slab/matrix/traits.h:24:43: note: candidate template ignored: disabled by 'enable_if' [with Args = <int, int>]
If you want to compile the project, you probably need to install both cmake and intel MKL (I want to implement some template functions as the interface to CBLAS). I thought it is simple refactoring of classes, but it just takes hours and I cannot figure out why it happens.
I find the solution and it seems to be a duplicated question (How to call a template member function in a template base class?
).
Now the codes has been moved to the MatrixBase:
template<typename T, std::size_t N>
template<typename... Args>
T &MatrixBase<T, N>::operator()(Args... args) {
assert(matrix_impl::check_bounds(this->desc_, args...));
return *(data() + this->desc_(args...));
}
template<typename T, std::size_t N>
template<typename... Args>
const T &MatrixBase<T, N>::operator()(Args... args) const {
assert(matrix_impl::check_bounds(this->desc_, args...));
return *(data() + this->desc_(args...));
}
And inside the derived classes Matrix and MatrixRef we have
// m(i,j,k) subscripting with integers
template<typename... Args>
Enable_if<matrix_impl::Requesting_element<Args...>(), T &>
operator()(Args... args) {
return MatrixBase<T,N>::template operator()<Args...>(args...);
}
template<typename... Args>
Enable_if<matrix_impl::Requesting_element<Args...>(), const T &>
operator()(Args... args) const {
return MatrixBase<T,N>::template operator()<Args...>(args...);
}
Note that the scripting checking will only be done in the derived classes since the base class is a pure abstract class and we will never create an object of MatrixBase.
I'm looking for a better way to calculate the sum of numeric template parameters associated with nested template classes. I have a working solution here, but I want to do this without having to create this extra helper template class DepthCalculator and partial specialization DepthCalculator<double,N>:
#include <array>
#include <iostream>
template<typename T,size_t N>
struct DepthCalculator
{
static constexpr size_t Calculate()
{
return N + T::Depth();
}
};
template<size_t N>
struct DepthCalculator<double,N>
{
static constexpr size_t Calculate()
{
return N;
}
};
template<typename T,size_t N>
class A
{
std::array<T,N> arr;
public:
static constexpr size_t Depth()
{
return DepthCalculator<T,N>::Calculate();
}
// ...
// Too many methods in A to write a separate specialization for.
};
int main()
{
using U = A<A<A<double,3>,4>,5>;
U x;
constexpr size_t Depth = U::Depth(); // 3 + 4 + 5 = 12
std::cout << "Depth is " << Depth << std::endl;
A<double,Depth> y;
// Do stuff with x and y
return 0;
}
The static function A::Depth() returns the proper depth at compile time, which can then be used as a parameter to create other instances of A. It just seems like a messy hack to have to create both the DepthCalculator template and a specialization just for this purpose.
I know I can also create a specialization of A itself with a different definition of Depth(), but this is even more messy due to the number of methods in A, most of which depend on the template parameters. Another alternative is to inherit from A and then specialize the child classes, but this also seems overly complicated for something that seems should be simpler.
Are there any cleaner solutions using C++11?
Summary Edit
In the end, this is the solution I went with in my working project:
#include <array>
#include <iostream>
template<typename T,size_t N>
class A
{
std::array<T,N> arr;
template<typename U>
struct Get { };
template<size_t M>
struct Get<A<double,M>> { static constexpr size_t Depth() { return M; } };
template<typename U,size_t M>
struct Get<A<U,M>>
{ static constexpr size_t Depth() { return M + Get<U>::Depth(); } };
public:
static constexpr size_t GetDepth()
{
return Get<A<T,N>>::Depth();
}
// ...
// Too many methods in A to write a separate specialization for.
};
int main()
{
using U = A<A<A<double,3>,4>,5>;
U x;
constexpr size_t Depth = U::GetDepth(); // 3 + 4 + 5 = 12
std::cout << "Depth is " << Depth << std::endl;
A<double,Depth> y;
// Do stuff with x and y
return 0;
}
Nir Friedman made some good points about why GetDepth() should be an external function, however in this case there are other Get functions (not shown) which are appropriately member functions, and therefore it would make the most sense to have GetDepth() a member function too. I also borrowed Nir's idea of having the Depth() functions only call themselves, rather than GetDepth() which creates a bit less circular dependencies.
I chose skypjack's answer because it most directly provided what I had originally asked for.
You said:
I want to do this without having to create this extra helper template class DepthCalculator
So, maybe this one (minimal, working example) is fine for you:
#include<type_traits>
#include<cassert>
template<class T, std::size_t N>
struct S {
template<class U, std::size_t M>
static constexpr
typename std::enable_if<not std::is_arithmetic<U>::value, std::size_t>::type
calc() {
return M+U::calc();
}
template<typename U, std::size_t M>
static constexpr
typename std::enable_if<std::is_arithmetic<U>::value, std::size_t>::type
calc() {
return M;
}
static constexpr std::size_t calc() {
return calc<T, N>();
}
};
int main() {
using U = S<S<S<double,3>,4>,5>;
static_assert(U::calc() == 12, "oops");
constexpr std::size_t d = U::calc();
assert(d == 12);
}
I'm not sure I got exactly your problem.
Hoping this can help.
If you are with C++14, you can use also:
template<class U, std::size_t M>
static constexpr
std::enable_if_t<not std::is_arithmetic<U>::value, std::size_t>
If you are with C++17, it becomes:
template<class U, std::size_t M>
static constexpr
std::enable_if_t<not std::is_arithmetic_v<U>, std::size_t>
The same applies to the other sfinaed return type.
Option #1
Redefine your trait as follows:
#include <array>
#include <cstddef>
template <typename T>
struct DepthCalculator
{
static constexpr std::size_t Calculate()
{
return 0;
}
};
template <template <typename, std::size_t> class C, typename T, std::size_t N>
struct DepthCalculator<C<T,N>>
{
static constexpr size_t Calculate()
{
return N + DepthCalculator<T>::Calculate();
}
};
template <typename T, std::size_t N>
class A
{
public:
static constexpr size_t Depth()
{
return DepthCalculator<A>::Calculate();
}
private:
std::array<T,N> arr;
};
DEMO
Option #2
Change the trait into function overloads:
#include <array>
#include <cstddef>
namespace DepthCalculator
{
template <typename T> struct tag {};
template <template <typename, std::size_t> class C, typename T, std::size_t N>
static constexpr size_t Compute(tag<C<T,N>>)
{
return N + Compute(tag<T>{});
}
template <typename T>
static constexpr size_t Compute(tag<T>)
{
return 0;
}
}
template <typename T, std::size_t N>
class A
{
public:
static constexpr std::size_t Depth()
{
return Compute(DepthCalculator::tag<A>{});
}
private:
std::array<T,N> arr;
};
DEMO 2
You can do this wholly non-intrusively, which I think is advantageous:
template <class T>
struct Depth
{
constexpr static std::size_t Calculate()
{
return 0;
}
};
template <class T, std::size_t N>
struct Depth<A<T, N>>
{
constexpr static std::size_t Calculate()
{
return N + Depth<T>::Calculate();
}
};
Usage:
using U = A<A<A<double,3>,4>,5>;
constexpr size_t depth = Depth<U>::Calculate(); // 3 + 4 + 5 = 12
I realize your original question was how to do this without the extra "helper template", which my solution still has. But on the flip side, it's moved the functionality completely out of A itself, so its not really a helper template any more, it's just a template. This is pretty short, doesn't have any template template parameters unlike Piotr's solutions, is easy to extend with other classes, etc.
Say you have a vector class that has a template length and type - i.e. vec<2,float>. These can also be nested - vec<2,vec<2,vec<2,float> > >, or vec<2,vec<2,float> >. You can calculate how deeply nested one of these vectors is like this:
template<typename T>
inline int depth(const T& t) { return 0; }
template<int N, typename T>
inline int depth(const vec<N,T>& v) { return 1+depth(v[0]); }
The trouble is you won't know how deep it is until run-time, but you may need to know the depth at comile-time in order to do something like this:
// Do this one when depth(v1) > depth(v2)
template<int N, typename T, int M, typename U>
inline vec<N,T> operator +(const vec<N,T>& v1, const vec<M,U>& v2) {
return v1 + coerce(v2,v1);
}
// Do this one when depth(v1) < depth(v2)
template<int N, typename T, int M, typename U>
inline vec<M,U> operator +(const vec<N,T>& v1, const vec<M,U>& v2) {
return coerce(v1,v2) + v2;
}
You can't just throw in an "if" statement because (a) which is deeper affects the return type and (b) coerce() generates a build error if you try to coerce a nested vector to a less-nested one.
Is it possible to do something like this or am I pushed up against the limits of C++ templates?
It's entirely possible. Try for example
template<int N, typename T, int M, typename U>
inline typename enable_if<is_deeper<T, U>::value, vec<N,T> >::type
operator +(const vec<N,T>& v1, const vec<M,U>& v2) {
return v1 + coerce(v2,v1);
}
template<int N, typename T, int M, typename U>
inline typename enable_if<is_deeper<U, T>::value, vec<M,U> >::type
operator +(const vec<N,T>& v1, const vec<M,U>& v2) {
return coerce(v1,v2) + v2;
}
Where is_deeper is something like
/* BTW what do you want to do if none is deeper? */
template<typename T, typename U>
struct is_deeper { static bool const value = false; };
template<typename T, int N, typename U>
struct is_deeper<vec<N, U>, T> {
static bool const value = true;
};
template<typename T, int N, typename U>
struct is_deeper<T, vec<N, U> > {
static bool const value = false;
};
template<typename T, int N, int M, typename U>
struct is_deeper<vec<M, T>, vec<N, U> > : is_deeper<T, U>
{ };
Template metaprogramming will set you free. I'm doing this at run-time, but it's evaluated at compile-time:
#include <iostream>
#include <boost\static_assert.hpp>
using namespace std;
template<size_t Depth> class Vec
{
public:
enum {MyDepth = Vec<Depth-1>::MyDepth + 1};
};
template<> class Vec<1>
{
public:
enum {MyDepth = 1};
};
BOOST_STATIC_ASSERT(Vec<12>::MyDepth == 12);
// Un-commenting the following line will generate a compile-time error
// BOOST_STATIC_ASSERT(Vec<48>::MyDepth == 12);
int main()
{
cout << "v12 depth = " << Vec<12>::MyDepth;
}
EDIT: Included a boost static assert to demonstrate how this is evaluated at compile-time.
Partial specialization is very useful for introspection. Usually it's better to avoid inline functions with compile-time constant results. (C++0x might ease this a bit, but I'm not sure how much.)
First, your vec template looks a lot like boost::array/std::tr1::array/std::array, so I'll just call it array.
template< class ArrT >
struct array_depth; // in the general case, array depth is undefined
template< class ElemT, size_t N > // partial specialization
struct array_depth< array< ElemT, N > > { // arrays do have depth
enum { value = 0 }; // in the general case, it is zero
};
template< class ElemT, size_t N1, size_t N2 > // more specialized than previous
struct array_depth< array< array< ElemT, N1 >, N2 > {
enum { value = 1 + array_depth< array< ElemT, N1 > >::value }; // recurse
};
// define specializations for other nested datatypes, C-style arrays, etc.
// C++0x std::rank<> already defines this for C-style arrays