c++c++11variadic-templates

C++ Expand only part of the parameter pack


Preface

Given I have N parameters of different types (uint, int and float) and each parameter has its own valid range of values (i.e. from 2.5 to 20.0). Given also that some parameters are derived from other parameters. For those parameters I use different functions (f1, f2, f3, ...).

To be able to calculate the valid range of values (minimum and maximum) of the derived parameters, I have defined for each derived parameter two new functions f_min and f_max. Inside those functions I call f with the right combination of min and max parameters values to get the limit bounds of my derived parameter.

Let's make a simple example:

f(a, b) = a / b
min = f(a_min, b_max)
max = f(a_max, b_min)

How to solve it

First, I store all minimum and maximum parameters values into two containers. Then I define a deriveLimit template function which gets as template arguments a pointer to the function to use to derive the limit and the list of parameter indexes that it depends on. As function arguments the deriveLimit template functions gets the two list of min and max parameter values.

Container for the minimum and maximum parameter values

template <ParamIndex ...Indexes, typename ParamType, typename ...Args>
static ParamData deriveLimitUtil(const ParamData minParams[], const ParamData maxParams[],
                                 ParamValue<ParamType> (*function)(ParamValue<Args>...))
{
    ParamValue<ParamType> result = function(ParamValue<Args>(minParams[Indexes])..., ParamValue<Args>(maxParams[Indexes])...);
    return result.getData();
}

template <typename FuncType, FuncType function, ParamIndex ...Indexes>
static ParamData deriveLimit(const ParamData minParams[], const ParamData maxParams[])
{
    return deriveLimitUtil<Indexes...>(minParams, maxParams, function);
}

For example to derive the upper limit of parameter 2, I call deriveLimit as follow:

deriveLimit<typeof(&DeriveTest::deriveMaxLimit2), &DeriveTest::deriveMaxLimit2, ParamIndex::PARAM_2_INT, ParamIndex::PARAM_3_FLOAT_1>(minParams, maxParams);

Where deriveMaxLimit2 is declared as follow:

ParamValue<int32_t> DeriveTest::deriveMaxLimit2(ParamValue<int32_t> minValue2, ParamValue<float> minValue3, ParamValue<int32_t> maxValue2, ParamValue<float> maxValue3)

Problem

When I compile this code, the compiler returns the following error:

error: mismatched argument pack lengths while expanding ‘(ParamValue<Args>)(maxParams[Indexes])’
In instantiation of ParamData deriveLimitUtil(const ParamData*, const ParamData*, ParamValue<ParamType> (*)(ParamValue<Args>...)) [with short unsigned int ...Indexes = {1u, 2u}; ParamType = int; Args = {int, float, int, float}]’:
required from ParamData deriveLimit(const ParamData*, const ParamData*) [with FuncType = ParamValue<int> (*)(ParamValue<int>, ParamValue<float>, ParamValue<int>, ParamValue<float>); FuncType function = DeriveTest::deriveMaxLimit2; short unsigned int ...Indexes = {1u, 2u}]’

Question

How to I expand only half of the parameter pack Args for ParamValue<Args>(minParams[Indexes])...?


Solution

  • How to I expand only half of the parameter pack Args for ParamValue<Args>(minParams[Indexes])... ?

    #include <tuple>
    #include <utility>
    #include <cstddef>
    
    template <ParamIndex ...Indexes, typename ParamType, typename ...Args, std::size_t ...Is>
    static ParamData deriveLimitUtil(const ParamData minParams[], const ParamData maxParams[],
                                     ParamValue<ParamType> (*function)(ParamValue<Args>...),
                                     std::index_sequence<Is...>)
    {
        using Tuple = std::tuple<Args...>;
        ParamValue<ParamType> result = function(ParamValue<std::tuple_element_t<Is, Tuple>>(minParams[Indexes])...
                                              , ParamValue<std::tuple_element_t<Is, Tuple>>(maxParams[Indexes])...);
        return result.getData();
    }
    
    template <typename FuncType, FuncType function, ParamIndex ...Indexes>
    static ParamData deriveLimit(const ParamData minParams[], const ParamData maxParams[])
    {
        return deriveLimitUtil<Indexes...>(minParams, maxParams, function, std::make_index_sequence<sizeof...(Indexes)>{});
    }