Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it legal to check whether the address of a subobject lies within the bounds of a containing object

2 Questions:

  1. Is the following code well formed with defined behaviour?

  2. Is there any possible c++ implementation in which it could assert?

Code (c++11 and higher):

#include <cassert>
#include <utility>
#include <ciso646>

template<class T> 
auto to_address(T* p) { return reinterpret_cast<unsigned char const*>(p); }

/// Test whether part is a sub-object of object
template<class Object, class Part>
bool is_within_object(Object& object, Part& part)
{
    auto first = to_address(std::addressof(object)),
                 last = first + sizeof(Object);

    auto p = to_address(std::addressof(part));

    return (first <= p) and (p < last);
}

struct X
{
    int a = 0;

    int& get_a() { return a; }
    int& get_b() { return b; }
private:

    int b = 0;
};

int main()
{
    X x;

    assert(is_within_object(x, x.get_a()));
    assert(is_within_object(x, x.get_b()));
}

Note that a and b have different access specifiers.

like image 334
Richard Hodges Avatar asked Dec 02 '17 17:12

Richard Hodges


1 Answers

Pointer comparison is defined in [expr.rel]/3-4:

Comparing unequal pointers to objects is defined as follows:

  • If two pointers point to different elements of the same array, or to subobjects thereof, the pointer to the element with the higher subscript compares greater.
  • If two pointers point to different non-static data members of the same object, or to subobjects of such members, recursively, the pointer to the later declared member compares greater provided the two members have the same access control and provided their class is not a union.
  • Otherwise, neither pointer compares greater than the other.

If two operands p and q compare equal, p<=q and p>=q both yield true and pq both yield false. Otherwise, if a pointer p compares greater than a pointer q, p>=q, p>q, q<=p, and q=p, and q>p all yield false. Otherwise, the result of each of the operators is unspecified.

What conclusions can we draw from this?

There is a total order of pointers of the same type within an object, but there is no order of pointers to different objects or of different subobjects with different access control. This lack of a general total order of pointers makes is_within_object() not very meaningful. In the cases where you'd expect it to return true, it works. In the cases where you'd expect it to return false, the result of these operators is unspecified? That's not a very useful result.


That said, we do have a giant loophole for this in the form of [comparisons]:

For templates less, greater, less_­equal, and greater_­equal, the specializations for any pointer type yield a strict total order that is consistent among those specializations and is also consistent with the partial order imposed by the built-in operators <, >, <=, >=.

So the following would be well-defined:

template<class T> 
auto byte_address(T& p) {
    return reinterpret_cast<std::byte const*>(std::addressof(p));
}

template<class Object, class Part>
bool is_within_object(Object& object, Part& part)
{
    auto first = byte_address(object);
    auto last = first + sizeof(Object);   
    auto p = byte_address(part);


    return std::less_equal<std::byte*>{}(first, p) &&
        std::less<std::byte*>{}(p, last);
}
like image 159
Barry Avatar answered Oct 21 '22 07:10

Barry