2

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?

Jay Bazuzi
  • 45,157
  • 15
  • 111
  • 168

2 Answers2

2

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)...

Community
  • 1
  • 1
W.F.
  • 13,888
  • 2
  • 34
  • 81
  • Great idea your `typer`; it's the first time I see SFINAE used this way; today I've learned something new. – max66 Apr 30 '17 at 18:37
  • @max66 Well the idea isn't really mine I saw something similar in T.C. answer (I can't find it now maybe he deleted it?) to make it possible to specialize `std::hash` to some templated type. It's really crazy but it should work in some very specific circumstances... like in case of OP's question I guess :) – W.F. Apr 30 '17 at 18:41
  • Great idea, anyway; only a thing: I've corrected an error in my answer that I suppose is present also in your code: you write `assertion_traits::equal` but I suppose should be `assertion_traits::equal` (or something similar to get the type of the contained type; `T` is the container type; otherwise can be `T::value_type` but I don't know if it's valid for every container) – max66 Apr 30 '17 at 18:46
  • @max66 you're right. Thanks! Yes I know there are some tricky stuff going on with e.g. `vector::inner_types...` :) `decltype` is the safer approach :) – W.F. Apr 30 '17 at 18:51
1

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);
 }
max66
  • 65,235
  • 10
  • 71
  • 111
  • @JayBazuzi - you should show us the generic `assertion_traits`; anyway, look at the anser from W.F.: is a lot better than mine (or, at least, is a base for solution better than mine) – max66 Apr 30 '17 at 18:19
  • That helps a lot: `CPPUNIT_ASSERT_EQUAL` is happy with declaration. But the `std::equal()` call fails to compile. – Jay Bazuzi Apr 30 '17 at 18:20
  • http://cppunit.sourceforge.net/doc/1.8.0/struct_cpp_unit_1_1assertion__traits.html – Jay Bazuzi Apr 30 '17 at 18:20
  • This worked, thanks! For anyone else, here's the complete implementation: https://gist.github.com/JayBazuzi/b2f1b6562018b088ac849275e7f24d8f – Jay Bazuzi Apr 30 '17 at 23:32
  • Well, it works for `vector`, but not for `unordered_set`, because it's comparing in iterator order. – Jay Bazuzi Apr 30 '17 at 23:38
  • 1
    @JayBazuzi - I see the problem... well, I suppose that only the solution from W.F. can really solve the problem and create two specializations: one for unordered containers and one for ordered containers; but I don't know what have to do the version for unordered containers; maybe tomorrow I try to think about it al little more. – max66 May 01 '17 at 00:32