Search code examples
c++templatesvariadic-templates

Recursive variadic C++ template


So,

I have the following template that multiplies two unit, e.g. velocity and time.

    //! The product of the TWO units
    template<template<typename> typename QuantityLhs, template<typename> typename QuantityRhs>
    struct Multiply
    {
        template<typename T>
        using type = units::unit<typename product_t<QuantityLhs<UNIT_LIB_DEFAULT_TYPE>, QuantityRhs<UNIT_LIB_DEFAULT_TYPE>>::conversion_factor, T>;
    };

This is called for example in the following way:

using AccuType = Multiply<Velocity, Time>::type<float>;

Issue

The above definition only accepts two template arguments, but I want an arbitrary number of them.

So what I wish to be able to write is something like

using AccuType = Multiply<Velocity, Time, Temperature, Density>::type<float>;

So my idea is to create a variadic template

    //! The product of the ANY number of units
    template<typename... Quantities>
    struct MultiplyMany
    {
        // Code
    };

Unfortunately, I don't know how to make it work. I have this idea that MultiplyMany would just somehow use a for loop or something to call the basic Multiply struct as many times as neccesary (iterating over template arguments).

Is that even possible?


Solution

  • The old school method is to create both variadic and regular templates as specializations of the main template, and let the former call the later and itself recursively. Make sure there is no ambiguity between the two!

    template<template<typename> typename... Quantities>
    struct Multiply;
    
    // The product of one unit is just that unit: one argument
    template<template<typename> typename Quantity>
    struct Multiply<Quantity>
    {
        template<typename T>
        using type = Quantity<T>;
    };
    
    // The product of two OR MORE units: two or more arguments
    template<template<typename> typename QuantityLhs, template<typename> typename QuantityRhs, template<typename> typename ... Rest>
    struct Multiply<QuantityLhs, QuantityRhs, Rest...>
    {
        template <typename T>
        using type0 = typename Multiply<QuantityRhs, Rest...>::type<T>;
    
        template<typename T>
        using type = units::unit<typename product_t<QuantityLhs<UNIT_LIB_DEFAULT_TYPE>, type0<UNIT_LIB_DEFAULT_TYPE>>::conversion_factor, T>; 
    };
    

    (Not tested due to lack of a minimal reproducible example)

    (Actually a multiplication of zero arguments should be allowed, and result in an undimensioned quantity of 1, but I don't know how you handle those).