I know I can specialize assertion_traits on std::vector<>:
namespace CPPUNIT_NS
{
template <class T> struct assertion_traits<std::vector<T>>
{
inline static bool equal(const std::vector<T>& left, const std::vector<T>& right)
{
return std::equal(left.begin(), left.end(), right.begin(), assertion_traits<std::vector<T>>::equal);
}
inline static string toString(const std::vector<T>& vector)
// etc...
But if I want to CPPUNIT_ASSERT_EQUAL on unordered_set, I have to write another assertion_traits. How can I write one assertion_traits that works on all things with iterators?
I'm guessing I need something like Boosts's range concepts?
You can make use of alias template that would sfinae out types without containing iterator type passed to your specialization e.g.:
#include <vector>
#include <array>
template <class>
struct without_iterator { };
template <class T>
struct X {
static constexpr bool value = false;
};
template <class T, class...>
using typer = T;
template <class T>
struct X<typer<T, typename T::iterator>> {
static constexpr bool value = true;
};
int main() {
static_assert(X<std::vector<int>>::value, "!");
static_assert(X<std::array<int, 4>>::value, "!");
static_assert(!X<without_iterator<int>>::value, "!");
static_assert(!X<int>::value, "!");
}
where X is your CPPUNIT_NS::assertion_traits
[live demo]
To apply it to your solution:
template <class T, class...>
using typer = T;
namespace CPPUNIT_NS
{
template <class T> struct assertion_traits<typer<T, typename T::iterator>>
{
inline static bool equal(const T& left, const T& right)
{
return std::equal(left.begin(), left.end(), right.begin(), assertion_traits<decltype(*(left.begin()))>::equal);
}
inline static string toString(const T& vector)
// etc...
If you'd like you could also test for begin end existance to make sure interface of your T is as expected. You could even test if T comes from std namespace if you'd like (at least in some limited extend).
Edit:
The safer approach would be to stick to:
template <template <class...> class V, class... Ts>
struct X<typer<V<Ts...>, typename V<Ts...>::iterator>> {
static constexpr bool value = true;
};
even though it can't be applied to std::array as using struct X<typer<T, typename T::iterator>> might not be seen by the compiler as specialization of an X template but the redefinition of X (example)...
What about
template <template <typename ...> class C, typename ... T>
struct assertion_traits<C<T...>>
{
inline static bool equal(C<T...> const & left, C<T...> const & right)
{
return std::equal(left.begin(), left.end(), right.begin(),
assertion_traits<decltype(*(left.begin()))>::equal);
}
};
?
Doesn't work with "all things with iterators" (by example: doesn't work with std::array because there is a template argument that isn't a type) but should intercept a lot of they.
A big problem is that can intercept also containers without iterators; I suggest you to follow the example from W.C.; a solution based on typer can intercept std::array and avoid false positives.
Anyway, a full working example
#include <vector>
#include <algorithm>
template <typename T>
struct assertion_traits
{
inline static bool equal (T const & left, T const & right)
{ return left == right; }
};
template <template <typename ...> class C, typename ... T>
struct assertion_traits<C<T...>>
{
inline static bool equal(C<T...> const & left, C<T...> const & right)
{
return std::equal(left.begin(), left.end(), right.begin(),
assertion_traits<decltype(*(left.begin()))>::equal);
}
};
int main ()
{
std::vector<int> v1;
assertion_traits<std::vector<int>>::equal(v1, v1);
}
Related
I do have a generic container like so:
template<typename T>
struct GenericContainer
{
T& at(size_t index)
{
return data.at(index);
}
void append(const T& value)
{
data.push_back(value);
}
std::vector<T> data;
};
As you all know we can't get a non const reference to bool through std::vector.at function.
So this does not compile : bool& b = std::vector<bool>(true).front()
For the same reason i can't call the function at on my GenericContainer.
I would like to enable the function at on my GenericContainer only if i can get a non const reference to T.
I tried to implement a type traits like that but it does not work, it returns true_type for bool and int, i am looking for false_type for bool and true_type for int.
It is inspired from a is_copy_assignable type traits.
template <typename T>
struct is_ref_extractable_from_vector
{
private:
template <class U, class ENABLED = decltype(std::declval<U&>() = std::declval<std::vector<U>>().front())>
static std::true_type try_assignment(U&&);
static std::false_type try_assignment(...);
public:
using type = decltype(try_assignment(std::declval<T>()));
};
template<typename T>
using is_ref_assignable_from_vector_t = typename is_ref_extractable_from_vector<T>::type;
template <typename T>
inline constexpr bool is_ref_assignable_from_vector_v = is_ref_assignable_from_vector_t<T>::value;
static_assert(!is_ref_assignable_from_vector_v<bool>);
static_assert(is_ref_assignable_from_vector_v<int>);
i have no clue of why the assignment in the enable_if clause always compile.
i was hopping SFINAE to discard try_assignment(U&&) overload with U = bool.
thanks for your help.
ps : note that i'm a bit novice in meta programming so i will not be suprised if there is an obvious reason for that result :)
It is only std::vector<bool> that has the broken specification, so the simplest way to write your trait is
template <typename T>
struct is_ref_extractable_from_vector : std::true_type {};
template <>
struct is_ref_extractable_from_vector<bool> : std::false_type {};
Alternatively, you could specialise GenericContainer for bool such that it doesn't use std::vector<bool>
class vector_bool {
// all the members of std::vector
private:
bool * data;
std::size_t size;
std::size_t capacity;
};
template<>
struct GenericContainer<bool> {
bool& at(size_t index)
{
return data.at(index);
}
void append(const bool& value)
{
data.push_back(value);
}
vector_bool data;
}
std::vector<bool> does not return a bool&, but nevertheless this is completely fine:
std::vector<bool> x{0,0,0};
x.at(0) = false;
The reaons is that x.at returns a proxy (of type std::vector<bool>::reference) that allows you to write the element. If you want to check if it returns a non-const reference, you need to check that, not whether you can assign to it.
Alternatively you can tell std::vector<bool> apart from others by comparing its reference and value_type member aliases, because for a "normal" vector reference is just value_type&, while std::vector<bool>::reference is "special" (and reference is what is returned from at):
#include <vector>
#include <type_traits>
template <typename T,
typename U = void>
struct ref_is_ref_value : std::false_type {};
template <typename T>
struct ref_is_ref_value<T,std::enable_if_t< std::is_same_v< typename T::reference, typename T::value_type&>,void>>
: std::true_type {};
static_assert(!ref_is_ref_value<std::vector<bool>>::value);
static_assert(ref_is_ref_value<std::vector<int>>::value);
Of course, you could also just check if value_type is bool, because there is no other weird specialization of std::vector.
If you can use C++20, you can express this notion in a concept like so:
#include <vector>
#include <concepts>
#include <iostream>
template<class T>
concept is_ref_extractable_from_vector = requires(std::vector<T> vec)
{
{vec.front()} -> std::same_as<T&>;
};
int main()
{
std::cout << "Int extractable: " << is_ref_extractable_from_vector<int> << '\n';
std::cout << "Bool extractable: " << is_ref_extractable_from_vector<bool> << '\n';
}
Output:
Int extractable: 1
Bool extractable: 0
Here the concept is_ref_extractable_from_vector<T> has the following requirements: Given an std::vector<T> named vec, vec.front() must compile and the return value of that expression must be the same as a T&.
Then you could use it in your struct like so:
template<typename T>
struct GenericContainer
{
T& at(size_t index) requires is_ref_extractable_from_vector<T>
{
return data.at(index);
}
void append(const T& value)
{
data.push_back(value);
}
std::vector<T> data;
};
Now if you try to call GenericContainer<bool>::at, it will refuse to compile with an error message mentioning the constraint is_ref_extractable_from_vector but you can still use other GenericContainer<bool> functionality.
Your traits will not work because std::declval<bool&>() = std::declval<std::vector<bool>>().front()) is still well-formed even if the latter is an rvalue.
You can determine whether T& can be bound to vector<T>.front() by defining a help function that only accepts lvalue reference:
template <class U>
static void fun(U&);
With this we can define our trait like:
template <typename T>
struct is_ref_extractable_from_vector
{
private:
template <class U>
static void fun(U&);
template <class U, class ENABLED = decltype(fun<U>(std::declval<std::vector<U>>().front()))>
static std::true_type try_assignment(U&&);
static std::false_type try_assignment(...);
public:
using type = decltype(try_assignment(std::declval<T>()));
};
Demo.
I'm writing a utility function to look for elements in containers like so:
#include <algorithm>
#include <type_traits>
// Helper to determine whether there's a const_iterator for T.
template <typename T>
struct hasConstIt {
private:
template<typename C> static char test(typename C::const_iterator*);
template<typename C> static int test(...);
public:
enum { value = sizeof(test<T>(0)) == sizeof(char) };
};
// Check if a container contains an element.
template <typename Container>
typename std::enable_if<hasConstIt<Container>::value, void>::type
struct In {
const Container & container;
typename Container::value_type const & element;
In(const Container & container, typename Container::value_type const & element) :
container(container), element(element) {}
bool operator()() const {
return std::find(container.begin(), container.end(), element) != container.end();
}
};
I took my solution from this stackoverflow question.
When I compile the code with g++ -std=c++17 I get the following error messages:
./util.hpp:19:1: error: cannot combine with previous 'type-name' declaration specifier
struct In {
^
./util.hpp:18:1: error: declaration does not declare anything [-Werror,-Wmissing-declarations]
typename std::enable_if<hasConstIt<Container>::value, void>::type
I have been looking for a solution but couldn't find one. Why is the compiler complaining here?
template <typename Container>
typename std::enable_if<hasConstIt<Container>::value, void>::type
struct In {
is wrong. This is done as either
template <class Container, typename = std::enable_if_t<hasConstIt<Container>::value>>
struct In {
in old-fashioned C++11. Note the second argument: it has default value and is normally never specified explicitly but prevents instantiation when the predicate is unsatisfied.
Or, more modern,
template<class Container> requires (hasConstIt<Container>::value) struct In {
in C++20.
I'm trying to select at compile time between different template implementations depending on whether or not the argument implements a particular function. This is a common question (see this S.O. question and this example referenced by this article. The common answer is "use expression SFINAE".
Most of the examples show how expression SFINAE can be used to select based on the presence of a zero-argument function. I've tried to adapt these to my 1-argument use case by using declval (loosely based off of this example), but I cannot seem to get it to work.
I'm sure I'm doing something wrong in the example below, but I can't figure out what it is. The example is trying to define two versions of a template bool Util::Container::Contains(container, value) which will use the container's built-in find(value) method if it exists, and otherwise fall back to a linear search using std::find(...)
Please note: I know I can make this work by just overloading Contains() for unordered_map, unordered_set, etc., but instead I'd like to figure out this pattern-based approach so that it will automatically delegate to any container's find(value) without requiring an overload to be added.
#include <unordered_set>
#include <unordered_map>
#include <vector>
#include <string>
namespace Util::Container {
namespace Detail
{
template <typename T>
class HasFindMethod
{
private:
typedef char YesType[1];
typedef char NoType[2];
// This is how the examples show it being done for a 0-arg function
//template <typename C> static YesType& Test(decltype(&C::find));
// Here's my attempt to make it match a 1-arg function
template <typename C> static YesType&
Test(decltype(std::declval<C>().find(std::declval<const C::value_type&>())));
template <typename C> static NoType& Test(...);
public:
enum { value = sizeof(Test<T>(0)) == sizeof(YesType) };
};
}
// Fallback: uses std::find() to do the lookup if no type-specific T::find(value) exists
template<typename T>
bool Contains(const T& in_container, const typename T::value_type& in_item)
{
const auto& result = std::find(in_container.cbegin(), in_container.cend(), in_item);
return (result != in_container.cend());
}
// Preferred: use T::find() to do the lookup if possible
template<typename T>
inline typename std::enable_if<Detail::HasFindMethod<T>::value, bool>::type
Contains(const T& in_container, const typename T::value_type& in_item)
{
return (in_container.find(in_item) != in_container.end());
}
}
int main()
{
const std::vector<int> v { 1, 2, 3 };
const std::unordered_map<int, std::string> m { {1,"1" }, {2,"2"} };
const std::unordered_set<std::string> s { "1" , "2" };
// These should use the std::find()-based version of Contains() since vector and unordered_map
// have no find(value_type) method. And they do.
const bool r_v = Util::Container::Contains(v, 2);
const bool r_m = Util::Container::Contains(m, { 2, "2" });
// !!!!!!
//
// This should use the T::find(value_type)-based version of Contains() since
// unordered_set has a find(value_type) method.
//
// But it doesn't --- that's the issue I'm trying to solve.
//
const bool r_s = Util::Container::Contains(s, "2");
}
If anyone can show me how to fix this, I'd very much appreciate it.
FWIW, I'm attempting to implement this in Visual Studio 2017 v15.8
An easy way with decltype is
template<typename C, typename V>
auto Contains(const C& c, const V& value)
-> decltype(std::find(c.cbegin(), c.cend(), value) != c.cend())
{
return std::find(c.cbegin(), c.cend(), value) != c.cend();
}
template <typename C, typename Key>
auto Contains(const C& c, const Key& key)
-> decltype(c.find(key) != c.end())
{
return c.find(key) != c.end();
}
but then when both function are possible you would have ambiguous call.
So just add extra parameter to prioritize the overload:
struct low_priority {};
struct high_priority : low_priority {};
template<typename C, typename V>
auto ContainsImpl(low_priority, const C& c, const V& value)
-> decltype(std::find(c.cbegin(), c.cend(), value) != c.cend())
{
return std::find(c.cbegin(), c.cend(), value) != c.cend();
}
template <typename C, typename Key>
auto ContainsImpl(high_priority, const C& c, const Key& key)
-> decltype(c.find(key) != c.end())
{
return c.find(key) != c.end();
}
template <typename C, typename T>
auto Contains(const C& c, const T& t)
-> decltype(ContainsImpl(high_priority{}, c, t))
{
return ContainsImpl(high_priority{}, c, t);
}
Now about your version, you have several issues
The last one:
// Expected Fallback: uses std::find() to do the lookup if no type-specific T::find(value) exists
template<typename T>
bool Contains(const T&, const typename T::value_type&);
// Expected Preferred: use T::find() to do the lookup if possible
template<typename T>
typename std::enable_if<Detail::HasFindMethod<T>::value, bool>::type
Contains(const T&, const typename T::value_type&);
SFINAE allows to discard overload, but not to prioritize them.
You have to use priority, as shown above, or create exclusive set of overload:
template<typename T>
typename std::enable_if<!Detail::HasFindMethod<T>::value, bool>::type
Contains(const T&, const typename T::value_type&);
template<typename T>
typename std::enable_if<Detail::HasFindMethod<T>::value, bool>::type
Contains(const T&, const typename T::value_type&);
In addition to that, as mentioned in comment map family would use key_type and not value_type.
Then your detection code is buggy,
// This is how the examples show it being done for a 0-arg function
//template static YesType& Test(decltype(&C::find));
No, this detects if C has a method find (without overload).
template <typename C> static YesType&
Test(decltype(std::declval<C>().find(std::declval<const C::value_type&>())));
Here, you use SFINAE, but final type would be the (const_)iterator, and Test<C>(0) won't take that overload (unless iterator can be build from 0 which is not the regular case). Adding extra * is a possibility, then you have pointer on iterator which might be initialized by 0.
Else you might use code provided in your provided link:
namespace detail{
template<class T, typename ... Args>
static auto test_find(int)
-> sfinae_true<decltype(std::declval<T>().find(std::declval<const Arg&>()...))>;
template<class, class ...>
static auto test_find(long) -> std::false_type;
} // detail::
template<class C, typename ... Args>
struct has_find : decltype(detail::test_find<T, Args...>(0)){};
// int has higher priority than long for overload resolution
and then use your traits with std::enable_if has_find<Container, Key>::value.
The immediate problem is that the argument you are passing to Test is not compatible with the YesType version.
For example, Detail::HasFindMethod<std::unordered_set<int>> will result in the following two Test signatures (because find would return an iterator):
static YesType& Test(std::unordered_set<int>::iterator);
static NoType& Test(...);
You try to call Test with the argument 0, which is not convertible to iterator. Hence, the second one is picked.
As a solution, use a pointer:
template <typename C> static YesType&
Test(decltype(std::declval<C>().find(std::declval<const C::value_type&>()))*);
// ^
Then do the check with a nullptr argument:
enum { value = sizeof(Test<T>(nullptr)) == sizeof(YesType) };
Now we would have ambiguity (the Test(...) would also match), so we can make that one a worse match:
template <typename C, class ... Args> static NoType& Test(void*, Args...);
As shown in the other answers, this is still a comparatively convoluted solution (and there are more issues that prevent it from working in your instance, such as ambiguity between the overloads when the enable_if does work). Just explaining the particular stopper in your attempt here.
A simpler (in my opinion) and more readable solution can be achieved using void_t utility:
template <typename T, typename Dummy = void>
struct has_member_find : std::false_type { };
template <typename T>
struct has_member_find<T,
std::void_t<decltype(std::declval<T>().find(std::declval<typename T::value_type &>()))>>
: std::true_type { };
template<typename T>
std::enable_if_t<!has_member_find<T>::value, bool>
Contains(const T& in_container, const typename T::value_type& in_item)
{
const auto& result = std::find(in_container.cbegin(), in_container.cend(), in_item);
return (result != in_container.cend());
}
template<typename T>
std::enable_if_t<has_member_find<T>::value, bool>
Contains(const T& in_container, const typename T::value_type& in_item)
{
return (in_container.find(in_item) != in_container.end());
}
Note that void_t is only available since C++17, however it you don't have a full C++17 support you can define it yourself since it's definition is ridiculously simple:
template< class... >
using void_t = void;
You can learn more about this utility and the pattern it introduces in this paper.
I'm writing a templated singleton superclass, that can provide a thread-local instance or a process-global instance. The code below compiles and technically fits my need.
But, how can I write the function implementation outside of the class declaration? How do I declare the function in the class (is the commented line right)?
All similar questions implement the function in the class declaration.
#include <vector>
#include <type_traits>
using namespace std;
template <class t, bool tls=true>
class A{
public:
typedef std::vector<t> Avector;
// static Avector& getVector();
template <class T=t, bool TLS=tls, typename std::enable_if<!TLS>::type* = nullptr>
static Avector&
getVector(){
static Avector v=Avector();
return v;
}
template <class T=t, bool TLS=tls, typename std::enable_if<TLS>::type* = nullptr>
static Avector&
getVector(){
static thread_local Avector v=Avector();
return v;
}
};
int main(){
vector<int>& vi = A<int>::getVector();
vector<double>& vd = A<double, false>::getVector();
return 0;
}
You can instead write
template<typename T, bool>
struct A
{
typedef std::vector<T> Avector;
static Avector& getVector();
};
template<typename T, bool b>
typename A<T, b>::Avector& A<T, b>::getVector()
{
thread_local typename A<T, true>::Avector v;
return v;
}
template<typename T>
class A<T, false>
{
typedef std::vector<T> Avector;
static Avector& getVector();
};
template<typename T>
typename A<T, false>::Avector& A<T, false>::getVector()
{
static typename A<T, false>::Avector v;
return v;
}
Also, generally singletons shouldn't be used
I was searching an answer, and we got one on Stack Overflow 2 years after you posted your question, here : How to use std::enable_if on method of templated class with seperate declaration and definition via specialization.
First, you have to redefine the method as a template, with another typename, and with default value the class's template.
Second, you have to template<> 2 times on the implementation's side.
// hpp
template<typename T>
class A {
template <class U=T, typename std::enable_if_t<myCondition, bool>>
void myMethod();
}
// cpp
template<typename T>
template <class U=T, typename std::enable_if_t<myCondition, bool>>
void A::myMethod() {
// ...
}
If you don't want to define another template parameter, you can define the restriction as return type. Here we changed bool by void, but it can be whatever you want :
// hpp
template<typename T>
class A {
typename std::enable_if_t<myCondition, void>
myMethod();
}
// cpp
template<typename T>
typename std::enable_if_t<myCondition, void>
A::myMethod() {
// ...
}
I'm currently creating a template container class in which I like to have a sorting ability whenever the template class has the comparison operator.
With SFINAE I can determine if the operator exist during runtime. However, when compiling the code below it of course still tries to compile the line containing sort, which will return compiler errors if the compare operator is not specified for that class.
Is there a way to 'avoid' compiling that line if the comparison operator doesn't exist? Not knowing the naming of such procedure, something like an '#ifdef SFINAE'?
template <class UseClass> class Container {
public:
bool Sort(void) {
if (CHECK::opCompareExists<UseClass>::value) {
sort(m_classlist.begin(),m_classlist.end()); //compile error, of course
return true;
}
return false;
}
private:
vector<UseClass> m_classlist;
};
Maybe I shouldn't use SFINAE at all... maybe it should be a template specification? How would that work (based on automatic detection that the operator is not present)?
std::sort() by default uses the less-than operator (operator<()) to compare elements so you can simply use expression SFINAE to rule out a specific overload if the type doesn't have it:
template<typename T = UseClass>
auto Sort() -> decltype(std::declval<T>() < std::declval<T>(), bool())
{
return true;
}
If substitution fails (SomeClass does not have operator<()) then compilation will fail.
If this wasn't your intention, and instead you wanted to return true if it could sort and false otherwise, then you need a trait class that you can overload based on:
namespace detail
{
template<typename T>
auto has_less_than_impl(int)
-> decltype(std::declval<T>() < std::declval<T>(), std::true_type());
template<typename>
std::false_type has_less_than_impl(...);
}
template<typename T>
struct has_less_than : decltype(detail::has_less_than_impl<T>(0)) { };
template <class UseClass> class Container
{
public:
bool Sort() { return Sort(has_less_than<UseClass>::value); }
private:
bool Sort(std::true_type)
{
sort(m_classlist.begin(), m_classlist.end());
return true;
}
bool Sort(std::false_type) { return false; }
};
Update: As per your comment here is a C++03 implementation:
template <typename T>
class has_less_than {
struct Fallback { bool operator<(T const&); };
struct Derived : T, Fallback {};
template <typename U, U> struct S;
template <typename C> static char (&f(S<bool (Fallback::*)(T const&), &C::operator<>*))[1];
template <typename C> static char (&f(...))[2];
public:
const static bool value = sizeof(f<Derived>(0)) == 2;
};
namespace detail
{
template <bool B, typename R = void>
struct enable_if { typedef R type; };
template <typename R>
struct enable_if<false, R> { };
}
template <class UseClass> class Container {
public:
bool Sort() { return Sort<UseClass>(); }
private:
template <typename T>
bool Sort(typename detail::enable_if<has_less_than<T>::value, int>::type = 0) {
sort(m_classlist.begin(),m_classlist.end());
return true;
}
template <typename T>
bool Sort(typename detail::enable_if<!has_less_than<T>::value, int>::type = 0) {
return false;
}
private:
vector<UseClass> m_classlist;
};