Transitive template instantiation

121 views Asked by At

I have a conversion template, that should convert between pointers to types if the conversion is safe. That is something like static_cast but allows user defined types.

Example: 2 representations of complex types, one as a struct with 2 members for real/imag, one as a array with 2 elements. It is safe to convert a pointer to one of them to a pointer to the other.

My templates looks like this:

template< typename T_Src, typename T_Dest, typename T_SFINAE = void >
struct SafePtrCast;

template< typename T >
struct SafePtrCast< Complex<T>*, T* >
{
    T*
    operator()(Complex<T>* data)
    {
        return &data->real;
    }
};

template< typename T >
struct SafePtrCast< T*, fftw_complex* >
    :Ptr2Ptr<T, fftw_complex>{};

template< typename T_Src, typename T_Dest = T_Src >
struct Ptr2Ptr
{
    using Src = T_Src;
    using Dest = T_Dest;

    Dest*
    operator()(Src* data) const
    {
        return reinterpret_cast<Dest*>(data);
    }
};

That is, I can convert from Complex* to T* and from T* to fftw_complex*. Semantically this means I could also convert from Complex* to fftw_complex*.

But how can I tell the compiler, that this is ok? I tried with:

template< typename T_Src, typename T_Dest >
struct SafePtrCast<
    T_Src,
    T_Dest,
    void_t< std::result_of_t<
        SafePtrCast<
            std::result_of_t<
                SafePtrCast<T_Src, double* >
            >,
            T_Dest
        >
    > >
>{
    using Conv1 = SafePtrCast< T_Src, double* >;
    using Conv2 = SafePtrCast< double*, T_Dest >;

    T_Dest
    operator()(T_Src&& data) const
    {
        return Conv2(Conv1(std::forward<T_Src>(data)));
    }
};

and use this with a couple of base types (double, float, int...) to allow at least T->base->U which should be enough.

Unfortunately the compiler does not seem to find the specialization for SafePtrCast< Complex, fftw_complex >

Is there a better way to deal with this? Whats wrong with my template?

1

There are 1 answers

2
marom On

What about using a trait:

template< typename T_Src, typename T_Dest, bool  >
struct Ptr2Ptr

// allowed conversion
template< typename T_Src, typename T_Dest>
struct Ptr2Ptr<T_Src, T_Dest, true>
{
    using Src = T_Src;
    using Dest = T_Dest;

    static Dest* conv(Src* data) const
    {
        return reinterpret_cast<Dest*>(data);
    }
};

template <typename T_Src, typename T_Dest>
conv_trait
{
    static const bool enabled = false ;
}
template< typename T_Src, typename T_Dest>
struct SafePtrCast
{
    T_Dest*
    operator()(T_Src* data) const
    {
        return Ptr2Ptr<T_Src, T_Dest, conv_trait<T_Src, T_Dest>::value>::conv(data);
    }
}

Now you can specialize the conv_trais for allowed conversions

template <class T>
conv_trait<Complex, T *>
{
    static const bool value = true ;
}