Preventing upcasts for C style array pointers

Viewed 134
struct Entry
{
    int Data0;
};

struct ExtendedEntry : public Entry
{
    int Data1;
};

I have a simple method expecting a C-style array pointer to Entry like this

void Calculate(Entry* data, int count);

This method obviously fails, when passing a pointer to an array of ExtendedEntrys. How to prevent users from doing so?

ExtendedEntry items[50];
// [...]
Calculate(items, 50);

It's not that I pursue a bullet-proof API, I just want to prevent me and my co-workers from making the same mistake again.

3 Answers

Make a simple wrapper:

template <typename TEntry>
void CalculateSafe(TEntry* data, int count)
{
    static_assert(sizeof(TEntry) == sizeof(Entry), "size mismatch");
    Calculate(data, count);
}

This enables passing any derived type so long as it has the same size, which will solve the problem you're having when a C API needs to do pointer arithmetic in an array.

@John Zwinck's solution is very nice!

Yet, there is an additional approach. You could define the method like this:

template < class T >
void Calculate(T * arr, int count)
{
    static_assert(std::is_base_of<Entry, T>::value, "true");

    for (int i = 0; i < count; i++)
        std::cout << arr[i].Data0 << std::endl;
}

While the template assures the correctness of the implementation, the static_assert assures that you only call this method with pre-defined types.

Or you could declare a deleted overload:

template<class T,std::size_t N>
std::enable_if_t<!std::is_same_v<T,Entry>> 
Calculate(const T(&)[N],int)
  = delete;

You could replace type comparison by type size comparison too.

Related