Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sum Nested Template Parameters at Compile Time

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.

like image 495
Matt Avatar asked Jul 25 '26 06:07

Matt


2 Answers

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.

like image 111
skypjack Avatar answered Jul 27 '26 21:07

skypjack


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

like image 33
Piotr Skotnicki Avatar answered Jul 27 '26 20:07

Piotr Skotnicki



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!