

Learn to program or die trying 


Page 1 of 1

[ 4 posts ] 

Author 
Message 
albinopapa

Posted: December 19th, 2017, 10:06 am 

Joined: February 28th, 2013, 3:23 am Posts: 2970 Location: Oklahoma, United States

It sure is nice to have all these great features allowing a programmer to be so lazy. So, normally, I put stuff like DotProduct, Normalize, Length and LengthSq in the vector classes that they need to work on, but lately, I've been messing around with templates and some C++17 features as Visual Studio allows. One of them is if constexpr. What this does is allows you to, in a function, determine which code path to COMPILE not just run as long as the condition is a constexpr value. Here is my DotProduct example Code: // Only dealing with float base types; _Vec2<float>, _Vec3<float>
// Just to make code look nicer in function template<class VecType> constexpr void VecType_Error() { static_assert( !(is_vec2 && is_vec3), "Requires VecType to be a _Vec2<float or _Vec3<float>." ); }
template<class VecType> float operator*( const VecType& lhs, const VecType& rhs ) { return DotProduct( lhs, rhs ); }
template<class VecType> float DotProduct( const VecType& lhs, const VecType& rhs ) { // absurd value indicating you passed in something you shouldn't have float result = std::limits<float>::max();
constexpr auto is_vec2 = std::is_same_v<VecType, Vec2>; constexpr auto is_vec3 = std::is_same_v<VecType, Vec3>;
// Test for Vec2 if constexpr( is_vec2 ) { result = lhs.x * rhs.x + lhs.y * rhs.y; } else if constexpr( is_vec3 ) { result = lhs.x * rhs.x + lhs.y * rhs.y + lhs.z * rhs.z; } else { // Will show error message in Output window if path is compiled VecType_Error<VecType>(); } return result; }
Now I can write two function instead of 6. Granted, this could be done without the if constexpr, but you would pay a runtime branching penalty. This way, it's decided at compile time which branch to take.
_________________ If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves.  gameprogrammingpatterns.com





albinopapa

Posted: December 19th, 2017, 10:28 am 

Joined: February 28th, 2013, 3:23 am Posts: 2970 Location: Oklahoma, United States

Not sure about CrossProduct though. The Vec2 type returns a float, and the Vec3 type returns a Vec3. Code: template<class VecType> auto operator%( const VecType& lhs, const VecType& rhs )>decltype( CrossProduct( lhs, rhs ) { VecType_Error<VecType>(); return CrossProduct( lhs, rhs ); }
float CrossProduct( const Vec2& lhs, const Vec2& rhs ) { return (lhs.y * rhs.x)  (lhs.x * rhs.y); } Vec3 CrossProduct( const Vec3& lhs, const Vec3& rhs ) { return { (lhs.y * rhs.z)  (lhs.z * rhs.y), (lhs.z * rhs.x)  (lhs.x * rhs.z), (lhs.x * rhs.y)  (lhs.y * rhs.x) }; }
I wouldn't know how to get the CrossProduct functions to auto deduce their return type since not only are the return types different, but also they way they are calculated. If not possible, then I only saved one function.
_________________ If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves.  gameprogrammingpatterns.com





albinopapa

Posted: December 19th, 2017, 11:08 am 

Joined: February 28th, 2013, 3:23 am Posts: 2970 Location: Oklahoma, United States

Ok, I think I have a way. I just put a public type alias inside those vector classes and use that as the return type. Code: template<class T> class _Vec2 { public: using CrossReturnType = T; };
template<class T> class _Vec3 { public: using CrossReturnType = _Vec3<T>; };
template<class VecType> auto operator*( const VecType& lhs, const VecType& rhs )>typename VecType::CrossReturnType { return CrossProduct( lhs, rhs ); }
template<class VecType> auto CrossProduct( const VecType& lhs, const VecType& rhs )>typename VecType::CrossReturnType { constexpr auto is_vec2 = std::is_same_v<VecType, _Vec2<float>>; constexpr auto is_vec3 = std::is_same_v<VecType, _Vec3<float>>; static_assert( is_vec2  is_vec3, "Not a valid vector or not implemented." );
if constexpr( is_vec2 ) { // return Vec2 cross } else { // return Vec3 cross } }
Haven't tried this yet, still working on some things.
_________________ If you think paging some data from disk into RAM is slow, try paging it into a simian cerebrum over a pair of optical nerves.  gameprogrammingpatterns.com





chili

Posted: December 20th, 2017, 3:25 am 

Site Admin 

Joined: December 31st, 2011, 4:53 pm Posts: 3538 Location: Japan

consexpr if can do some cool shit.
It is not true that without constexpr if you will pay a runtime branching penalty. The compiler will remove the branch for a if( true ) or if( false). Also, for branches where the same path is taken every time, but overhead is negligible since the branch predictor will never fail (or will only fail the first time). There are some exceptions to this rule (which can also often be dealt with with PGO), but in general that is the gist.
The real place that constexpr if shines is when you need to write templated code that will not be guaranteed to be wellformed depending on the template param. In that case, with a normal if it will be compiled, and the compilation will fail (or at least, the template overload will be removed from the candidate list via SFINAE). But with constexpr if, you can have code in the function that is ill formed, and it will not be touched by the compiler if the predicate evaluates to false.
_________________ Chili







Page 1 of 1

[ 4 posts ] 



You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot post attachments in this forum


