HiveBrain v1.2.0
Get Started
← Back to all entries
patterncppMinor

Selecting variable amount of vector elements in C++

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
elementsamountselectingvariablevector

Problem

Suppose we are given a C++ vector. We want to specify a variable amount of indices and select elements from a vector being indexed. I have two implementation: (A) one relies on C++11 initializer lists, and the second one (B) on va_list and macro mess. Arrangement B is however funkier to type because it requires no typing the braces comprising the initializer list.

So my main question is: which one should an adult C++ programmer use, if any?

coderodde.h:

#ifndef CODERODDE_H
#define CODERODDE_H

#include 
#include 
#include 

#define NUM_ARGS(...) (sizeof((size_t[]){__VA_ARGS__}) / sizeof(size_t))

namespace coderodde {

    template 
    std::vector select(const std::vector& vec, 
                          const std::vector& indices) 
    {
        std::vector ret;

        for (auto index : indices) {
            ret.push_back(vec.at(index));
        }

        return ret;
    }

    template
    std::vector select(const std::vector& vec, const size_t len, ...)
    {
        std::va_list ap;
        std::vector ret;
        va_start(ap, len);

        for (size_t i = 0; i < len; ++i) 
        {
            ret.push_back(vec.at(va_arg(ap, size_t)));
        }

        va_end(ap);
        return ret;
    }
}

#define SELECT(vec, ...) (select(vec, NUM_ARGS(__VA_ARGS__), __VA_ARGS__))

#endif // CODERODDE_H


The test driver main.cpp:

#include 
#include 
#include 
#include "coderodde.h"

using std::cout;
using std::endl;
using std::string;
using std::vector;
using coderodde::select;

void test_smart(const vector& input) 
{
    const size_t N = input.size();

    for (size_t ca = 0; ca & input) 
{
    const size_t N = input.size();

    for (size_t ca = 0; ca  string_vec {"A", "B", "C", "D"};
    cout << "Smart:" << endl;
    test_smart(string_vec);
    cout << "Stupid:" << endl;
    test_stupid(string_vec);
    return 0;
}

Solution

You can avoid problems related to variadic macros and problems related to C-style variadic functions altogether by creating a function that takes an std::initializer_list directly:

template
std::vector select(const std::vector& vec, std::initializer_list indices)
{
    std::vector ret;
    for (std::size_t ind: indices) 
    {
        ret.push_back(vec.at(ind));
    }
    return ret;
}


Moreover, it is the easiest to read of the three implementations.

Code Snippets

template<class T>
std::vector<T> select(const std::vector<T>& vec, std::initializer_list<std::size_t> indices)
{
    std::vector<T> ret;
    for (std::size_t ind: indices) 
    {
        ret.push_back(vec.at(ind));
    }
    return ret;
}

Context

StackExchange Code Review Q#83115, answer score: 5

Revisions (0)

No revisions yet.