Reputation: 519
struct A
{
};
struct B : A
{
virtual ~B() {}
};
template<typename BASE, typename EXTENDED>
void ASSERT_BASE_EXTENDED()
{
static_assert(static_cast<BASE*>((EXTENDED*)256)==(BASE*)256, "error");
}
I'm looking for a way to have a compile time assertion to check if BASE class is a base of EXTENDED, and they have the same memory address.
In the above example even though B is based on A, when casted to A it has a different memory address, because the virtual function table pointer is actually the first member of B. But I need a check if A is the first member.
The above works OK but is not compile-time, because I'm getting an error "error C2131: expression did not evaluate to a constant" when using VS 2017 compiler.
I'm not interested in "std::is_base_of" because that one ignores checking for the same memory address. Is there another way to do this?
Thanks
Upvotes: 2
Views: 745
Reputation: 391
Limitation: it works only with classes which has constexpr constructors
class Base_A
{
size_t a;
};
class Base_B
{
size_t b;
};
class Derived2: public Base_A, public Base_B
{
size_t x;
};
template<class Derived, class Base>
constexpr bool IsSameAddressCast()
{
Derived drv;
Derived* drv_p = &drv;
Base* base_p = drv_p;
void* drv_v = drv_p;
void* base_v = base_p;
return (drv_v==base_v);
};
static_assert(IsSameAddressCast<Derived2, Base_A>(), "different address");
static_assert(IsSameAddressCast<Derived2, Base_B>(), "different address");// this assert will triggers
Another solution suppose you can access some not static member in base class
class Base_A
{
public:
size_t amember;
};
class Base_B
{
size_t b;
};
class Derived1: public Base_B, public Base_A
{
size_t x;
};
class Derived2: public Base_A, public Base_B
{
size_t x;
};
class Derived3: public Base_B
{
public:
size_t amember;
};
template<class T>
struct Allocator_OffsetOf_ObjData
{
static const size_t value = (size_t)&(((T*)nullptr)->amember);
};
template<class Derived, class Base>
constexpr bool IsSameAddressCast2()
{
static_assert(std::is_base_of<Base, Derived>::value, "not a base class");
return Allocator_OffsetOf_ObjData<Base>::value == Allocator_OffsetOf_ObjData<Derived>::value;
}
static_assert(IsSameAddressCast2<Derived1, Base_A>(), "different base address");// triggers assert
static_assert(IsSameAddressCast2<Derived2, Base_A>(), "different base address");
static_assert(IsSameAddressCast2<Derived3, Base_A>(), "different base address");// triggers assert
Upvotes: 1
Reputation: 2647
Memory addresses are a runtime construct. You cannot check them at compile time because at that point they don’t exist. The same is true for the kind of casting you mention. That happens at runtime entirely. You’ll have to replace static_assert
with a runtime check and error handling, e.g. an assert()
or an exception.
That’s for the imo common use case. For hard-coded memory addresses like in your example the problem is the cast of those address-ints to pointers. The only valid way to do this is a reinterpret_cast (that’s one of the casts the compiler tries for the C-style cast in your example), because the types int and pointer to T are completely unrelated. But reinterpret_cast is not allowed at compile time.
Clang’s error message puts it nicely:
main.cpp:14:38: note: cast that performs the conversions of a reinterpret_cast is not allowed in a constant expression
static_assert(static_cast<BASE*>((EXTENDED*)256)==(BASE*)256, "error");
^
Upvotes: 2