TransWikia.com

std::array and std::vector Type Arbitrary Nested Iterable Generator Functions Implementation in C++

Code Review Asked on February 2, 2021

This is a follow-up question for the previous questions about recursive functions, including A Summation Function For Arbitrary Nested Vector Implementation In C++, A recursive_count_if Function For Various Type Arbitrary Nested Iterable Implementation in C++, A recursive_count_if Function with Specified value_type for Various Type Arbitrary Nested Iterable Implementation in C++, A recursive_count_if Function with Automatic Type Deducing from Lambda for Various Type Arbitrary Nested Iterable Implementation in C++ and A recursive_count_if Function with Unwrap Level for Various Type Arbitrary Nested Iterable Implementation in C++. In order to test these functions for arbitrary nested iterables, I am trying to implement some arbitrary nested iterable generators to construct nested std::vectors and nested std::arrays with the given parameters. The generator for std::array case is named n_dim_array_generator and another generator for std::vector is named n_dim_vector_generator. After trying to use if constexpr syntax in the previous question, I found that it is possible to use the similar technique in n_dim_array_generator function and n_dim_vector_generator function.

The usage description

For example, auto test_vector = n_dim_vector_generator<2, int>(1, 3); is expected to create a "test_vector" object which type is std::vector<std::vector<int>>. The content of this test_vector should as same as the following code.

std::vector<int> vector1;
vector1.push_back(1);
vector1.push_back(1);
vector1.push_back(1);
std::vector<std::vector<int>> test_vector;
test_vector.push_back(vector1);
test_vector.push_back(vector1);
test_vector.push_back(vector1);

There are four key part in the usage n_dim_vector_generator<2, int>(1, 3). The first number 2 represents the nested layers, the second parameter int represents the type of base elements, the third parameter 1 represents the input element which would be filled in, and the fourth parameter 3 represents the element count in each layer.

In the n_dim_array_generator function, the first parameter also represents the nested layers, but the parameter which represents the element count in each layer has been moved into the second one. Then, the third parameter represents the type of base elements and the fourth parameter represents the input element which would be filled in.

The experimental implementation

The experimental implementation of n_dim_vector_generator function and n_dim_array_generator function:

template<std::size_t dim, class T>
auto n_dim_vector_generator(T input, std::size_t times)
{
    if (dim < 0)
    {
        throw std::logic_error("Dimension can not be set less than 0");
    }
    if constexpr (dim == 0)
    {
        return input;
    }
    else
    {
        std::vector<decltype(n_dim_vector_generator<dim - 1>(input, times))> output;
        for (size_t i = 0; i < times; i++)
        {
            output.push_back(n_dim_vector_generator<dim - 1>(input, times));
        }
        return output;
    }
}

template<std::size_t dim, std::size_t times, class T>
auto n_dim_array_generator(T input)
{
    if (dim < 0)
    {
        throw std::logic_error("Dimension can not be set less than 0");
    }
    if constexpr (dim == 0)
    {
        return input;
    }
    else
    {
        std::array<decltype(n_dim_array_generator<dim - 1, times>(input)), times> output;
        for (size_t i = 0; i < times; i++)
        {
            output[i] = n_dim_array_generator<dim - 1, times>(input);
        }
        return output;
    }
}

Test cases

The test cases of n_dim_vector_generator function and n_dim_array_generator function are as below.

//  n_dim_vector_generator function usage with recursive_reduce function and recursive_count_if function
auto test_vector1 = n_dim_vector_generator<3, int>(3, 2);
std::cout << "recursive_reduce output: " << recursive_reduce(test_vector1, 0) << std::endl;
std::cout << "recursive_count_if output: " << recursive_count_if<3>(test_vector1, [](auto& i) {return i == 3; }) << std::endl;

//  n_dim_array_generator function usage with recursive_reduce function and recursive_count_if function
auto test_array1 = n_dim_array_generator<3, 2, int>(3);
std::cout << "recursive_reduce output: " << recursive_reduce(test_array1, 0) << std::endl;
std::cout << "recursive_count_if output: " << recursive_count_if<3>(test_array1, [](auto& i) {return i == 3; }) << std::endl;

The output of this test is as follows.

recursive_reduce output: 24
recursive_count_if output: 8
recursive_reduce output: 24
recursive_count_if output: 8

A Godbolt link is here.

All suggestions are welcome.

The summary information:

One Answer

Unnecessary check for dim < 0

Since dim is a std::size_t, it can never be negative, so this check is redundant.

Reserve spaces in vectors

If you know what the size of a vector is going to be up front, reserve() space for the elements to avoid unnecessary memory operations.

Prefer emplace_back() over push_back() where possible

In n_dim_vector_generator() you can use emplace_back() instead of push_back(). It will probably not matter much with an optimizing compiler, but this way you avoid a move operation each time you add an item.

Consider using std::fill()

At least in n_dim_array_generator(), you can use std::fill() or std::fill_n() to fill the array you created:

std::array<decltype(n_dim_array_generator<dim - 1, times>(input)), times> output;
std::fill(std::begin(output), std::end(output), n_dim_array_generator<dim - 1, times>(input));
return output;

You could also do this with n_dim_vector_generator() if you resize() the vector before filling it, but that causes unnecessary default construction of elements right before they are overwritten again, or you could combine it with std::back_inserter(), like so:

std::vector<decltype(n_dim_vector_generator<dim - 1>(input, times))> output;
output.reserve(times);
std::fill_n(std::back_inserter(output), times, n_dim_vector_generator<dim - 1>(input, times));
return output;

Although sadly there is no std::back_emplacer().

Answered by G. Sliepen on February 2, 2021

Add your own answers!

Ask a Question

Get help from others!

© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP