Header file staticif.hpp

#define SIPLASPLAS_UTILITY_STATICIF_HPP 

#include "meta.hpp"

#include "identity.hpp"

#include <utility>

namespace cpp
{
    namespace detail
    {
        template <bool Condition>
        class If;
        
        template <>
        class If<false>;
    }
    
    template <bool Condition, typename ThenBody, typename ... Args>
    auto staticIf(const ThenBody& thenBody, Args&&... args);
}

Class template cpp::detail::If<Condition>

template <bool Condition>
class If
{
public:
    template <typename T>
    class ElseBypass;
    
    template <typename T>
    class ElseBypass<T&>;
    
    template <typename Body, typename ... Args>
    constexpr typename std::enable_if<
    !std::is_void<decltype(body(::cpp::Identity(), std::forward<Args>(args)...))>::value,
    ElseBypass<decltype(body(::cpp::Identity(), std::forward<Args>(args)...))>
    >::type Then(const Body& body, Args&&... args);
    
    template <typename Body, typename ... Args>
    constexpr typename std::enable_if<
    std::is_void<decltype(body(::cpp::Identity(), std::forward<Args>(args)...))>::value,
    If&
    >::type Then(const Body& body, Args&&... args);
    
    template <typename Body, typename ... Args>
    constexpr void Else(const Body&, Args&&...);
};

Implements the then branch of an static conditional.

The cpp::detail::If template implements the internals of an static_if(). The main template implements the true condition branch (The branch executing the then body). Returning values in this case may not be as efficient as possible since the returned value should bypass the then body (This involves two move operations, see cpp::If::ElseBypass):

std::string str = If<true>().Then([](auto identity)
{
    return "hello, world!"s; // the string is moved twice
                             // to exit the static if.
});

Template parameter cpp::detail::If<Condition>::Condition

bool Condition

If condition. True in the main template.


Function template cpp::staticIf<Condition, ThenBody, Args...>

template <bool Condition, typename ThenBody, typename ... Args>
auto staticIf(const ThenBody& thenBody, Args&&... args);

An static conditional allows to conditionally evaluate some code depending on the value of a compile time property. The body of the conditional is implemented by user provided functions.

template<typename T>
void f(T value)
{
    cpp::staticIf<std::is_integral<T>::value>([&](auto identity)
    {
        std::cout << "the value is integral\n";
    });

    ...
}

The body of the untaken conditional path shall not be evaluated by the compiler and can contain ill formed code. NOTE: This behavior relies on the two phase template processing scheme, so the statement above is only true for entities that will be evaluated in the instantiation phase only:

template<typename T>
void foo(T value)
{
   int i = 0;

   // The condition is false, so the code
   // inside the if "should" not be evaluated:
   cpp::staticIf<false>([&](auto identity)
   {
       // Ok: value depends on T template parameter.
       value.TheMostBizarreMethodName();

       // ERROR: 'int' type is not class/struct/union.
       i.method();

       // Ok: identity(i) depends on a template parameter.
       identity(i).method();
   });
}

As the example shows, the conditional body takes an identity parameter that can be used to force the evaluation of an expression at the second phase.

The static conditional expression also provides an else sentence in the form of an Else() method:

template<typename T>
void foo(T value)
{
   int i = 0;

   cpp::staticIf<false>([&](auto identity)
   {
       value.TheMostBizarreMethodName();
       i.method();
       identity(i).method();
   }).Else([&](auto identity)
   {
       std::cout << i << std::endl;
   });
}

cpp::staticIf() supports returning values from the conditional body too:

template<typename T>
T twice(const T& value)
{
    return cpp::staticIf<std::is_class<T>::value>([&](auto)
    {
        return value.twice();
    }).Else([&](auto)
    {
        return value * 2;
    });
}

note this has some caveats:

Template parameter cpp::staticIf<Condition, ThenBody, Args...>::Condition

bool Condition

Value of the condition. The value should be evaluable at compile time, else compilation fails. \tparam ThenBody Function type with one template-dependent value parameter. \param thenBody Body of the then path of the conditional. Evaluated only if the condition is true. \return An unspecified type implementing the else part of the conditional.