Search code examples
c++templatesstlstdvectorstdarray

C++ template function to concatenate both std::vector and std::array types


I have project where I am working with both fixed and variable length arrays of bytes. I want to have a function that can concatenate two arbitrary containers of bytes and return a single vector. Currently I am using

std::vector<uint8_t> catBytes(uint8_t const* bytes1, size_t const len1, 
                              uint8_t const* bytes2, size_t const len2) {
    std::vector<uint8_t> all_bytes;
    all_bytes.reserve(len1 + len2);
    all_bytes.insert(all_bytes.begin(), bytes1, bytes1 + len1);
    all_bytes.insert(all_bytes.begin() + len1, bytes2, bytes2 + len2);
    return all_bytes;
} // catBytes

However, I would like more generic way to do this, which better uses the capabilities of C++. I do not want to just accept iterators. I am trying to figure out how to accept two arbitrary containers and return a vector containing their contents. Ideally I would also not like to have to know the type inside the vector.

Something like

std::vector<unit_8> v1 = { 1, 2, 3, 4 };
std::array<unit_8, 4> a1 = { 1, 2, 3, 4 };
std::array<unit_8, 2> a2 = { 1, 2 };
auto res1 = concat(v1, a1); // std::vector<uint_8> of size 8
auto res2 = concat(a1, a2); // std::vector<uint_8> of size 6

// or

std::vector<char> v2 = { 1, 2, 3, 4 };
std::array<char, 4> a3 = { 1, 2, 3, 4 };
auto res3 = concat(v1, a1); // std::vector<char> of size 8

I think there is a templated approach to this but I just have not been able to figure it out.


Solution

  • In general, generic + arbitrary, means templates.

    Something like this?

    template<class SizedRange1, class SizedRange2>
    auto concat(SizedRange1 const& r1, SizedRange2 const& r2) {
        std::vector<typename SizedRange1::value_type> ret;
        ret.reserve(r1.size() + r2.size());
    
        using std::begin; using std::end;
        ret.insert(ret.end(), begin(r1), end(r1));
        ret.insert(ret.end(), begin(r2), end(r2));
    
        return ret;
    }
    

    EDIT:

    The advantage of @康桓瑋's solution (which assumes that you are only interested in concatenating contiguous memory containers as you stated) is that you have a single function and you avoid code bloat.

    As you noticed in the comments, the problem with that you can not deduce the element type, which can be anything in principle, so you are back to needing templates anyway.

    Fortunately, you can combine the two approaches and reduce the number of functions generated as machine code, in the case that you have many different input containers, which is combinatorial.

    template<class T>
    std::vector<T> 
    concat_aux(std::span<const T> x, std::span<const T> y) {
      std::vector<T> ret;
      ret.reserve(x.size() + y.size());
      ret.insert(ret.end(), x.begin(), x.end());
      ret.insert(ret.end(), y.begin(), y.end());
      return ret;
    }
    
    template<class ContinuousRange1, class ContinuousRange2>
    auto concat(ContinuousRange1 const& r1, ContinuousRange2 const& r2) {
      return concat_aux<typename ContinousRange1::value_type>(r1, r2);
    }
    

    The advantage is marginal here, but if the function concat is very complicated this last approach will pay off because it will generate code only for the different types of elements, not the number of containers squared in your instantiating code.