Fil Fil - 2 months ago 10
C++ Question

Convert a vector<T> to initializer_list<T>

Everyone creates

std::vector
from
std::initializer_list
, but what about the other way around?

eg. if you use a
std::initializer_list
as a parameter:

void someThing(std::initializer_list<int> items)
{
...
}


There are times when you have your items in a
vector<T>
instead of a literal list:

std::vector<int> v;
// populate v with values
someThing(v); // boom! No viable conversion etc.


The more general question is: how to create an
stl::initializer_list
from a STL iterable, not just
std::vector
.

Answer

I posted a way that seemed to work but unfortunately caused memory access violations because of how initializer_lists are treated as references to locally-scoped copies of values.

Here's an alternative. A separate function and a separate static initializer list is generated for each possible number of items, which are counted with a parameter pack. This is not thread safe and uses a const_cast (which is considered very bad) to write into the static initializer_list memory. However, it works cleanly in both gcc and clang.

If for some obscure reason you need this problem solved and have no other options, you could try this hack.

#include <initializer_list>
#include <iostream>
#include <stdexcept>
#include <type_traits>
#include <vector>

namespace __range_to_initializer_list {

    constexpr size_t DEFAULT_MAX_LENGTH = 128;

    template <typename V> struct backingValue { static V value; };
    template <typename V> V backingValue<V>::value;

    template <typename V, typename... Vcount> struct backingList { static std::initializer_list<V> list; };
    template <typename V, typename... Vcount>
    std::initializer_list<V> backingList<V, Vcount...>::list = {(Vcount)backingValue<V>::value...};

    template <size_t maxLength, typename It, typename V = typename It::value_type, typename... Vcount>
    static typename std::enable_if< sizeof...(Vcount) >= maxLength,
    std::initializer_list<V> >::type generate_n(It begin, It end, It current)
    {
        throw std::length_error("More than maxLength elements in range.");
    }

    template <size_t maxLength = DEFAULT_MAX_LENGTH, typename It, typename V = typename It::value_type, typename... Vcount>
    static typename std::enable_if< sizeof...(Vcount) < maxLength,
    std::initializer_list<V> >::type generate_n(It begin, It end, It current)
    {
        if (current != end)
            return generate_n<maxLength, It, V, V, Vcount...>(begin, end, ++current);

        current = begin;
        for (auto it = backingList<V,Vcount...>::list.begin();
             it != backingList<V,Vcount...>::list.end();
             ++current, ++it)
            *const_cast<V*>(&*it) = *current;

        return backingList<V,Vcount...>::list;
    }

}

template <typename It>
std::initializer_list<typename It::value_type> range_to_initializer_list(It begin, It end)
{
    return __range_to_initializer_list::generate_n(begin, end, begin);
}

int main()
{
    std::vector<int> vec = {1,2,3,4,5,6,7,8,9,10};
    std::initializer_list<int> list = range_to_initializer_list(vec.begin(), vec.end());
    for (int i : list)
        std::cout << i << std::endl;
    return 0;
}