mojo1mojo2
mojo1mojo2

Reputation: 1130

Nested Template Classes

I want to be able to create a generic nested template such that I can find the total size of all classes. To start, imagine for classes A, B, C, etc... each of which have a mSize member, and GetSize() function. I do the following process:

int main()
{
    using Abc = A<B<C<>>>;  // Imagine it is defined similarly to this for now.
    Abc abc;

    std::cout << abc.GetSize() << std::endl;

    // For abc.GetSize(), this will do the following:
    // 1. Go into A::GetSize().
    // 2. This will return A::mSize + B::GetSize()
    // 3. This will go into B::GetSize()
    // 4. This will return B::mSize + C::GetSize()
    // 5. Etc
    // Overall, we will have the total size of A+B+C as
    // A::mSize + B::mSize + C::mSize.

    return 0;
}

It will recursively go through each template class until the end and call GetSize(). My current attempts to do so have been using template-templates and variadic templates.

template <template<typename> class First, template<typename> class ...Args>
class A
{
public:
    int GetSize() const
    {
        First<Args...> foo;
        return mSize + foo.GetSize();
    }
private:
    int mSize{1};
};

template <template<typename> class First, template<typename> class ...Args>
class B
{
public:
    int GetSize() const
    {
        First<Args...> foo;
        return mSize + foo.GetSize();
    }
private:
    int mSize{2};
};

template <template<typename> class First, template<typename> class ...Args>
class C
{
public:
    int GetSize() const
    {
        First<Args...> foo;
        return mSize + foo.GetSize();
    }
private:
    int mSize{3};
};

This obviously has not worked. I would really like to be able to achieve the process described in int main().

Notes:

  1. These classes don't necessarily have to be included, or be in order. We could have A<C> or B<E<C<F<>>>>. Ideally, it can be infinitely long.

  2. I don't want to use polymorphism, wanting it to be resolved at runtime. I could have them all inherit from the same class, create a std::vector<Parent*>, push_back each child class, and iterate through using GetSize(). It would be nice to be able to define unique types such as A<B<>>, A<B<C<>>>, etc.

Upvotes: 2

Views: 563

Answers (2)

Holt
Holt

Reputation: 37606

Since your mSize is the same for all instance, your method should be static, and since it looks like it is a constant, it should be a constexpr.

Here is an implementation that uses a general template and then partially instantiate it with specific sizes:

template <int Size, typename T>
struct Holder {
    static constexpr int GetSize() {
        return Size + T::GetSize();
    }
};

template <int Size>
struct Holder<Size, void> {
    static constexpr int GetSize() {
        return Size;
    }
};

template <typename T = void>
using A = Holder<1, T>;

template <typename T = void>
using B = Holder<2, T>;

template <typename T = void>
using C = Holder<3, T>;

Then you can test:

using AB = A<B<>>;
using ABC = A<B<C<>>>;

static_assert(AB::GetSize() == 1 + 2, "Oops!");
static_assert(ABC::GetSize() == 1 + 2 + 3, "Oops!");

Of course you can make A, B, C, ... extends Holder instead of partially instantiate it if you need it.

Upvotes: 4

W.F.
W.F.

Reputation: 13988

You could do something like:

#include <iostream>
#include <type_traits>

using namespace std;

template <class T>
struct A {
  static constexpr int size = 1;
  using inner_type = T;
};

template <class T>
struct B {
  static constexpr int size = 2;
  using inner_type = T;
};

//template <class T>
struct C {
  static constexpr int size = 3;
  using inner_type = void;
};

template <class T, class = void>
struct TotalSizeGetter {
  static constexpr int get() {
    return T::size + TotalSizeGetter<typename T::inner_type>::get(); 
  }
};

template <class T>
struct TotalSizeGetter<T, typename enable_if<is_void<typename T::inner_type>::value>::type> {
  static constexpr int get() {
    return T::size;
  }
};

int main() {
  cout << TotalSizeGetter<A<B<C>>>::get() << endl;
}

This uses c++11 constexpr and enable_if but I see this is not a limitation as you use term variadic templates in your question...

Upvotes: 3

Related Questions