Caduchon
Caduchon

Reputation: 5231

Is there an elegant way to instantiate a boost::array from a std::vector?

Writing an interface, I have to convert instances of std::vector<double> to boost::array<double,N>. Each time, by construction (without bug), I'm sure the vector has the right size.

Here is an example of what I'm currently doing (I have about 100 such functions to write in the interface) :

double foo1(const std::vector<double>& x)
{
  assert(x.size() == 4);
  boost::array<double,4> x_;
  for(std::size_t i = 0; i < 4; ++i)
    x_[i] = x[i];
  return foo1_(x_);
}

double foo2(const std::vector<double>& x)
{
  assert(x.size() == 6);
  boost::array<double,6> x_;
  for(std::size_t i = 0; i < 6; ++i)
    x_[i] = x[i];
  return foo2_(x_);
}

Is there a shorter way to do that ?

Note 1: I don't use C++11 for compatibility reasons.

Note 2: I added tag stdarray because it can maybe help even if it's a C++11 tag.

Upvotes: 2

Views: 3173

Answers (3)

Igor R.
Igor R.

Reputation: 15085

Something like this:

#include <boost/array.hpp>
#include <vector>
#include <boost/range/algorithm.hpp>
#include <iostream>
#include <cassert>

template<size_t Size, class Container>
boost::array<typename Container::value_type, Size> as_array(const Container &cont)
{
    assert(cont.size() == Size);
    boost::array<typename Container::value_type, Size> result;
    boost::range::copy(cont, result.begin());
    return result;
}

int main()
{
    // this is C++11 initialization, but the rest of code is C++03-complient
    std::vector<int> v{1, 2, 3};
    boost::array<int, 3> a = as_array<3>(v);
    boost::range::copy(a, std::ostream_iterator<int>(std::cout,", "));
}

But keep in mind that such a "translation" from a run-time to a compile-time container might be quite dangerous, as its correctness relies on assert, which is eliminated in release mode.

Upvotes: 5

Caleth
Caleth

Reputation: 63402

You can template over the number of elements (That's what array<> is doing too). If you order the parameters sensibly, you can specify just the size and have double still deduced

template<std::size_t N, typename T>
boost::array<T, N> from_vector(const std::vector<T> & vec)
{
    assert(x.size() == N);
    boost::array<T, N> res;
    std::copy(vec.begin(), vec.end(), res.begin());
    return res;
}

Which gets used

double foo1(const std::vector<double> & vec)
{
    return foo1_(from_vector<4>(vec));
}

double foo2(const std::vector<double> & vec)
{
    return foo2_(from_vector<6>(vec));
}

But a better idea would be to re-implement foo1_ and foo2_ in terms of iterators (or something like gsl::span). You can assert(std::distance(begin, end) == 4) as needed

Upvotes: 2

Slava
Slava

Reputation: 44288

Write a template function:

template<class T,size_t N>
std::array<T,N> convert( const std::vector<T> & v )
{
    //assert(v.size() == N);
    std::array<T,N> r;
    std::copy( v.begin(), v.end(), r.begin() );
    return r;
}

then your functions will become one liners:

double foo1(const std::vector<double>& x)
{
    return foo1_( convert<double,4>( x ) );
}

live example

Upvotes: 2

Related Questions