Hello
This is a follow-up question to https://e2e.ti.com/support/development_tools/compiler/f/343/t/387340 , or at least it's closely related.
Let me first briefly explain what I was trying to achieve: I have a custom pool allocator. The pool allocator essentially manages a number of fixed-size blocks, identified by a void pointer (to the beginning of the block). Objects are then placement-constructed into these memory buffers. Finally, I'd like to properly release the objects to their pool, which is working well except if the pointer is to a base-object of the allocated object, in which case I cannot reinterpret_cast to void* as that would not yield the pointer to the memory block, i.e.
struct A { int x; };
struct B { int y; };
struct C : A, B {};
void *allocate(size_t nbytes);
void deallocate(void* block);
B* ptr = new (allocate(sizeof(C))) C(); // implicit conversion to B*
ptr->~B(); // ouch, non-virtual... however, C* ptr and ptr->~C() would work well here
deallocate( static_cast<void*>(ptr) ); // ouch, does not (necessarily) point to the most-derived object
That is fine, as it matches the usual new/delete semantics, except if we add a virtual destructor to B, in which case delete would correctly free all memory. At the same time, I could simply use dynamic_cast to obtain a pointer to the most-derived object:
struct A { int x; };
struct B { int y; virtual ~B() {} };
struct C : A, B {};
void *allocate(size_t nbytes);
void deallocate(void* block);
B* ptr = new (allocate(sizeof(C))) C(); // implicit conversion to B*
ptr->~B(); // clean up
deallocate( dynamic_cast<void*>(ptr) ); // all is well
The problem I face arises when trying to wrap the memory management in some generic Ptr<T> class. A dynamic_cast<cv void*>(ptr) is only allowed if the static type of ptr is polymorphic. Otherwise I would assume that the pointer is to the most-derived object and would simply use a static_cast to void*. To solve that problem I devised a little SFINAE helper that would tell me if a type T is polymorphic (it would not tell me if T's destructor is virtual, but I don't care, I only want to know if I can dynamic_cast). The code looks as follows:
template<typename T>
struct IsPolymorphic
{
typedef char (&t)[1];
typedef char (&f)[2];
template<typename U>
static U* make();
template<typename U>
static t test(U* ptr, char (*sfinae)[ sizeof(dynamic_cast<const volatile void*>(make<U>())) ] = 0);
static f test(...);
static const int value = (sizeof( test(make<T>()) ) == 1);
};
template<typename T>
const int IsPolymorphic<T>::value;
struct NonPolymorphic
{
int mIAmASimpleStruct;
};
struct Polymorphic
{
int mIHaveAVirtualTable;
virtual ~Polymorphic() {}
};
int main()
{
typedef char static_assert_1[ IsPolymorphic<NonPolymorphic>::value ? -1 : 1 ];
typedef char static_assert_2[ IsPolymorphic<Polymorphic>::value ? 1 : -1 ];
}
[Note: this is very similar to the code in the question that I linked to above]
I used three different compilers to compile this code: GCC 5.3, TI's C6000 CGT 7.4.16, and TI's C6000 CGT 8.1.0. The results were:
GCC 5.3 compiles the code without complaints.
CGT 7.4.16 emits the following error:
> cl6x -mv64+ --rtti test.cpp
"test.cpp", line 11: error: the operand of a runtime dynamic_cast must have a polymorphic class type
detected during:
instantiation of "IsPolymorphic<T>::test [with T=NonPolymorphic]" based on template argument <NonPolymorphic> at line 14
instantiation of class "IsPolymorphic<T> [with T=NonPolymorphic]" at line 33
"test.cpp", line 33: error: the size of an array must be greater than zero
2 errors detected in the compilation of "test.cpp".
>> Compilation failure
CGT 8.1.0 does not complain about the dynamic_cast, but seems to set IsPolymorphic<T>::value == 0 in all cases:
cl6x.exe -mv64+ --rtti test.cpp "test.cpp", line 34: error: the size of an array must be greater than zero 1 error detected in the compilation of "test.cpp". >> Compilation failure
I believe GCC is right (of course I do ;-) ), so is this a bug? Can it be fixed in the 7.4.x series?
Also, is there another way to test at compile time if T is a polymorphic type? Right now I can only think of an intrusive solution which would explicitly tag all polymorphic types; certainly a less than ideal solution.
Regards
Markus