Search code examples
c++templatesconstantsc++17array-view

Function template specialization for both view and strided view and const nightmare


I define a class array_view and a class strided_view (think about array_view and strided_array_view http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0122r0.pdf) and i would like to specialize the way i could iterate on it for efficiency Let's say I have a function dispatcher which try to specialize different case.

Lets start with some simple code

template <class T>
    class view
    {};
template <class T>
    class sview
    {};
template<typename T1, typename T2, template <typename> class View1,   template <typename> class View2>
void PixelWiseUnary(const View1<T1>& i_vin, View2<T2>& o_vout)
{       
    PixelWiseUnaryDispatch<T1, T2, View1, View2> dispatcher;
    dispatcher(i_vin, o_vout);
}

Then i define different specialization

// primary use strided view 
template<typename T1, typename T2, template <typename> class View1, template <typename> class View2, typename = void>
struct PixelWiseUnaryDispatch
{
    void operator()(const View1<T1>& i_vin, View2<T2>& o_vout) const
    {
        std::cout << "***************" << std::endl;
        std::cout << "primary template" << std::endl;
        std::cout << "***************" << std::endl;

    }
};

template<typename T1, typename T2, template <typename> class View1, template <typename> class View2>
struct PixelWiseUnaryDispatch<T1,T2, View1, View2,
    std::enable_if_t<(!std::is_same_v<T1,T2> && std::is_same_v<View1<T1>,view<T1>> )&& std::is_same_v<View2<T2>, view<T2>>>
>
{
    void operator()(const View1<T1>& i_vin, View2<T2>& o_vout) const
    {
        std::cout << "***************" << std::endl;
        std::cout << "both view != type" << std::endl;
        std::cout << "***************" << std::endl;

    }
};

template<typename T,template <typename> class View1, template <typename> class View2>
struct PixelWiseUnaryDispatch<T,T, View1, View2,
    std::enable_if_t<(std::is_arithmetic_v<T> && std::is_same_v<View1<T>, view<T>>) && std::is_same_v<View2<T>, view<T>>>
>
{
    void operator()(const View1<T>& i_vin, View2<T>& o_vout) const
    {
        std::cout << "***************" << std::endl;
        std::cout << "both view same type" << std::endl;
        std::cout << "***************" << std::endl;

    }
};

Then define a simple main

void main(void)
{
view<int> vin;
view<float> vinf;
view<int> vout;
sview<int> vsout;
PixelWiseUnary(vin, vsout); //primary template
PixelWiseUnary(vinf, vout); //both view != type
PixelWiseUnary(vin, vout);  //both view same type 
}

All is fine and switch properly

But things goes weird when i try to use const Eg

void main(void)
{
view<const int> vin;
view<const float> vinf;
view<int> vout;
sview<int> vsout;
PixelWiseUnary(vin, vsout); //primary template as expected
PixelWiseUnary(vinf, vout); //both view != type WTF i don't provide specialisation for const (cf https://stackoverflow.com/questions/14926482/const-and-non-const-template-specialization) so i expected primary template
PixelWiseUnary(vin, vout); //both view != type WTF i don't provide specialisation for const and i loose the same type specialization
}
}

I try to add advice from Const and non const template specialization but its change nothing in my case.

What i have missing? Regards

Note : i work with the lastest Visual2017 community edition


Solution

  • I suppose you can solve the problem (1) testing that !std::is_same_v<T1 const, T2 const> (instead of testing that T1 and T2 arn't the same type) for the "both view != type" case (2) use T1 and T2 (instead of T) in the "both view same type" and adding the test std::is_same_v<T1 const, T2 const>

    I mean (I have only a C++14 compiler, so I've used std::is_same<>::value instead of std::is_same_v<>)

    template <typename T1, typename T2,
              template <typename> class View1,
              template <typename> class View2>
    struct PixelWiseUnaryDispatch <T1, T2, View1, View2,
       std::enable_if_t<
             !std::is_same<T1 const, T2 const>::value
          && std::is_same<View1<T1>, view<T1>>::value
          && std::is_same<View2<T2>, view<T2>>::value>
    >
     {
       void operator()(View1<T1> const & i_vin, View2<T2> & o_vout) const
        {
          std::cout << "***************" << std::endl;
          std::cout << "both view != type" << std::endl;
          std::cout << "***************" << std::endl;
        }
     };
    
    template <typename T1, typename T2, template <typename> class View1,
              template <typename> class View2>
    struct PixelWiseUnaryDispatch<T1, T2, View1, View2,
       std::enable_if_t<
             std::is_same<T1 const, T2 const>::value
          && std::is_arithmetic<T1>::value
          && std::is_same<View1<T1>, view<T1>>::value
          && std::is_same<View2<T2>, view<T2>>::value>
    >
     {
       void operator()(View1<T1> const & i_vin, View2<T2> & o_vout) const
        {
          std::cout << "***************" << std::endl;
          std::cout << "both view same type" << std::endl;
          std::cout << "***************" << std::endl;
    
        }
     };
    

    En passant: instead of impose that View1<T1> and View2<T2> are the same type as view<T1> and view<T2>, you can (in your specializations) avoid the use of View1 and View2 and use view directly.

    You can semplify your specializations as follows

    template <typename T1, typename T2>
    struct PixelWiseUnaryDispatch <T1, T2, view, view,
       std::enable_if_t<!std::is_same<T1 const, T2 const>::value>>
     {
       void operator()(view<T1> const & i_vin, view<T2> & o_vout) const
        {
          std::cout << "***************" << std::endl;
          std::cout << "both view != type" << std::endl;
          std::cout << "***************" << std::endl;
        }
     };
    template <typename T1, typename T2>
    struct PixelWiseUnaryDispatch<T1, T2, view, view,
       std::enable_if_t<
             std::is_same<T1 const, T2 const>::value
          && std::is_arithmetic<T1>::value>>
     {
       void operator()(view<T1> const & i_vin, view<T2> & o_vout) const
        {
          std::cout << "***************" << std::endl;
          std::cout << "both view same type" << std::endl;
          std::cout << "***************" << std::endl;
    
        }
     };