1. 4FipS.com
  2. Photos
  3. Videos
  4. Code
  5. Forums
  6. pfQuizzz
  7. About
/*
(c) 2012 +++ Filip Stoklas, aka FipS, http://www.4FipS.com +++
THIS CODE IS FREE - LICENSED UNDER THE MIT LICENSE
ARTICLE URL: http://forums.4fips.com/viewtopic.php?f=3&t=813
*/

#include <vector>
#include <cstdint> // uint8_t, ...
#include <cassert>

/// A reference to an array (data mapper) with a rich STL-like interface, safe & cheep to pass around.
template <typename T>
class Array_ref
{
 public:

    Array_ref() : _data(nullptr), _size(0) {}
    Array_ref(const T &value) : _data(&value), _size(1) {}
    Array_ref(const T *begin, const T *end) : _data(begin), _size(end - begin) { assert(begin <= end); }
    Array_ref(const T *data, size_t size) : _data(data), _size(size) {}
    Array_ref(const std::vector<T> &vec) : _data(vec.empty() ? nullptr : &vec.front()), _size(vec.size()) {}

    bool empty() const { return _size == 0; }

    const T * begin() const { return _data; }
    const T * end() const { return _data + _size; }

    size_t size() const { return _size; }
    const T * data() const { return _data; }

    const T & front() const { assert(!empty()); return _data[0]; }
    const T & back() const { assert(!empty()); return _data[_size - 1]; }

    const T & operator [] (size_t index) const { assert(index < _size); return _data[index]; }

    //... other convenient stuff

 private:

    const T *_data;
    size_t _size;
};

/// A helper that conveniently translates a raw array to the Array_ref.
template <typename T, size_t N>
Array_ref<T> make_array_ref(const T (&array)[N]) // a reference to array is used here, to preserve N
{
    return Array_ref<T>(&array[0], &array[N]);
}

//--- DEMO ---

struct Color { uint8_t r, g, b; };
typedef Array_ref<Color> Color_array_ref;

/// A traditional way to refer data, but it's unsafe and requires 2 params to manage.
void pass_color_data_ugly1(const Color *data, size_t size)
{
    printf("pass_color_data_ugly1():\n");
    for(size_t i = 0; i < size; ++i)
    {
        const Color &c = data[i];
        printf("%02x %02x %02x\n", c.r, c.g, c.b);
    }
}

/// A traditional way to refer data, but it's unsafe and requires 2 params to manage.
void pass_color_data_ugly2(const Color *begin, const Color *end)
{
    printf("pass_color_data_ugly2():\n");
    for(const Color *c = begin; c < end; ++c)
    {
        printf("%02x %02x %02x\n", c->r, c->g, c->b);
    }
}

/// A safe C++ish way to refer data, but it requires an unnecessary initial copy :(
void pass_color_data_ugly3(const std::vector<Color> &data)
{
    printf("pass_color_data_ugly3():\n");
    for(std::vector<Color>::const_iterator c = data.begin(), end = data.end(); c < end; ++c)
    {
        printf("%02x %02x %02x\n", c->r, c->g, c->b);
    }
}

/// A safe way to refer data, no initial copy needed, it's just a mapping!
void pass_color_data_cool(Color_array_ref color_arr)
{
    printf("pass_color_data_cool():\n");
    for(size_t i = 0; i < color_arr.size(); ++i)
    {
        const Color &c = color_arr[i];
        printf("%02x %02x %02x\n", c.r, c.g, c.b);
    }
}

int main()
{
    // This represents raw data. It might be a raw array like this or just
    // a bunch of bytes, somewhere it the middle of a BLOB.
    const Color data[] = {
     { 0xff, 0x00, 0x00 }, { 0x00, 0xff, 0x00 },
     { 0x00, 0x00, 0xff }, { 0x00, 0x00, 0x00 }
    };

    pass_color_data_ugly1(&data[0], sizeof(data) / sizeof(data[0]));
    pass_color_data_ugly2(&data[0], &data[sizeof(data) / sizeof(data[0])]);
    pass_color_data_ugly3(std::vector<Color>(&data[0], &data[sizeof(data) / sizeof(data[0])]));
    pass_color_data_cool(make_array_ref(data)); // Pure beauty, isn't it?

    return 0;
}

// output:
// pass_color_data_ugly1():
// ff 00 00
// 00 ff 00
// 00 00 ff
// 00 00 00
// pass_color_data_ugly2():
// ff 00 00
// 00 ff 00
// 00 00 ff
// 00 00 00
// pass_color_data_ugly3():
// ff 00 00
// 00 ff 00
// 00 00 ff
// 00 00 00
// pass_color_data_cool():
// ff 00 00
// 00 ff 00
// 00 00 ff
// 00 00 00