SFINAE failing when evaluating a constexpr in a template parameter? - c++

For some reason, this constexpr is not being evaluated correctly in a template parameter context:
#include <iostream>
#include <functional>
namespace detail
{
// Reason to use an enum class rahter than just an int is so as to ensure
// there will not be any clashes resulting in an ambigious overload.
enum class enabler
{
enabled
};
}
#define ENABLE_IF(...) std::enable_if_t<(__VA_ARGS__), detail::enabler> = detail::enabler::enabled
#define ENABLE_IF_DEFINITION(...) std::enable_if_t<(__VA_ARGS__), detail::enabler>
namespace detail
{
template <typename T, bool IS_BUILTIN>
class is_value
{
T item_to_find;
std::function<bool(T const& lhs, T const& rhs)> predicate;
public:
constexpr is_value(T item_to_find, std::function<bool(T, T)> predicate)
: item_to_find(item_to_find)
, predicate(predicate)
{}
constexpr bool one_of() const
{
return false;
}
template <typename T1, typename...Ts>
constexpr bool one_of(T1 const & item, Ts const&...args) const
{
return predicate(item_to_find, item) ? true : one_of(args...);
}
};
template <typename T>
class is_value<T, false>
{
T const& item_to_find;
std::function<bool(T const& lhs, T const& rhs)> predicate;
public:
constexpr is_value(T const& item_to_find, std::function<bool(T const&, T const&)> predicate)
: item_to_find(item_to_find)
, predicate(predicate)
{}
constexpr bool one_of() const
{
return false;
}
template <typename T1, typename...Ts>
constexpr bool one_of(T1 const & item, Ts const&...args) const
{
return predicate(item_to_find, item) ? true : one_of(args...);
}
};
}
// Find if a value is one of one of the values in the variadic parameter list.
// There is one overload for builtin types and one for classes. This is so
// that you can use builtins for template parameters.
//
// Complexity is O(n).
//
// Usage:
//
// if (is_value(1).one_of(3, 2, 1)) { /* do something */ }
//
template <typename T, ENABLE_IF(!std::is_class<T>::value)>
constexpr auto const is_value(T item_to_find, std::function<bool(T, T)> predicate = [](T lhs, T rhs) { return lhs == rhs; })
{
return detail::is_value<T, true>(item_to_find, predicate);
}
template <typename T, ENABLE_IF(std::is_class<T>::value)>
constexpr auto const is_value(T const& item_to_find, std::function<bool(T const&, T const&)> predicate = [](T const& lhs, T const& rhs) { return lhs == rhs; })
{
return detail::is_value<T, false>(item_to_find, predicate);
}
template <int I, ENABLE_IF(is_value(I).one_of(3,2,1))>
void fn()
{
}
int main()
{
fn<3>();
std::cout << "Hello, world!\n" << is_value(3).one_of(3,2,1);
}
I've tested this with clang, g++ and vc++. Each had different errors:
clang
source_file.cpp:98:5: error: no matching function for call to 'fn'
fn<3>();
^~~~~
source_file.cpp:92:10: note: candidate template ignored: substitution failure [with I = 3]: non-type template argument is not a constant expression
void fn()
^
1 error generated.
g++
source_file.cpp: In function ‘int main()’:
source_file.cpp:98:11: error: no matching function for call to ‘fn()’
fn<3>();
...
vc++
source_file.cpp(91): fatal error C1001: An internal error has occurred in the compiler.
(compiler file 'msc1.cpp', line 1421)
...
Is my code invalid or are the compilers just not up to the job yet?

