-
Notifications
You must be signed in to change notification settings - Fork 9
Open
Description
The tensor ops functions typically require a single/combination of integer arguments to set the bounds for the function. Take for example:
template< std::ptrdiff_t ISIZE, typename VECTOR >
LVARRAY_HOST_DEVICE CONSTEXPR_WITHOUT_BOUNDS_CHECK inline
auto l2NormSquared( VECTOR const & vector )
{
static_assert( ISIZE > 0, "ISIZE must be greater than zero." );
internal::checkSizes< ISIZE >( vector );
auto norm = vector[ 0 ] * vector[ 0 ];
for( std::ptrdiff_t i = 1; i < ISIZE; ++i )
{
norm = norm + vector[ i ] * vector[ i ];
}
return norm;
}
The call looks something like:
l2NormSquared<3>(array)
where the N
(3
) is always required, and the type VECTOR
is deduced. This is a little bit clunky imo. We could do something where we deduced both a size and type. Like so:
#include <tuple>
#include <iostream>
#include <type_traits>
template< typename _T >
struct HasMemberFunction_size_impl
{
private:
template< typename CLASS > static constexpr auto test( int )->decltype( std::is_convertible< decltype( std::declval< CLASS >().size( ) ), int >::value, int() )
{ return CLASS::size(); }
template< typename CLASS > static constexpr auto test( ... )->int
{ return sizeof(CLASS)/sizeof(std::declval<CLASS>()[0]);; }
public:
static constexpr int value = test< _T >( 0 );
};
template< typename CLASS >
static constexpr int HasMemberFunction_size = HasMemberFunction_size_impl< CLASS >::value;
template< int N >
struct Tensor
{
static constexpr int size()
{ return N; }
double & operator[]( int const i )
{
return data[i];
}
double const & operator[]( int const i ) const
{
return data[i];
}
double data[N];
};
template< typename T, int N>
double func_impl( T const & array )
{
double rvalue = array[0]*array[0];
for( int i=1 ; i<N ; ++i )
{
rvalue += array[i]*array[i];
}
return rvalue;
}
template< typename T, int N = HasMemberFunction_size<T> >
typename std::enable_if<!std::is_pointer<T>::value, double>::type func( T const & array )
{
std::cout<<"Calling reference version"<<std::endl;
return func_impl<T,N>(array);
}
template< int N, typename T >
typename std::enable_if<std::is_pointer<T>::value, double>::type func( T const array )
{
std::cout<<"Calling pointer version"<<std::endl;
return func_impl<T,N>(array);
}
////////////////////////////////////////////
int main()
{
Tensor<3> t;
t[0] = 1;
t[1] = 2;
t[2] = 3;
double a[3] = {1,2,3};
double * const pa = a;
std::cout<<func(t)<<std::endl;
std::cout<<func(a)<<std::endl;
std::cout<<func<3>(pa)<<std::endl;
}
Here it is in a compiler explorer:
https://godbolt.org/z/K1P4T9qdE
If we were to standardize the way we return compile time sizes, this works fairly well. With a raw pointer you would still require the specification of N
. With multi-dimensions this would be more complicated...but I don't think it is too prohibitive, and the usability is certainly nicer.
Metadata
Metadata
Assignees
Labels
No labels