Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Forcing template member function to instantiate

I have a class C in the namespace N with a public template member function F as follows:

namespace N {
    class C {
    public:
        template<int I>
        void F() {
            // ...
        }
    };
};

The values of I for N::C::F<I> are not known until runtime. However, the value of I is constrained such that 0 <= I < 2^8. I am looking for a way to force this function to expand the template to all 256 possible forms.

So far, I have manually created a static array within a second function of C that points to every possible function:

        template<int I>
        void F() {
            // ...
        }

        void G(int I) {
            static void(* const funcs[256])() = {
                F<0>, F<1>, F<2>, ...
            };

            funcs[I]();
        }

though I am left wondering if there is a better way. I already have a macro in N that is responsible for constructing a separate struct for each value of I (for use by F itself) and was looking if I could possibly integrate the instantiation of the template member function somehow in it:

    template<int> struct S;

    #define M(I, n) \
        template<> struct S<I> { \
            static const char name[] = #n; \
            /*
                Some how instantiate the function here, like (just guessing here):

                static const SEvaluator<I> eval = &C::F<I>;

                given

                template<int I>
                using SEvaluator = void(*)();
            */
        };

    M(0, "foo"); M(1, "bar");

    #undef M

My proposed method does not work as is and the compiler complains about F not being a constexpr. F manipulates several variables of C and calls external methods and could not be declared constexpr. Is there a way to salvage this or do I have to resort to my first hackish method?

like image 435
Brandon Avatar asked Sep 15 '26 19:09

Brandon


2 Answers

You can use an index_sequence<I...> (C++14) and expand I into the static array.

template<std::size_t... Is>
void G(int i, std::index_sequence<Is...>) {
    using C = void(C::*)();
    static C funcs[sizeof...(Is)] = { &C::F<Is>... };
    (this->*funcs[i])();
}

void G(int i) {
    G(i, std::make_index_sequence<256>());
}

For a non-C++14 solution you can write your own version of an index_sequence:

template<int... Is>
struct index_sequence { };

template<int N, int... Is>
struct make_index_sequence : make_index_sequence<N - 1, N - 1, Is...> { };

template<int... Is>
struct make_index_sequence<0, Is...> : index_sequence<Is...> { };
like image 185
David G Avatar answered Sep 17 '26 17:09

David G


Apparently I've been bested by Batman. I'll post this anyway : the principle is the same, it's just done another way around.

template <std::size_t... Idx>
auto const &makeFunctions(std::index_sequence<Idx...>) {
    static auto funcs = {&N::C::F<Idx>...};
    return funcs;
}

auto const &function(std::size_t i) { return *(begin(makeFunctions(std::make_index_sequence<256>{})) + i); }

Call :

N::C c;
for(int i = 0; i < 10; ++i)
    (c.*function(i))();

Live on Coliru

like image 28
Quentin Avatar answered Sep 17 '26 17:09

Quentin