0

I'm getting data from a binary file, reading from file and writing in a vector of unsigned char. I can't edit it, because I'm using a external library.

But the data that I'm reading from file is a 16 bits image, and I'd like to put the data in a vector of unsigned short

Maybe I can do a cast for it?

Rgds.

Etore Marcari Jr.
  • 572
  • 2
  • 10
  • 19

5 Answers5

6

A generic approach (not bullet proof):

#include <vector>
#include <iostream>
#include <iterator>
#include <algorithm>

typedef unsigned char u8;
typedef unsigned short u16;

u16 combine_two_bytes(u8 a, u8 b) {
    return a | (b << 8);
}

template<typename InIter, typename OutIter, typename InT, typename OutT>
void combine_pairs(InIter in, InIter in_end, OutIter out, OutT (*func)(InT, InT)) {
    while(1) {
        if(in == in_end) {
            break;
        }

        InT &left = *in++;

        if(in == in_end) {
            break;
        }

        InT &right = *in++;

        *out++ = func(left, right);
    }
}

int main() {
    using namespace std;    // lazy

    u8 input[] = { 1, 2, 3, 4, 5, 6, 7, 8 };
    const size_t in_size = sizeof(input) / sizeof(*input);
    u16 output[in_size / 2];

    cout << "Original: ";
    copy(input, input + in_size, ostream_iterator<int>(cout, " "));
    cout << endl;

    combine_pairs(input, input + in_size, output, combine_two_bytes);

    cout << "Transformed: ";
    copy(output, output + in_size / 2, ostream_iterator<int>(cout, " "));
    cout << endl;

    return 0;
}
strager
  • 88,763
  • 26
  • 134
  • 176
  • 2
    Very nice. One minor critique is that your code is limited to random access containers because of the statement "i += 2" in your for loop. If you change that to "std::advance(i, 2)" it will work for all container (and will continue to be constant time for random access iterators). – R Samuel Klatchko Dec 14 '09 at 07:05
  • @Klatchko, Thanks for your comment. You're right in that the code was limited to random-access iterators. However, even when using `std::advance` I had a check against `i + 1` which can't be done using all containers. I've reworked the code and hopefully it works better now (and is more type-safe). – strager Dec 14 '09 at 09:28
6

If you just want to convert from one type to the other then use the standard constructor. As long as the iterators value type is auto convertible to the destination vectors value type the compiler will do the auto conversion between the two types. Just use the standard constructor

#include <vector>
#include <algorithm>
#include <iterator>

int main()
{
    std::vector<unsigned char>      a;
    a.push_back((unsigned char)12);
    a.push_back((unsigned char)13);
    a.push_back((unsigned char)14);

    std::vector<unsigned short>     b(a.begin(),a.end());

    // Print out the vector
    std::copy(b.begin(),b.end(),std::ostream_iterator<unsigned short>(std::cout,"\t"));
}

> g++ t.cpp
> ./a.out
12  13 14

If you actually want to convert two bytes into one then some work is required. But it depends if the input data is actually the same endianess as the machine you are on. If you know that it is the same endianess that you just need to cast the input type.

#include <vector>
#include <algorithm>
#include <iterator>

int main()
{
    std::vector<unsigned char>      a;

    // Make sure that the size is correct.
    // ie. An Odd number indicates that something is not quite correct.
    //
    std::vector<unsigned short>     b(static_cast<unsigned short*>(&a[0]),
                                      static_cast<unsigned short*>(&a[a.size()]));

    // Print out the vector
    std::copy(b.begin(),b.end(),std::ostream_iterator<unsigned short>(std::cout,"\t"));
}

Alternatively if you actually need to combine two values into a single value where the endianess is not the same as the target architecture, you can write a special iterator. Something like this:

#include <Converter.h>

int main()
{
    std::vector<unsigned char>      a;

    // Make sure that the size is correct.
    // ie. An Odd number indicates that something is not quite correct.
    //
    std::vector<unsigned short>     b(make_Converter(a.begin()),make_Converter(a.end()));

    // Print out the vector
    std::copy(b.begin(),b.end(),std::ostream_iterator<unsigned short>(std::cout,"\t"));
}

Converter.h

#include <vector>
#include <iostream>
#include <iterator>

template<typename I>
struct Converter
{
    I   iterator;

    typedef typename std::input_iterator_tag                    iterator_category;
    typedef typename std::iterator_traits<I>::value_type        value_type;
    typedef typename std::iterator_traits<I>::difference_type   difference_type;
    typedef typename std::iterator_traits<I>::pointer           pointer;
    typedef typename std::iterator_traits<I>::reference         reference;

    Converter(I iter)
        :iterator(iter)
    {}

    Converter& operator++()
    {
        iterator++;
        return *this;
    }

    Converter operator++(int)
    {
        Converter   tmp(*this);
        this->operator++();

        return (tmp);
    }

    value_type operator*()
    {
        /*
         * The actual calculation done here will depend on the underlying hardware.
         */
        typename std::iterator_traits<I>::value_type val(*iterator);
        val << 8;
        iterator++;
        val |= (*iterator);

        return val;
    }

    bool operator!=(Converter const& rhs)
    {
        return iterator != rhs.iterator;
    }
};

template<typename I>
Converter<I> make_Converter(I iter)
{
    return Converter<I>(iter);
}
Martin York
  • 257,169
  • 86
  • 333
  • 562
4
vector<unsigned char> a = ...;
vector<unsigned short> b(a.begin(), a.end());

But you want valarrays for simple data vectors.

Alex Budovski
  • 17,947
  • 6
  • 53
  • 58
  • I don't know if he wants his vector of n unsigned chars to turn into a vector of n unsigned shorts, or n/2 unsigned shorts. Looks like he's assuming that unsigned short is 2 bytes if he wants n/2 elements. But the question as stated is unclear. – Alok Singhal Dec 13 '09 at 23:50
1

Assuming the binary data in your file is in little-endian order, I would do this the simple way:

vector<unsigned char> a = ...;

std::vector<unsigned short> b;
b.reserve( a.size() / sizeof(unsigned short) );

for( std::vector<unsigned char>::const_iterator i=a.begin(); i!=a.end(); i+=2 )
{
    unsigned short shortValue = *(i+1);
    shortValue <<= 8;
    shortValue |= *i;
    b.push_back( shortValue );
}

If the data in your file is big-endian you will need to compose the short value the other way round. You should also guard against the number of entries in "a" not being a multiple of 2.

Bids
  • 2,422
  • 18
  • 26
0

disclaimer: I don't have a compiler right now:

vector<unsigned char> vec = getVector();
vector<unsigned short> sv(reinterpret_cast<unsigned short*>(&vec[0]), 
                          reinterpret_cast<unsigned short*>(&vec[vec.size()]));
rlbond
  • 65,341
  • 56
  • 178
  • 228
  • No need to do the casts. Vector have a constructor that takes two iterators. As long as the value type of the iterator is convertible to the value type of the destination container the compiler will do the correct thing. – Martin York Dec 14 '09 at 00:42
  • @Martin York: except he doesn't want to convert the `char`s to `short`s, he wants them reinterpreted as shorts. – rlbond Dec 14 '09 at 01:07
  • @rlbond: That's debatable. It depends how you read the question. Its skewed that way because of the comment under the question. – Martin York Dec 14 '09 at 02:03
  • No debate actually: the data represents "a 16 bits image", i.e the only way to get back each word is to combine 2 bytes. Widening each byte to 16 bits won't help. – MSalters Dec 14 '09 at 08:41