2
\$\begingroup\$

This class was inspired from Microsoft's array_view.

It intended to use with mmap-ed memory for easily check bounds and so on.

I probably will need to throw exceptions, but this is not my usual way of working.

Any comments are welcome.

#include <cstring>

class BlobRef{
public:
    BlobRef(const void *mem, size_t const size) noexcept :
                mem_( (const char *) mem),
                size_(size){}

    // for test purposes
    BlobRef(const char *s) noexcept :
                BlobRef(s, strlen(s) + 1){}

    template<size_t N>
    BlobRef(const char(&mem)[N]) noexcept:
                BlobRef(mem, N){}

public:
    bool empty() const noexcept{
        return mem_ == nullptr || size_ == 0;
    }

    size_t size() const noexcept{
        return size_;
    }

    const void *data_() const noexcept{
        return mem_;
    }

public:
    const void *safeAccessMemory(size_t const pos, size_t const size) const noexcept{
        if (pos + size <= size_)
            return & mem_[pos];
        else
            return nullptr;
    }

    template <class T>
    const T *as(size_t const pos = 0, size_t const elements = 1) const noexcept{
        return (const T *) safeAccessMemory(pos, elements * sizeof(T));
    }

private:
    const char  *mem_ = nullptr;
    size_t      size_ = 0;
};


#include <cstdint>
#include <assert.h>
#include <endian.h>

int test_blobref(){
    constexpr size_t SIZE = 256;
    char mem[SIZE];

    for(int i = 0; i < SIZE; ++i)
        mem[i] = (char) i;

    BlobRef br{ mem };

    assert(*br.as<uint16_t>(0x00) == htobe16(0x0001)    );
    assert(*br.as<uint16_t>(0x0E) == htobe16(0x0E0F)    );
    assert(*br.as<uint32_t>(0x10) == htobe32(0x10111213)    );

    {
        const char *s = br.as<char>('a');
        assert(strncmp(s, "abcde", 5) == 0);
    }

    {
        struct TestStruct{
            uint16_t    i;
            char        c;
            char        s[4];
        }__attribute__((__packed__));

        const TestStruct *st = br.as<TestStruct>(0x50);
        assert(st->i == htobe16(0x5051)     );
        assert(st->c == 0x52            );

        const char *s1 = st->s;
        const char s2[] = { 0x53, 0x54, 0x55, 0x56, 0x57 };
        assert(strncmp(s1, s2, sizeof s2) == 0);
    }

    {
        size_t const pos = SIZE / sizeof(uint64_t);

        const uint64_t *u64 = br.as<uint64_t>(0, pos);
        assert(u64 != nullptr   );
        assert(u64[      0] == htobe64(0x0001020304050607)  );
        assert(u64[pos - 1] == htobe64(0xf8f9fafbFCFDFEFF)  );
    }

    {
        size_t const pos = SIZE / sizeof(uint64_t) ;

        const uint64_t *u64 = br.as<uint64_t>(0, pos + 1 );
        assert(u64 == nullptr   );
    }

    return 0;
}

int main(){
    test_blobref();
}
\$\endgroup\$
5
  • \$\begingroup\$ You have a big flaw there. Why would you use strlen if you're sure if the memory given is not string? strlen counts only until 0, so the code contains UB \$\endgroup\$ Commented Sep 11, 2016 at 12:21
  • \$\begingroup\$ testing purposes with C strings. not intended to be used in "real" code. but yes, I missed +1. FIXED. I doubt I'll ever need templated constructor as well. \$\endgroup\$
    – Nick
    Commented Sep 11, 2016 at 12:24
  • \$\begingroup\$ "not intended to be used in "real" code". Does it work? Have you tried it? Does the compiler even want to compile it? Is the output as expected? \$\endgroup\$
    – Mast
    Commented Sep 11, 2016 at 12:27
  • \$\begingroup\$ yes, cstring constructor works. BlobRef br{ "hello" }; will execute it and will create 6 bytes blob. I guess it will be useful for patter matching. \$\endgroup\$
    – Nick
    Commented Sep 11, 2016 at 12:29
  • 1
    \$\begingroup\$ You may want to read up on Alignment \$\endgroup\$ Commented Sep 11, 2016 at 17:05

1 Answer 1

2
\$\begingroup\$

BlobRef details

  • You should provide a default constructor.
  • safeAccessMemory contains a bug. If you have an empty BlobRef and you call safeAccessMemory(0, 0) you will deference a nullptr (the guard fails).
  • Don't use c-style casts. In your cases you should use reinterpret_cast. This helps readability, search-ability and signals clear intent.
  • as can invoke undefined behaviour if called on certain types. The gritty details are in 3.10.10 of the C++ spec.

If a program attempts to access the stored value of an object through a glvalue of other than one of the following types the behavior is undefined:

the dynamic type of the object.

a cv-qualified version of the dynamic type of the object.

a type similar (as defined in 4.4) to the dynamic type of the object.

a type that is the signed or unsigned type corresponding to the dynamic type of the object.

a type that is the signed or unsigned type corresponding to a cv-qualified version of the dynamic type of the object.

an aggregate or union type that includes one of the aforementioned types among its elements or nonstatic data members (including, recursively, an element or non-static data member of a subaggregate or contained union), a type that is a (possibly cv-qualified) base class type of the dynamic type of the object.

a char or unsigned char type.

My understand is that there's no easy way to make this work (the gsl folks removed theirs)

Unfortunately this would remove the entire essence of this class. One approach you could take to remedy this would be to use type-traits to restrict the function to types for which it will be well defined.

template <class T, typename = std::enable_if_t<std::is_integral<T>::value>>
const T *as(size_t const pos = 0, size_t const elements = 1) const noexcept{
    return (const T *) safeAccessMemory(pos, elements * sizeof(T));
}
\$\endgroup\$
5
  • \$\begingroup\$ can you elaborate why I need default constructor? this is not Java :) \$\endgroup\$
    – Nick
    Commented Sep 11, 2016 at 19:36
  • \$\begingroup\$ i "fixed" as() with static_assert + is_pod \$\endgroup\$
    – Nick
    Commented Sep 11, 2016 at 19:45
  • 1
    \$\begingroup\$ @Nick Having a default constructor will make working with the STL containers/algorithms more painless amongst other reasons. \$\endgroup\$ Commented Sep 12, 2016 at 7:53
  • \$\begingroup\$ yes, I added the class to my "bigger" class and already noticed it ;) \$\endgroup\$
    – Nick
    Commented Sep 12, 2016 at 9:31
  • \$\begingroup\$ follow up: codereview.stackexchange.com/questions/141162/… \$\endgroup\$
    – Nick
    Commented Sep 12, 2016 at 15:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.