mardi 24 février 2015

Problems with conditional type-trait Universal Reference

So I have a function I use to check values and throw an exception if the value is not valid, otherwise pass the value back as it was received. I am trying to generalize this routine with universal reverences and type-traits. I feel I am close as my example works in some cases, but not all. It appears to only work with rvalues.



#include <iostream>
#include <utility>
#include <type_traits>
#include <string>
#include <vector>

using namespace std;

struct error_type{};

template <class T>
class has_empty
{
template<class U, class = typename std::enable_if<std::is_member_pointer<decltype(&U::empty)>::value>::type>
static std::true_type check(int);
template <class>
static std::false_type check(...);
public:
static constexpr bool value = decltype(check<T>(0))::value;
};

template <bool invalid, class T, typename E = void>
struct valid {};

template <class T, typename E>
struct valid<false, T, E>
{
static inline T&& check(T&& toCheck, const E& toThrow)
{
if (!toCheck)
std::cout << "NoEmpty Throw" << '\n';
else
std::cout << "NoEmpty valid" << '\n';
return std::forward<T>(toCheck);
}
};

template <class T, typename E>
struct valid<true, T, E>
{
static inline T&& check(T&& toCheck, const E& toThrow)
{
if (toCheck.empty())
std::cout << "HasEmpty Throw" << '\n';
else
std::cout << "HasEmpty valid" << '\n';
return std::forward<T>(toCheck);
}
};

template<typename T
, typename E
, typename = typename std::enable_if<std::is_base_of<error_type, E>::value>::type>
inline T&& do_check(T&& toCheck, const E& toThrow)
{
return valid<has_empty<T>::value, T, E>::check(std::forward<T>(toCheck), toThrow);
}

struct HasEmpty
{
bool empty() {return false;}
};

struct NoEmpty
{
};


int main()
{
error_type e;

cout << has_empty<std::wstring>::value << '\n';
cout << has_empty<std::vector<std::wstring>>::value << '\n';
cout << has_empty<int>::value << '\n';
cout << has_empty<HasEmpty>::value << '\n';
cout << has_empty<NoEmpty>::value << '\n';

do_check(true, e);
do_check(false, e);

do_check(std::string("45"), e);
do_check(HasEmpty(), e);
do_check(std::vector<bool>(), e);
HasEmpty he;
do_check(std::move(he), e);
//do_check(he, e); // does not work, has_empty<T>::value is 0
}


Produces the output



1
1
0
1
0
NoEmpty valid
NoEmpty Throw
HasEmpty valid
HasEmpty valid
HasEmpty Throw
HasEmpty valid


If I uncomment the last line I get the following error:



prog.cpp: In instantiation of 'static T&& valid<false, T, E>::check(T&&, const E&) [with T = HasEmpty&; E = error_type]':
prog.cpp:56:84: required from 'T&& do_check(T&&, const E&) [with T = HasEmpty&; E = error_type; <template-parameter-1-3> = void]'
prog.cpp:87:19: required from here
prog.cpp:30:11: error: no match for 'operator!' (operand type is 'HasEmpty')
if (!toCheck)
^
prog.cpp:30:11: note: candidate is:
prog.cpp:30:11: note: operator!(bool) <built-in>
prog.cpp:30:11: note: no known conversion for argument 1 from 'HasEmpty' to 'bool'


Which appears to that has_empty::value is evaluating to false. I'm sure I could do a different work around to get this to work, so at this point it is kind of academic. Still, any help would be appreciated.


Aucun commentaire:

Enregistrer un commentaire