Domanda

Say I have an int array like int arr[N] and say arr[i] are from a tiny domain (e.g. 1-10). Say I also have a variadic templated class with a common interface (abstract class)

template <int... A>
class FooImpl : public Foo
{
}

The question is how do I implement a function:

Foo* getFoo(int arr[N]);

or maybe better:

Foo* getFoo(int* pint, int size);

which would return FooImpl with template arguments corresponding my array? For example for arr = {4,2,6,1} I would get FooImpl<4,2,6,1>

È stato utile?

Soluzione

I found the answer to my question. The trick is in using struct variadic templates instead of function variadic templates which I initially tried with. I use _getFoo_impl struct with func function which builds up element by element.

Let's assume the elements are in range [1-5] and the size <= 4 and then the code looks as following:

class Foo
{
};

template <int...A>
class FooImpl : Foo {
};

template<int...As>
struct _getFoo_impl
{
    static Foo* func(int *arr, int sz)
    {
        if (sz == 0)
            return new FooImpl<As...>;

        switch (*arr)
        {
        case 1: return _getFoo_impl<As..., 1>::func(arr + 1, sz - 1);
        case 2: return _getFoo_impl<As..., 2>::func(arr + 1, sz - 1);
        case 3: return _getFoo_impl<As..., 3>::func(arr + 1, sz - 1);
        case 4: return _getFoo_impl<As..., 4>::func(arr + 1, sz - 1);
        case 5: return _getFoo_impl<As..., 5>::func(arr + 1, sz - 1);
        default: throw "element out of range";
        }
    }
};

template<int A1, int A2, int A3, int A4, int A5>
struct _getFoo_impl<A1, A2, A3, A4, A5>
{
    static Foo* func(int*, int sz) {
        std::terminate();
    }
};

Foo* getFoo(int *arr, int size)
{
    return _getFoo_impl<>::func(arr, size);
}

Altri suggerimenti

You cannot store or generate a type from a class template

template <int... N>
class FooImpl;

with run-time integers as template arguments. However, if the following might also fit your actual problem, you could store and then look up a pointer-to-function

template <int... N>
R FooFun(A...);

given run-time integers as template arguments, assuming the function signature R(A...) is the same for all template arguments. Note that A... is not a pack of template parameters here; it's just a placeholder for the types of your own concrete function parameters, e.g. FooFun(int, int).

It seems to me that this formulation indeed fits your problem because you have a factory function with no input parameters that returns a pointer to object FooImpl<N...> always seen as Foo*, so in your case FooFun would look like

template <int... N>
Foo* FooFun();

A general solution for this kind of conversion is based on a look-up table of pointers-to-function and is given in this answer of my previous question adapting a non-constexpr integral value to a non-type template parameter (which by the way works very smoothly now and I'm very happy with it - my actual implementation is here).

The difference in your case is that you need a multi-dimensional look-up table. I suggest that you first define a function "dispatcher" with only one integer template argument

template <int OFFSET>
R FooDispatch(A...);

which represents the linear offset in the multi-dimensional table. In this case the previous solution applies directly to FooDispatch. Then, you have to convert OFFSET to a set of multi-dimensional indices N..., at compile time. For this, you would also need to know the dimensions of the table, or better its strides, again at compile time. Having deduced arguments N..., FooDispatch can now call FooFun<N...>(...).

To be more precise, the logic of this offset-to-index conversion is exactly as in Matlab's function ind2sub, only it's a compile-time operation. This would need some work, so if you like this approach and need help in this last step, I'd be happy to assist. In this case, I think you'd better post a new question with the sub-problem appropriately formulated.

All the above imply that the number of dimensions is also known at compile time, so eventually a function of the form

Foo* getFoo(int arr[N]);

would be fine as an interface, but

Foo* getFoo(int* pint, int size);

does not make much sense; you could only make a run-time check in the latter case, and maybe throw an exception if dimensions do not agree.

Finally, note that using out-of-range indices at run-time would have the same effect as using out-of-range indices in an ordinary C array.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top