Question

In video-games is common that resources are loaded in a step fashion way, so within a single thread a loading bar can update at each loading step. By example:

1 -> Load texture A

2 -> Update Loading Bar to 2%

3 -> Load texture B

4 -> Update Loading Bar to 4%

5 ...

This can be done in many ways. One of these is define a function for each loading step.

void LoadTextureA()
{
    //Loading routine
    ...
}

This has the advantage of readability, not need too much nested code and even possible in some cases to share loading routines between two game states.

Now what I was thinking was to generalize this "function-for-step" model with templates. Lets say.

template <int S>
struct Foo{
    void LoadingStep()
    {
    }
};

template <>
struct Foo<0>
{
    void LoadingStep()
    {
        //First loading step
        ...
    }
};

Please correct me if I'm wrong. But it appears possible that I can compile-time iterate through 0 .. to N steps using metaprogramming and assign this specialized functions to an array or vector of function pointers. N steps are known at compile time along with it respective functions. Function pointer vector would be iterated like this:

template <int Steps>
class Loader  {
public:
    bool Load() 
    {
        functionArray[m_step]();
        if (++m_step == Steps)
            return false; //End loading
        else
            return true;
    }  
private:
    int m_step;
}

Is this possible? I know that that are easier ways to do it. But besides project requirments it's an interesting programming challenge

Was it helpful?

Solution

I achieved it based on Kal answer of a similar problem Create N-element constexpr array in C++11

template <int S>
struct Foo{
    static void LoadingStep()
    {
    }
};

template <>
struct Foo<0>
{
    static void LoadingStep()
    {
        //First loading step

    }
};

template<template<int S> class T,int N, int... Rest>
struct Array_impl {
    static constexpr auto& value = Array_impl<T,N - 1, N, Rest...>::value;
};

template<template<int S> class T,int... Rest>
struct Array_impl<T,0, Rest...> {
    static constexpr std::array<void*,sizeof...(Rest)+1> value = {reinterpret_cast<void*>(T<0>::LoadingStep),reinterpret_cast<void*>(T<Rest>::LoadingStep)...};
};

template<template<int S> class T,int... Rest>
constexpr std::array<void*,sizeof...(Rest)+1> Array_impl<T,0, Rest...>::value;

template<template<int S> class T,int N>
struct F_Array {
    static_assert(N >= 0, "N must be at least 0");

    static constexpr auto& value = Array_impl<T,N>::value;

    F_Array() = delete;
    F_Array(const F_Array&) = delete;
    F_Array(F_Array&&) = delete;
};

Using example:

int main()
{
    auto& value = F_Array< Foo ,4>::value;
    std::cout << value[0] << std::endl;

}

This yields of void* array of pointers to template functions:

Foo<0>::LoadinStep()
Foo<1>::LoadinStep() 
Foo<2>::LoadinStep()
Foo<3>::LoadinStep()
Foo<4>::LoadinStep()

Since Foo<1..3> are not specialized they will fall to Default LoadingStep function

OTHER TIPS

Yes. It's possible. And if you use the template metaprogramming, you don't need to use a run time loop, but a recursive call to a template method:

#include <iostream>

// The template numerated methods
template <int S> struct Foo{static void LoadingStep(){}};
template <> struct Foo<0> {static void LoadingStep(){std::cout<<0;}};
template <> struct Foo<1> {static void LoadingStep(){std::cout<<1;}};
template <> struct Foo<2> {static void LoadingStep(){std::cout<<2;}};

// The loader template method
template <int Step> 
void Loader()
{
    Foo<Step>::LoadingStep();
    Loader<Step-1>();
}

// Stopping rule
template <> void Loader<-1>(){}

int main()
{
    Loader<2>();
}

If you want an array:

 LoadingFunction functionArray[] = {Function0, Function1, Function2};
 .....

 for (int i = 0; i < nSteps; ++i)
    RunStep(i, nSteps, Function[i]);

Or initialize an std container with it.

If you want templates, you could write

 for (int i = 0; i < nSteps; ++i)
    RunStep(i, nSteps, Function<i>);

except i in Function<i> must be a constant. So you have to do it with a templated recursive something:

 template <int i, int NSteps> struct RunSteps
 {
    void Run() 
    {
      RunStep(i, NSteps, Function<i>);
      RunSteps<i+1, NSteps>::Run();
    }
 };

 template <int NSteps> struct RunSteps<NSteps, NSteps>
 {
    void Run() {}
 };

 RunSteps<0, NSteps>::Run();

Compile-time iteration doesn't really exist. The for loop and the templated recursive something do exactly the same thing. The compiler is as capable of unrolling a loop, as of inlining a call.

It looks like there's very little to be gained from templatizing this stuff, and lots to lose.

It is not clear why you would want to put templated functions to an array at compile time, but here you go:

 LoadingFunction functionArray[] = {Function<0>, Function<1>, Function<2>};

Now if you don't want to enumerate functions manually like that, it could be a bit of a challenge. It doesn't seem possible with either legacy C arrays or any of the std containers. Assuming you really need it, it's possible to write a custom container capable of such initialization.

 template <template <int> class FunctionWrappper, int NFunctions>
 class MyOptimizedFunctionArray {
      // filling this space is left as an exercise
 };
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top