Conditional Compilation Based on Template Values

c++ conditional compilation based on member presence

The following example demonstrates a technique that can be used (in C++11, makes use of SFINAE for expressions):

typedef char no;
typedef no (&yes)[2];

no detect_push_front( ... );

template < class C >
auto detect_push_front( C &c ) -> typename std::conditional< false, decltype( c.push_front( std::declval< typename C::value_type >() ) ), yes >::type;

template < class C >
struct has_push_front : std::integral_constant< bool, sizeof( detect_push_front( std::declval< C & >() ) ) == sizeof( yes ) > {};

template < class C >
void add( C &c, typename C::value_type v, std::true_type )
{
c.push_front( v );
}

template < class C >
void add( C &c, typename C::value_type v, std::false_type )
{
c.push_back( v );
}

template < class C >
void add( C &c, typename C::value_type v )
{
add( c, v, has_push_front< C >() );
}

int main()
{
std::vector< int > v;
add( v, 0 );
add( v, 1 );
add( v, 2 );
std::copy( v.begin(), v.end(), std::ostream_iterator< int >( std::cout, " " ) );
std::cout << '\n';

std::list< int > l;
add( l, 0 );
add( l, 1 );
add( l, 2 );
std::copy( l.begin(), l.end(), std::ostream_iterator< int >( std::cout, " " ) );
std::cout << '\n';
}

Output:

0 1 2
2 1 0

Conditional code depending on template parameters comparision

As already said, C++17's if constexpr is the best solution:

template <unsigned long prec1>
DFixed & operator-=(DFixed<prec1> d) {
if constexpr (prec==prec1)
val -= d.val;
else if constexpr (prec<prec1)
val -= d.val/(prec1/prec);
else
val -= d.val*(prec/prec1);

return *this;
}

If you're using C++11, you can use SFINAE with std::enable_if hidden in a REQUIRES macro:

#define REQUIRES(...) typename std::enable_if<(__VA_ARGS__), int>::type = 0
template <unsigned long prec1, REQUIRES(prec==prec1)>
DFixed & operator-=(DFixed<prec1> const & d) {
val -= d.val;
return *this;
}
template <unsigned long prec1, REQUIRES(prec<prec1)>
DFixed & operator-=(DFixed<prec1> const & d) {
val -= d.val/(prec1/prec);
return *this;
}
template <unsigned long prec1, REQUIRES(prec>prec1)>
DFixed & operator-=(DFixed<prec1> const & d) {
val -= d.val*(prec/prec1);
return *this;
}

Note that input parameter d is passed by const reference to avoid unnecessary copy.

Conditional compilation and template

You could place the data in a base class, then use if constexpr:

template<class T>
struct C_data{
T m_result;
};
template<>
struct C_data<void>{
};

template<class T>
class C: C_data<T>
{
static constexpr auto is_void = std::is_same_v<T,void>;
public:
auto f(){
if constexpr(is_void)
return this->m_result;
else
return;
}
void todo(){
if constexpr(is_void)
this->m_result = doit<T>();
else
doit<T>();
}
};

But it can be argued that a the specialization of the class C is cleaner since all member of a template class should depend on all the template parameter (otherwise you should split your class in order to avoid code bloat).

So I would prefer to fully specialize C, and make part of the class C that are independent of T, a base class of C:

class C_base{
//any thing that is independent of T;
};
template<class T>
class C: public C_base{
//any thing that depend on T
};
template<>
class C<void>: public C_base{
//any thing that depend on T;
};

You could also specialize member funtion by member function, but I find it less clean.

You will find this last code structure in almost all headers of standard library implementations.

Conditional compilation of templates

The standard permits, but does not require, compilers to diagnose templates for which no valid instantiation can be generated. This can range from simple syntax errors to your example of a constant false expression in a static_assert. §14.6 [temp.res]/p8:

If no valid specialization can be generated for a template, and that
template is not instantiated, the template is ill-formed, no
diagnostic required.

I'm rather baffled by all this SFINAE machinery, though. A simple

template<typename T, typename... Us>
T* create_if_constructible(Us... args) { return new T(args...); }

already refuses to compile if T is not constructible from the parameter given, so I'm not sure how this complex circumlocution will help you "avoid null pointers".

Regardless, a simple way to make choosing the second function template a compile-time error is to explicitly delete it.

template<typename T, typename... Us>
std::enable_if_t< std::is_constructible<T, Us...>::value == false, T * >
create_if_constructible(Us... args) = delete;

Alternatively, if you are partial to static_asserts, perhaps because of the custom error message, you must ensure that there is theoretically a way to generate a valid instantiation of your template. That means that 1) what you are static_asserting on must depend on a template argument, and 2) there must be theoretically a way for the condition to be true. A simple way is to use an auxiliary template:

template<class> class always_false : std::false_type {};

template<typename T, typename... Us>
std::enable_if_t< std::is_constructible<T, Us...>::value == false, T * >
create_if_constructible(Us... args) {
static_assert( always_false<T>::value, "Class T constructor does not match argument list.");
return nullptr;
}

The key point here is that the compiler cannot assume that always_false<T>::value is always false because it is always possible that there's a specialization later that sets it to true, and so it is not allowed to reject this at template definition time.

Conditional compile based on type

Your issue is that differences in default template arguments don't declare distinct templates. That is why you are getting the redefinition error.

An additional issue is that you need to take the ::value of the std::is_integral and std::is_floating_point traits, as std::enable_if_t expects a bool, not a std::integral_constant.

To fix this, you can use a standard SFINAE trick to declare a template argument of a type which depends on the enable_if_t result:

template<class T, 
class S,
std::enable_if_t<
std::is_floating_point<T>::value && std::is_integral<S>::value
>* = nullptr>
Math::Vec4<T> Colour(S r, S g, S b, S a)
{
return Vec4<T>(r / static_cast<T>(255),
g / static_cast<T>(255),
b / static_cast<T>(255),
a / static_cast<T>(255));
};

template<class T,
class S,
std::enable_if_t<
std::is_integral<T>::value && std::is_floating_point<S>::value
>* = nullptr>
Math::Vec4<T> Colour(S r, S g, S b, S a)
{
return Vec4<T>(static_cast<T>(r * 255),
static_cast<T>(g * 255),
static_cast<T>(b * 255),
static_cast<T>(a * 255));
};


Related Topics



Leave a reply



Submit