Your code is invalid. The compiler (GCC7.1 for me) provides helpful error that allows us to solve this problem.
Issue:
...\main.cpp|19|note: 'detail::is_value<int, true>' is not literal because:|
...\main.cpp|19|note: 'detail::is_value<int, true>' has a non-trivial destructor|
The reason detail::is_value does not have a trivial destructor is due to the std::function<> member; std::function<> might perform dynamic memory allocation (among other reasons), thus it is not trivial. You have to replace it with a trivially destructible type; I present a simple solution below.
Note: Even if std::function<> was trivially destructible, its operator() does not seem to be declared as constexpr (see: http://en.cppreference.com/w/cpp/utility/functional/function/operator()), so it would not work either.
Sample working code (adapt as needed):
#include <iostream>
#include <functional>
namespace detail
{
// Reason to use an enum class rahter than just an int is so as to ensure
// there will not be any clashes resulting in an ambigious overload.
enum class enabler
{
enabled
};
}
#define ENABLE_IF(...) std::enable_if_t<(__VA_ARGS__), detail::enabler> = detail::enabler::enabled
#define ENABLE_IF_DEFINITION(...) std::enable_if_t<(__VA_ARGS__), detail::enabler>
namespace detail
{
// notice the new template parameter F
template <typename T, typename F, bool IS_BUILTIN>
class is_value
{
T item_to_find;
F predicate;
public:
constexpr is_value(T item_to_find, F predicate)
: item_to_find(item_to_find)
, predicate(predicate)
{}
constexpr bool one_of() const
{
return false;
}
template <typename T1, typename...Ts>
constexpr bool one_of(T1 const & item, Ts const&...args) const
{
return predicate(item_to_find, item) ? true : one_of(args...);
}
};
template <typename T, typename F>
class is_value<T, F, false>
{
T const& item_to_find;
F predicate;
public:
constexpr is_value(T const& item_to_find, F predicate)
: item_to_find(item_to_find)
, predicate(predicate)
{}
constexpr bool one_of() const
{
return false;
}
template <typename T1, typename...Ts>
constexpr bool one_of(T1 const& item, Ts const&... args) const
{
return predicate(item_to_find, item) ? true : one_of(args...);
}
};
}
// sample predicate
template<class T>
struct default_compare
{
constexpr bool operator()(T const& lhs, T const& rhs) const
noexcept(noexcept(std::declval<T const&>() == std::declval<T const&>()))
{
return lhs == rhs;
}
};
// Find if a value is one of one of the values in the variadic parameter list.
// There is one overload for builtin types and one for classes. This is so
// that you can use builtins for template parameters.
//
// Complexity is O(n).
//
// Usage:
//
// if (is_value(1).one_of(3, 2, 1)) { /* do something */ }
//
template <typename T, typename F = default_compare<T>, ENABLE_IF(!std::is_class<T>::value)>
constexpr auto const is_value(T item_to_find, F predicate = {})
{
return detail::is_value<T, F, true>(item_to_find, predicate);
}
template <typename T, typename F = default_compare<T>, ENABLE_IF(std::is_class<T>::value)>
constexpr auto const is_value(T const& item_to_find, F predicate = {})
{
return detail::is_value<T, F, false>(item_to_find, predicate);
}
template <int I, ENABLE_IF(is_value(I).one_of(3,2,1))>
void fn()
{
}
int main()
{
fn<3>();
std::cout << "Hello, world!\n" << is_value(3).one_of(3,2,1);
}

Related

Error: Variadic template class has incomplete type

I have the code:
#include <unordered_set>
template<typename First, typename Enable = void, typename ... T>
class converged_is_exactly_equal_functor;
template<typename ... T>
bool converged_is_exactly_equal(const T& ...);
template <typename T, typename std::enable_if<std::is_arithmetic<T>::value || std::is_enum<T>::value>::type* = nullptr>
bool is_exactly_equal(const T other, const T one) {
return (other == one);
}
template<typename First, typename ... T>
class converged_is_exactly_equal_functor<First, std::enable_if<sizeof...(T) == 1>, T ...>
{
private:
static std::unordered_set<First*> visited_values;
void visit_value (const First& value_to_visit) {
visited_values.insert(&value_to_visit);
}
bool is_visited (const First& value_to_check) {
return (visited_values.find(&value_to_check) != visited_values.end());
}
public:
converged_is_exactly_equal_functor(void){}
bool operator () (const First& first_arg, const T& ... expanded_args) const {
if (!is_visited(first_arg)) {
visit_value(first_arg);
return is_exactly_equal(first_arg, expanded_args ...);
}
return true;
}
};
template<typename First, typename ... T>
std::unordered_set<First*> converged_is_exactly_equal_functor<First, std::enable_if<sizeof...(T) == 1>, T ...>::visited_values;
template<typename First, typename ... T>
class converged_is_exactly_equal_functor<First, std::enable_if<sizeof...(T) != 1>, T ...>
{
public:
converged_is_exactly_equal_functor(void){}
bool operator () (const First& first_arg, const T& ... expanded_args) const {
return is_exactly_equal(first_arg, expanded_args ...);
}
};
template<typename ... T>
bool converged_is_exactly_equal(const T& ... expanded_args) {
converged_is_exactly_equal_functor<T ... > my_functor;
return my_functor(expanded_args ...);
}
class a {
public:
a() : dbid(1), lsb(123) {}
int dbid;
long lsb;
};
bool operator == (const a& other, const a& one) {
if (&other == &one)
return true;
return (
converged_is_exactly_equal(other.dbid, one.dbid) &&
converged_is_exactly_equal(other.lsb, one.lsb)
);
}
int main(void) {
a as, bs;
as == bs;
}
Given that class a is simple group of primitive types, why am I receiving the following error:
my_file.cxx: In instantiation of 'bool converged_is_exactly_equal(const T& ...) [with T = {long int, long int}]':
my_file.cxx:690:56: required from here
my_file.cxx:682:48: error: 'converged_is_exactly_equal_functor<long int, long int> my_functor' has incomplete type
converged_is_exactly_equal_functor<T ... > my_functor;
I believe the error has nothing to do with the proprietary data structures, but I don't see why the type could be incomplete. I have the header file for unordered_set included in this same file.
All definitions of is_exactly_equal(T) are done between the forward declarations and the templates' definitions.
Please be as explicit as possible, since I tend to find it complicated to understand template errors in general.
I can provide any more information necessary, but I'll only be back tomorrow. (This one has drained me out :-/)
The problem is in the converged_is_exactly_equal_functor class and in it's use.
You declare it as follows
template<typename First, typename Enable = void, typename ... T>
class converged_is_exactly_equal_functor;
without defining it.
Then you define two specializations: one for the case sizeof...(T) == 1
template<typename First, typename ... T>
class converged_is_exactly_equal_functor<First, std::enable_if<sizeof...(T) == 1>, T ...>
{
// ...
};
and one for the case sizeof...(T) != 1
template<typename First, typename ... T>
class converged_is_exactly_equal_functor<First, std::enable_if<sizeof...(T) != 1>, T ...>
{
// ...
};
You use the class inside converged_is_exactly_equal
template<typename ... T>
bool converged_is_exactly_equal(const T& ... expanded_args) {
converged_is_exactly_equal_functor<T ... > my_functor;
return my_functor(expanded_args ...);
}
that is called, in your programs, two times
converged_is_exactly_equal(other.dbid, one.dbid) &&
converged_is_exactly_equal(other.lsb, one.lsb)
The first time with two int, the second time with two long.
In both case, you declare a converged_is_exactly_equal_functor value with a second template parameter that isn't void, so doesn't matches the specialization, so matches the main template but the main template is declared but not defined.
So the error.
A possible solution: throw away the SFINAE part and simply declare/define the class for a variadic list of following types (the old sizeof...(T) != 0)
template <typename F, typename ... Ts>
class converged_is_exactly_equal_functor
{
public:
converged_is_exactly_equal_functor ()
{ }
bool operator () (F const & fa, Ts const & ... ea) const
{ return is_exactly_equal(fa, ea ...); }
};
and define a specialization for two cases only (the old sizeof...(T) == 1 case)
template <typename First, typename Second>
class converged_is_exactly_equal_functor<First, Second>
{
private:
static std::unordered_set<First const *> visited_values;
void visit_value (First const & value_to_visit) const
{ visited_values.insert(&value_to_visit); }
bool is_visited (First const & value_to_check) const
{ return (visited_values.find(&value_to_check) != visited_values.end()); }
public:
converged_is_exactly_equal_functor ()
{ }
bool operator () (First const & fa, Second const & sa) const
{
if ( false == is_visited(fa) )
{
visit_value(fa);
return is_exactly_equal(fa, sa);
}
return true;
}
};
template <typename First, typename Second>
std::unordered_set<First const *> converged_is_exactly_equal_functor<First, Second>::visited_values;
Observe that I've added some const to let it compile.

Templatize enum class operators

I'm trying to create a flags bitfield using C++11 enum classes. I'm looking for a way to templatize the operators' return types so they can be used as in code below:
#include <iostream>
enum class Flags {
one = 1,
two = 1 << 1,
three = 1 << 2,
four = 1 << 3
};
#define _CONVERT(_operator) \
static_cast<T>(static_cast<int>(lhs) _operator static_cast<int>(rhs))
template <typename T>
T operator & (const Flags& lhs, const Flags& rhs) {
return _CONVERT(&);
}
template <typename T>
T operator | (const Flags& lhs, const Flags& rhs) {
return _CONVERT(|);
}
#undef _convert
int main()
{
Flags flag = Flags::one | Flags::two | Flags::three;
if (flag & Flags::two)
std::cout << "Flag has two" << std::endl;
if (flag & Flags::four)
std::cout << "Flag has four" << std::endl;
std::cout << static_cast<int>(flag) << std::endl;
}
However, there are several problems:
Flags flag = Flags::one | Flags::two | Flags::three; can't deduce type to be Flags
if (flag & Flags::four) can't deduce type to be bool
I'm new to templates and am kinda lost when it comes to template deduction mechanisms. Also, i tried to create create conversion operator
operator bool(const Flags& flag)
but with no result.
First create a helper template:
template<class E>
struct bool_or_enum{
E e;
explicit operator bool()const{return static_cast<bool>(e); }
operator E() const {return e;}
};
Next, create a trait function and type:
namespace magic_operators {
template<class E>
constexpr std::false_type algebraic_enum(E const volatile&) {return {};}
template<class E>
using use_algebra=decltype( algebraic_enum( std::declval<E const volatile&>() ) );
}
Now magic_operators::use_algebra<E> searches using ADL for algebraic_enum overload returning std::true_type on E. This permits enabling the magic anywhere. MSVC 2015 lacks sufficient C++11 support to use the above; replace with traits class.
The meat: our operators. Stick them into a namespace and bring them in with using namespace:
template<class E, std::enable_if_t<magic_operators::use_algebra<E>{}, int> = 0>
bool_or_enum<E> operator&(E const& lhs, E const& rhs){
using U = std::underlying_type_t<E>;
return { E( static_cast<U>(lhs) | static_cast<U>(rhs) ) };
}
And similar for |.
For ~ and ^ you need a bit mask to remain defined behaviour. Have a traits class enum_mask<E> that defaults to E::bit_mask or somesuch to get it.
template<class E, std::enable_if_t<magic_operators::use_algebra<E>{}, int> = 0>
bool_or_enum<E> operator^(E const& lhs, E const& rhs){
using U = std::underlying_type_t<E>;
return { E( enum_mask<E>{} & (static_cast<U>(lhs) ^ static_cast<U>(rhs) ) ) };
}
template<class E, std::enable_if_t<magic_operators::use_algebra<E>{}, int> = 0>
bool_or_enum<E> operator~(E const& e){
using U = std::underlying_type_t<E>;
return { E( enum_mask<E>{} & (~static_cast<U>(e)) ) };
}
This is tricky due to standards requirements on out of gamut enums.
|= and &= isn't hard, but does need to be coded. = and |= and &= etc that support both assignment chaining and implicit bool requires yet more work. I say do not support it.
Oh and mark everything constexpr and add bool_or_enum<E> overloads to the operators.
The above code is not tested or compiled, but the design works.
The end result is:
enum class Bob { a=2, b=7, bit_mask = 0x00ff };
constexpr std::true_type algebraic_enum( Bob const& ){ return {}; }
using namespace algebraic_ops;
int main(){
Bob x=Bob::a;
x = x | Bob::b;
if( x &~ Bob::b ){
std::cout << "cast to bool bitmasking!\n";
}
}
Or somesuch.
While the #yakk-adam-nevraumont is nice and works, I preferred not to have to redefine anything for my enum classes, but rather have them to automatically get operators once I include my inline header...
So, I've done something like this:
#include <type_traits>
namespace EnumUtils {
template <typename T>
constexpr bool is_class() {
if constexpr (std::is_enum_v<T>) {
return !std::is_convertible_v<T, std::underlying_type_t<T>>;
}
return false;
}
template <class EnumType>
struct WrapperImpl {
EnumType e;
constexpr explicit WrapperImpl(EnumType const& en) : e(en) {}
constexpr explicit WrapperImpl(std::underlying_type_t<EnumType> const& en)
: e(static_cast<EnumType>(en)) {}
constexpr explicit operator bool() const { return static_cast<bool>(e); }
constexpr operator EnumType() const { return e; }
constexpr operator std::underlying_type_t<EnumType>() const {
return std::underlying_type_t<EnumType>(e);
}
};
template <class EnumType>
using Wrapper =
std::conditional_t<is_class<EnumType>(), WrapperImpl<EnumType>, void>;
} // namespace EnumUtils
template <class EnumType, class Wrapped = EnumUtils::Wrapper<EnumType>>
constexpr std::enable_if_t<EnumUtils::is_class<EnumType>(), Wrapped> operator&(
EnumType const& first, EnumType const& second) {
return static_cast<Wrapped>(static_cast<Wrapped>(first) &
static_cast<Wrapped>(second));
}
template <class EnumType, class Wrapped = EnumUtils::Wrapper<EnumType>>
constexpr std::enable_if_t<EnumUtils::is_class<EnumType>(), Wrapped> operator|(
EnumType const& first, EnumType const& second) {
return static_cast<Wrapped>(static_cast<Wrapped>(first) |
static_cast<Wrapped>(second));
}
template <class EnumType, class Wrapped = EnumUtils::Wrapper<EnumType>>
constexpr std::enable_if_t<EnumUtils::is_class<EnumType>(), Wrapped> operator^(
EnumType const& first, EnumType const& second) {
return static_cast<Wrapped>(static_cast<Wrapped>(first) ^
static_cast<Wrapped>(second));
}
template <class EnumType, class Wrapped = EnumUtils::Wrapper<EnumType>>
constexpr std::enable_if_t<EnumUtils::is_class<EnumType>(), Wrapped&> operator|=(
EnumType& first, // NOLINT(runtime/references)
EnumType const& second) {
first = (first | second);
return reinterpret_cast<Wrapped&>(first);
}
template <class EnumType, class Wrapped = EnumUtils::Wrapper<EnumType>>
constexpr std::enable_if_t<EnumUtils::is_class<EnumType>(), Wrapped&> operator&=(
EnumType& first, // NOLINT(runtime/references)
EnumType const& second) {
first = (first & second);
return reinterpret_cast<Wrapped&>(first);
}
template <class EnumType, class Wrapped = EnumUtils::Wrapper<EnumType>>
constexpr std::enable_if_t<EnumUtils::is_class<EnumType>(), EnumType> operator~(
EnumType const& first) {
return static_cast<EnumType>(~static_cast<Wrapped>(first));
}

Infinite recursive template instantiation when using Clang while GCC works fine?

the intention of the following c++ code is to wrap the ternary operator (?:) in a separate function, which later on will help with building a syntax tree.
Before looking at real c++ snippet let's take a quick look at what it does in pseudo code:
bool recursive(bool v) {
return v ? v : recursive(v);
}
int main() {
bool r = recursive(true)
}
Unfortunatly Clang has problems with terminating the recursion when the ternary operator (?:) is wrapped within a template function:
/****************** DECLARATIONS ******************/
template<typename T>
constexpr T
recursive(T t);
struct IfCase {
template<typename T>
constexpr T
operator()(T t) const;
};
struct ElseCase {
template<typename T>
constexpr T
operator()(T t) const;
};
#if defined(WORKS)
static constexpr bool
if_then_else_return(bool b, IfCase const& ic, ElseCase const& ec, bool x);
#else
template<typename T, typename IfFunctor, typename ElseFunctor>
static constexpr T
if_then_else_return(T b, IfFunctor const& ic, ElseFunctor const& ec, T x);
#endif
/****************** DEFINITIONS ******************/
template<typename T>
constexpr T
IfCase::operator()(T t) const {
return t;
}
template<typename T>
constexpr T
recursive(T t) {
return if_then_else_return(t, IfCase{}, ElseCase{}, t);
}
template<typename T>
constexpr T
ElseCase::operator()(T t) const {
return recursive(t);
}
#if defined(WORKS)
constexpr bool
if_then_else_return(bool b, IfCase const& ic, ElseCase const& ec, bool x) {
return b ? ic(x) : ec(x);
}
#else
template<typename T, typename IfFunctor, typename ElseFunctor>
constexpr T
if_then_else_return(T b, IfFunctor const& ic, ElseFunctor const& ec, T x) {
return b ? ic(x) : ec(x);
}
#endif
/****************** CALL ******************/
int main() {
constexpr auto r = recursive(true);
}
Build results:
g++ with reg. function (-DWORKS): OK
g++ with tmpl. function: OK
clang++ with reg. function (-DWORKS): OK (Find code & results also at Coliru)
clang++ with tmpl. function: FAIL (Find code & results also at Coliru)
GCC (4.9.2) compiles both variants without an error, but Clang (3.5 to 3.8) fails with the following error message:
main.cpp:56:14: fatal error: recursive template instantiation exceeded maximum depth of 256
return b ? ic(x) : ec(x);
^
/*** the next error messages for lines 64, 38 and 56 are repeated several times ***/
main.cpp:56:22: note: in instantiation of function template specialization 'ElseCase::operator()<bool>' requested here
return b ? ic(x) : ec(x);
^
main.cpp:38:9: note: in instantiation of function template specialization 'if_then_else_return<bool, IfCase, ElseCase>' requested here
return if_then_else_return(t, IfCase{}, ElseCase{}, t);
^
main.cpp:64:21: note: in instantiation of function template specialization 'recursive<bool>' requested here
constexpr auto r = recursive(true);
^
1 error generated.
But why? How can this code be rewritten so that Clang does not complain anymore?
Thank you very much in advance.
EDIT 1:
I've shorted the compiler message, hopefully increasing its readability. For a full backtrace, please take a look at those Coliru links provided above.
Removing the constexpr specifier will work around this Clang error. But this also reduces functionality and thus is not an option.
One workaround is to limit recursion yourself by adding a counter to the template arguments of the constructs participating in recursion, updating the counter on the recursive call, and using partial specialization to terminate recursion.
I made these changes to your program:
Changed IfCase and ElseCase (IfCase only for symmetry) to template classes instead of regular classes with a template member function. This allows partial specialization.
Added an integer counter template argument to ElseCase and recursive().
Incremented the counter when calling recursive() in ElseCase.
Created a partial specialization of ElseCase at an arbitrary recursion depth that does not make a recursive call. The maximum depth should be tuned to avoid the clang++ limit.
Here's the code:
#include <cassert>
/****************** DECLARATIONS ******************/
template<typename T, int N = 0>
constexpr T
recursive(T t);
template<typename T>
struct IfCase;
template<typename T, int N>
struct ElseCase;
#if defined(WORKS)
static constexpr bool
if_then_else_return(bool b, IfCase<bool> const& ic, ElseCase<bool> const& ec, bool x);
#else
template<typename T, typename IfFunctor, typename ElseFunctor>
static constexpr T
if_then_else_return(T b, IfFunctor const& ic, ElseFunctor const& ec, T x);
#endif
/****************** DEFINITIONS ******************/
template<typename T>
struct IfCase {
constexpr T
operator()(T t) const {
return t;
}
};
template<typename T, int N>
constexpr T
recursive(T t) {
return if_then_else_return(t, IfCase<T>{}, ElseCase<T, N>{}, t);
}
template<typename T, int N>
struct ElseCase {
constexpr T
operator()(T t) const {
return recursive<T, N + 1>(t);
}
};
static constexpr int MaxRecursionDepth = 10;
template<typename T>
struct ElseCase<T, MaxRecursionDepth> {
constexpr T
operator()(T t) const {
assert(false); // OK in C++14!
return t;
}
};
#if defined(WORKS)
constexpr bool
if_then_else_return(bool b, IfCase<bool> const& ic, ElseCase<bool> const& ec, bool x) {
return b ? ic(x) : ec(x);
}
#else
template<typename T, typename IfFunctor, typename ElseFunctor>
constexpr T
if_then_else_return(T b, IfFunctor const& ic, ElseFunctor const& ec, T x) {
return b ? ic(x) : ec(x);
}
#endif
/****************** CALL ******************/
int main() {
constexpr auto r = recursive(true);
}
It works at CoLiRu.
I was initially worried about how to detect an actual recursion depth error, as my original partially specialized class would silently return the wrong answer. Since you are using -std=c++14, assertions in constexpr functions are valid, which was news to me. I have updated the code to make use of this.
By using different code paths for runtime and compile time recursion I was able to work around endless recursion:
#include <boost/hana/integral_constant.hpp>
#include <boost/hana/unpack.hpp>
#include <boost/hana/equal.hpp>
#include <type_traits>
#include <tuple>
#include <cassert>
namespace hana = boost::hana;
namespace detail {
/* std::forward_as_tuple(views...) is not constexpr */
template<typename... Xs>
static constexpr auto
forward_as_tuple(Xs&&... xs) {
return std::tuple<Xs...>{
std::forward<Xs>(xs)...
};
}
/* namespace detail */ }
template<typename Condition, typename LastStep, typename RecursionStep>
struct functor_t {
constexpr
functor_t(Condition const c, LastStep ls, RecursionStep rs) : c{c}, ls{ls}, rs{rs} {};
template <typename Args>
constexpr decltype(auto)
eval(std::true_type, Args const& args) const {
return hana::unpack(args, ls);
}
template <typename Args>
constexpr decltype(auto)
eval(std::false_type, Args const& args) const {
auto vt = hana::unpack(args, rs);
return eval( hana::unpack(vt, c), vt);
}
template <typename Args>
constexpr decltype(auto)
eval(hana::true_, Args const& args) const {
return hana::unpack(args, ls);
}
template <typename Args>
constexpr decltype(auto)
eval(hana::false_, Args const& args) const {
auto vt = hana::unpack(args, rs);
return eval( hana::unpack(vt, c), vt);
}
template <typename Args>
decltype(auto)
eval(bool const& b, Args const& args) const {
if (b) {
return hana::unpack(args, ls);
}
auto vt = hana::unpack(args, rs);
return eval(hana::unpack(vt, c), vt);
}
template <typename... Args>
constexpr decltype(auto)
operator()(Args&& ...args) const {
return eval( c(std::forward<Args>(args)...), detail::forward_as_tuple(args...) );
}
Condition const c;
LastStep ls;
RecursionStep rs;
};
struct recurse_t {
template <typename Condition, typename LastStep, typename RecursionStep>
constexpr decltype(auto)
operator()(Condition && c, LastStep && ls, RecursionStep && rs) const {
return functor_t<Condition, LastStep, RecursionStep>{c, ls, rs};
}
};
constexpr recurse_t recurse{};
/****************** TEST ******************/
#include <boost/hana/plus.hpp>
#include <boost/hana/minus.hpp>
#include <boost/hana/equal.hpp>
#include <boost/hana/ext/std/tuple.hpp>
#include <tuple>
struct Condition {
template<typename I, typename S, typename J>
constexpr decltype(auto)
operator()(I const& i, S const& s, J const& j) const{
return (j == hana::int_c<1>);
}
};
struct LastStep {
template<typename I, typename S, typename J>
constexpr decltype(auto)
operator()(I const& i, S const& s, J const& j) const {
return hana::plus(s, i);
}
};
struct RecursionStep {
template<typename I, typename S, typename J>
constexpr decltype(auto)
operator()(I const& i, S const& s, J const& j) const {
return std::make_tuple(i, hana::plus(s,i), j-hana::int_c<1>);
}
};
int main() {
/* compute: 2*10 == 20 */
assert(recurse(Condition{}, LastStep{}, RecursionStep{})(2,0,10) == 20);
static_assert(recurse(Condition{}, LastStep{}, RecursionStep{})(hana::int_c<2>, hana::int_c<0>, hana::int_c<10>) == hana::int_c<20>, "");
assert(
recurse(
[](auto a, auto b, auto c) { return (a == 1); },
[](auto a, auto b, auto c) { return a+b; },
[](auto a, auto b, auto c) { return std::make_tuple(a, a+b, c-1); }
)(2,0,10) == 20
);
}

Implementing a compile-time "static-if" logic for different string types in a container

I'd like to write a function template that operates on a container of strings, for example a std::vector.
I'd like to support both CString and std::wstring with the same template function.
The problem is that CString and wstring have different interfaces, for example to get the "length" of a CString, you call the GetLength() method, instead for wstring you call size() or length().
If we had a "static if" feature in C++, I could write something like:
template <typename ContainerOfStrings>
void DoSomething(const ContainerOfStrings& strings)
{
for (const auto & s : strings)
{
static_if(strings::value_type is CString)
{
// Use the CString interface
}
static_else_if(strings::value_type is wstring)
{
// Use the wstring interface
}
}
}
Is there some template programming technique to achieve this goal with currently available C++11/14 tools?
PS
I know it's possible to write a couple of DoSomething() overloads with vector<CString> and vector<wstring>, but that's not the point of the question.
Moreover, I'd like this function template to work for any container on which you can iterate using a range-for loop.
#include <type_traits>
template <typename T, typename F>
auto static_if(std::true_type, T t, F f) { return t; }
template <typename T, typename F>
auto static_if(std::false_type, T t, F f) { return f; }
template <bool B, typename T, typename F>
auto static_if(T t, F f) { return static_if(std::integral_constant<bool, B>{}, t, f); }
template <bool B, typename T>
auto static_if(T t) { return static_if(std::integral_constant<bool, B>{}, t, [](auto&&...){}); }
Test:
template <typename ContainerOfStrings>
void DoSomething(const ContainerOfStrings& strings)
{
for (const auto & s : strings)
{
static_if<std::is_same<typename ContainerOfStrings::value_type, CString>{}>
([&](auto& ss)
{
// Use the CString interface
ss.GetLength();
})(s);
static_if<std::is_same<typename ContainerOfStrings::value_type, wstring>{}>
([&](auto& ss)
{
// Use the wstring interface
ss.size();
})(s);
}
}
DEMO
You could provide function overloads that do what you need:
size_t getSize(const std::string& str)
{
return str.size();
}
size_t getSize(const CString& str)
{
return str.GetLength();
}
template <typename ContainerOfStrings>
void DoSomething(const ContainerOfStrings& strings)
{
for (const auto & s : strings)
{
...
auto size = getSize(s);
...
}
}
Here is one with a pretty syntax.
The goal is to get rid of the extra ()s in #Piotr's solution.
Lots of boilerplate:
template<bool b>
struct static_if_t {};
template<bool b>
struct static_else_if_t {};
struct static_unsolved_t {};
template<class Op>
struct static_solved_t {
Op value;
template<class...Ts>
constexpr
decltype(auto) operator()(Ts&&...ts) {
return value(std::forward<Ts>(ts)...);
}
template<class Rhs>
constexpr
static_solved_t operator->*(Rhs&&)&&{
return std::move(*this);
}
};
template<class F>
constexpr
static_solved_t<std::decay_t<F>> static_solved(F&& f) {
return {std::forward<F>(f)};
}
template<class F>
constexpr
auto operator->*(static_if_t<true>, F&& f) {
return static_solved(std::forward<F>(f));
}
template<class F>
constexpr
static_unsolved_t operator->*(static_if_t<false>, F&&) {
return {};
}
constexpr
static_if_t<true> operator->*(static_unsolved_t, static_else_if_t<true>) {
return {};
}
constexpr
static_unsolved_t operator->*(static_unsolved_t, static_else_if_t<false>) {
return {};
}
template<bool b>
constexpr static_if_t<b> static_if{};
template<bool b>
constexpr static_else_if_t<b> static_else_if{};
constexpr static_else_if_t<true> static_else{};
Here is what it looks like at point of use:
template <typename ContainerOfStrings>
void DoSomething(const ContainerOfStrings& strings) {
for (const auto & s : strings)
{
auto op =
static_if<std::is_same<typename ContainerOfStrings::value_type,CString>{}>->*
[&](auto&& s){
// Use the CString interface
}
->*static_else_if<std::is_same<typename ContainerOfStrings::value_type, std::cstring>{}>->*
[&](auto&& s){
// Use the wstring interface
};
op(s); // fails to compile if both of the above tests fail
}
}
with an unlimited chain of static_else_ifs supported.
It does not prevent you from doing an unlimited chain of static_else (static_else in the above is just an alias for static_else_if<true>).
One common way to solve this is to extract the required interface out into a trait class. Something like this:
template <class S>
struct StringTraits
{
static size_t size(const S &s) { return s.size(); }
// More functions here
};
template <typename ContainerOfStrings>
void DoSomething(const ContainerOfStrings& strings)
{
for (const auto & s : strings)
{
auto len = StringTraits<typename std::decay<decltype(s)>::type>::size(s);
}
}
// Anyone can add their own specialisation of the traits, such as:
template <>
struct StringTraits<CString>
{
static size_t size(const CString &s) { return s.GetLength(); }
// More functions here
};
Of course, you can then go fancy and change the function itself to allow trait selection in addition to the type-based selection:
template <class ContainerOfStrings, class Traits = StringTraits<typename ContainerOfString::value_type>>
void DoSomething(const ContainerOfStrings& strings)
You could provide two overloads for getting the length:
template<typename T>
std::size_t getLength(T const &str)
{
return str.size();
}
std::size_t getLength(CString const &str)
{
return str.GetLength();
}

Defining hash for vectors: template parameters not deducible in partial specialization

I am trying to define a hasher for vectors. I have a primary template for simple types, and a specialization for classes which have operator().
However, I get an error template parameters not deducible in partial specialization. Could somebody please point out why?
template <typename T> struct hash<vector<T>>
{
size_t operator()(const vector<T> &x) const
{
size_t res = 0;
for(const auto &v:x) {
boost::hash_combine(res,v);
}
return res;
}
};
template <typename T> struct hash<vector<enable_if_t<true_t<decltype(sizeof(declval<T>()()))>::value, T>>>
{
size_t operator()(const vector<T> &x) const
{
size_t res = 0;
for(const auto &v:x) {
boost::hash_combine(res,v());
}
return res;
}
};
I don't really like partial specialization here, especially as it causes code duplication.
template <typename T>
struct hash<vector<T>>
{
template<class T>
static auto call_if_possible(const T& t, int) -> decltype(t()) { return t(); }
template<class T>
static auto call_if_possible(const T& t, ...) -> decltype(t) { return t; }
size_t operator()(const vector<T> &x) const
{
size_t res = 0;
for(const auto &v:x) {
boost::hash_combine(res,call_if_possible(v, 0));
}
return res;
}
};
(If this hash is actually std::hash, then the answer is "don't do it". You may not specialize a standard library template unless the specialization depends on a user-defined type.)
In your second template specialization T inside the enable_if is in a non-deduced context, so the compiler cannot deduce it. It is effectively the same as:
template<typename T>
struct Identity
{
using type = T;
}
template<typename T>
void f(typename Identity<T>::type x){} // T is non-deducible
Moreover, you have a "double" non-deducible context, because an expression containing T inside a decltype is non-deducible.
for what it's worth, here was my first attempt - handles ADL-lookup of hash_value as well as the one in the boost namespace:
#include <iostream>
#include <boost/functional/hash.hpp>
#include <vector>
template<class T>
struct hash_value_defined
{
template<class U> static auto boost_hash_value_test(U*p) -> decltype(boost::hash_value(*p),
void(),
std::true_type());
template<class U> static auto adl_hash_value_test(U*p) -> decltype(hash_value(*p),
void(),
std::true_type());
template<class U> static std::false_type boost_hash_value_test(...);
template<class U> static std::false_type adl_hash_value_test(...);
static constexpr bool boost_value = decltype(boost_hash_value_test<T>(nullptr))::value;
static constexpr bool adl_value = decltype(adl_hash_value_test<T>(nullptr))::value;
static constexpr bool value = boost_value or adl_value;
};
template<class T, class A, std::enable_if_t<hash_value_defined<T>::value> * = nullptr >
size_t hash_value(const std::vector<T, A>& v) {
size_t seed = 0;
for(const auto& e : v) {
boost::hash_combine(seed, e);
}
return seed;
}
int main()
{
using namespace std;
vector<int> x { 1, 2, 3, 4, 5 };
auto h = hash_value(x);
cout << h << endl;
return 0;
}