Home > Software engineering >  How to avoid duplicated code when using recursive parameter packs C
How to avoid duplicated code when using recursive parameter packs C

Time:11-04

How do you avoid code duplication when using varadic parameters in c ? Notice that I'm using templates recursively to achieve my goals, therefore I need some base cases and a recursive case. This creates a lot of code duplication, are there ways I could reduce this duplication?

Below, an example is provided of code that creates an arbitrary tensor (N dimensional array).

It's working fine but there's too much duplication. How can I avoid writing duplicated code when using template parameter packs recursively like this?

#include <cstddef>
#include <array>
#include <iostream>

template<typename T, std::size_t...>
class Tensor;

template<typename T, std::size_t N>
class Tensor<T, N> {
    using Type = std::array<T, N>;
    Type data;
public:

    Tensor()
    {
        zero();
    }

    void zero()
    {
        fill(0);
    }

    Type::iterator begin() { return data.begin(); }
    Type::iterator end() { return data.end(); }

    void fill(T value)
    {
        std::fill(data.begin(), data.end(), value);
    }

    void print() const
    {
        std::cout << "[";
        for(const auto& v : data)
        {
            std::cout << v << ",";
        }
        std::cout << "]";
    }
};

template<typename T, std::size_t N, std::size_t M>
class Tensor<T, N, M>
{
    using Type = std::array<Tensor<T, M>, N>;
    Type data;
public:

    Tensor()
    {
        zero();
    }

    void zero()
    {
        fill(0);
    }

    Type::iterator begin() { return data.begin(); }
    Type::iterator end() { return data.end(); }

    void fill(T value)
    {
        for(auto& v: data) {
            std::fill(v.begin(), v.end(), value);
        }
    }

    void print() const
    {
        std::cout << "[";
        for(const auto& v : data)
        {
            v.print();
            std::cout << ",";
        }
        std::cout << "]";
    }
};

template<typename T, std::size_t N, std::size_t... M>
class Tensor<T, N, M...>
{
    using Type = std::array<Tensor<T, M...>, N>;
    Type data;
public:
    Type::iterator begin() { return data.begin(); }
    Type::iterator end() { return data.end(); }

    Tensor()
    {
        zero();
    }

    void zero()
    {
        fill(0);
    }

    void fill(T value)
    {
        for(auto& v: data) {
            v.fill(value);
        }
    }

    void print() const
    {
        std::cout << "[";
        for(const auto& v : data)
        {
            v.print();
            std::cout << ",";
        }
        std::cout << "]";
    }

};

CodePudding user response:

The only difference between a single-dimension tensor and a multiple-dimension tensor is the type of std::array, T for single and Tensor<T, M...> for another.

template<typename T, std::size_t N, std::size_t... M>
class Tensor<T, N, M...> {
    using InnerT = std::conditional_t<(sizeof...(M) > 0),
                                Tensor<T, M...>,
                                T>;
    using Type = std::array<InnerT, N>;
    Type data;
}

Then, use if constexpr to distinguish single-dimension case,

    void fill(T value)
    {
        if constexpr(sizeof...(M) > 0) {
            for(auto& v: data) {
                v.fill(value);
            }
        } else {
            std::fill(data.begin(), data.end(), value);
        }
    }

    void print() const
    {
        std::cout << "[";
        for(const auto& v : data)
        {
            if constexpr(sizeof...(M) > 0) {
                v.print();
                std::cout << ",";
            } else {
                std::cout << v << ",";
            }
        }
        std::cout << "]";
    }

Demo

  • Related