Is it possible to create a trait to answer if a type comes from std?
Asked Answered
L

1

7

After this question by utilizing ADL one can create a trait to answer if the passed type comes from our namespace:

#include <utility>

namespace helper
{
  template <typename T, typename = void>
  struct is_member_of_sample : std::false_type
  {
  };

  template <typename T>
  struct is_member_of_sample<
      T,
      decltype(adl_is_member_of_sample(std::declval<T>()))> : std::true_type
  {
  };
}

namespace sample
{
  template <typename T>
  auto adl_is_member_of_sample(T && ) -> void;
}

// -- Test it

namespace sample
{
  struct X;
}

struct Y;

static_assert(helper::is_member_of_sample<sample::X>::value, "");
static_assert(not helper::is_member_of_sample<Y>::value, "");

int main(){}

From obvious reason this cannot be applied to the std namespace - there is simply no way to inject the adl_is_member_of_sample equivalent to the std namespace without exposing ourself to undefined behaviour.

Is there some workaround enabling to create the trait?

Lehr answered 4/2, 2017 at 16:16 Comment(8)
This sounds like an xy problem. Why do you need to do this?Braid
Note that a name can be in multiple namespaces for ADL purposes. std::pair<foo::fooClass, bar::barClass> is in three namespaces - std, foo, and bar.Carver
@PeteBecker The problem has origins this question, but I'm more curious about it because I think there is many more applications for it...Lehr
@MartinBonner yep I haven't taken it into account...Lehr
That's what I suspected: it's a workaround for a design mistake, i.e., an xy problem. Those hash functions should be free functions, not members. Then the "problem" disappears.Braid
@PeteBecker Well yes and no - I think the bigger problem is with std::hash which isn't sfinae ready...Lehr
@Pete Becker Is it good if a hash function become "friend"-function inside a class? Will it be free-function? Will it still be a design mistake?Mogilev
@Mogilev - "free function" is a different name for "non-member function". std::hash was designed as a set of free functions, and fighting that design just makes things harder. Whether it needs to be a friend depends on just what it does, but free function that's a friend is still a free function.Braid
K
5

This seems to work:

#include <functional>
#include <type_traits>
#include <utility>
#include <string>

namespace other { struct S{}; }

namespace my {
    template< class Type >
    void ref( Type&& ) {}

    template< class Type >
    auto ref_to( Type&& o )
        -> Type&
    { return o; }

    template< class Type >
    constexpr auto is_std_type()
        -> bool
    {
        using std::is_same;
        using std::declval;
        return not is_same< void, decltype( ref( ref_to( declval<Type>() ) ) )>::value;
    }

    struct Blah {};

    constexpr bool int_is_std       = is_std_type<int>();
    constexpr bool blah_is_std      = is_std_type<Blah>();
    constexpr bool other_is_std     = is_std_type<other::S>();
    constexpr bool string_is_std    = is_std_type<std::string>();
};

#include <iostream>
using namespace std;
auto main()
    -> int
{
    cout << boolalpha;
    cout << "int is std = " << my::int_is_std << "\n";
    cout << "blah is std = " << my::blah_is_std << "\n";
    cout << "other is std = " << my::other_is_std << "\n";
    cout << "string is std = " << my::string_is_std << "\n";
}
Kazmirci answered 4/2, 2017 at 16:39 Comment(6)
WOW! I need to analyse it more but it looks brilliant!Lehr
Well I think it may give you false positive for a type that involves a type from std, depending on how you define "is from", but I haven't tested. Have fun! :)Kazmirci
Yes. If you add template <typename T> struct TT{}; to other, then it yields true for other::TT<std::string>. I still agree with Pete Becker this sounds like an XY problem.Carver
The template parameter issue can be mostly fixed by adding a specialisation for when Type is a templated class, like so. This still doesn't work properly when Type has any defaulted template parameters that default to a std type, though, because I wasn't sure how to remove those without potentially breaking stuff. Tested with Clang, GCC, and MSVC (had to replace operator not with operator! for the latter, though, because it was being childish).Debauchery
There's also classes derived from std classes to consider. Also, ref_to( declval<Type>() ) is just declval<Type&>().Architectonics
@T.C.: Can't understand how I didn't think of Type&. Lols. :) Thank you.Kazmirci

© 2022 - 2024 — McMap. All rights reserved.