How to Relax an Argument Constraint

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
template<typename T>
concept has_set = 
    std::invocable<decltype(&T::set), 
                      T &, std::string>
    || std::invocable<decltype(&T::set), 
                      T &, std::string_view const &>
    || std::invocable<decltype(&T::set), 
                      T &, char const *> 
    ;

struct Foo {};
static_assert(!has_set<Foo>);

struct Bar {
    void set(std::string_view);
    void set(std::string);
};
static_assert(has_set<Bar>);

struct Baz {
    void set(std::string);
};
static_assert(has_set<Baz>);

struct Bazz {
    void set(char const *);
};
static_assert(has_set<Bazz>);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include <tuple>
#include <string>

template<typename>
struct function_traits;

template <typename Return, typename... Args>
struct function_traits<Return (*)(Args...)>
{
   using return_type = Return;
   using arg_types = std::tuple<Args...>;

   static constexpr std::size_t arity = sizeof...(Args);
   template <std::size_t i> using arg_type = Args...[i];
};

template <typename Class, typename Return, typename... Args>
struct function_traits<Return (Class::*)(Args...)>
   : function_traits<Return(*)(Args...)>
{
    using class_type = Class;
};

// test
std::size_t func(int, const std::string &s) { return s.size(); }
struct Test {
    std::size_t func(int, const std::string &s) { return s.size(); }
};

int main() {
  // using traits = function_traits<decltype(func)>;
  using traits = function_traits<decltype(&Test::func)>;

  static_assert(traits::arity == 2);
  static_assert(std::is_same_v<traits::return_type, std::size_t>);
  static_assert(std::is_same_v<traits::arg_type<0>, int>);
  static_assert(std::is_same_v<traits::arg_type<1>, const std::string &>);
  static_assert(std::is_same_v<traits::arg_types, std::tuple<int, const std::string &>>);

  return 0;
}


// template<auto mf, typename T>
// auto make_proxy(T && obj)
// {
//     return [&obj] (auto &&... args) {
//         return (std::forward<T>(obj).*mf)(std::forward<decltype(args)>(args)...);
//     };
// }

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#include <type_traits>
#include <string_view>
#include <string>
#include <print>

struct NeverUseThis {
    template <typename T>
    [[ noreturn ]] operator T() {
        static_assert("never reach me!");
        throw;
    }     
};

template <typename T>
concept has_set = 
    std::is_invocable_v<decltype(&T::set), T &, std::string>
    || std::is_invocable_v<decltype(&T::set), T &, std::string_view>
    || std::is_invocable_v<decltype(&T::set), T &, char const *> 
    ;

    // std::is_invocable<, decltype(a), int>

    // std::convertible_to<S, std::string_view> 
    // &&
    // requires(T t, S s) {
    //     { t.set(s) } -> std::same_as<void>;      
    // };

//static_cast<void(T::*)(std::string_view)>(&T::set); 

struct Foo {};
static_assert(!has_set<Foo>);

struct Bar {
    void set(std::string_view);
};
static_assert(has_set<Bar>);

struct Baz {
    void set(std::string);

};
static_assert(has_set<Baz>);

struct FooBarBaz {
    void set(std::string_view) { 
        std::print("FooBarBaz\n");
    };
    void set(int) { };
};
static_assert(has_set<FooBarBaz>);

struct HasSetClass
{
    void set([[maybe_unused]] std::string const & s) {
        std::print("HasSetClass\n");
    }
};

struct HasSetClass2
{
    void set([[maybe_unused]] std::string_view const & s) {
        std::print("HasSetClass2\n");
    }
};


void use(has_set auto & o)
{
     o.set("aaa");
}

int main() {
    FooBarBaz fbb;
    use(fbb);

    HasSetClass a1;
    use(a1);

    HasSetClass2 a2;
    use(a2);
}

1
2
3
4
5
6
7
struct NeverUseThis {
    template <typename T>
    [[ noreturn ]] operator T() {
        static_assert("never reach me!");
        throw;
    }
};

Dabei reicht

1
2
3
struct NeverUseThis {
    template <typename T> operator T();
};

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <string>
#include <string_view>

template<typename T>
concept has_set = requires(T t) {
    { t.set(
        [](){ struct {
            operator std::string_view();
            operator std::string();
            operator char *();

        } s; return s; }()
      ) } -> std::same_as<void>;
};

struct Foo {};
static_assert(!has_set<Foo>);

struct Bar {
    void set(std::string_view);
};
static_assert(has_set<Bar>);

struct Baz {
    void set(std::string);
    void set(int i);
};
static_assert(has_set<Baz>);

struct Bazz {
    void set(char *);
};
static_assert(has_set<Bazz>);


int main()
{
}

1
2
3
4
5
6
7
template< class F, class... Args >

concept invocable =
    requires(F&& f, Args&&... args) {
        std::invoke(std::forward<F>(f), std::forward<Args>(args)...);
            /* not required to be equality-preserving */
    };

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <string>
#include <string_view>
#include <concepts>

template<typename T, typename Return, typename Arg>
concept has_set_memfn = requires(T t) { { t.set(Arg{}) }->std::same_as<Return>; };

template<typename T>
concept has_set = has_set_memfn<T, void, std::string_view>;

struct Foo {};
static_assert(!has_set<Foo>);

struct Bar {
    void set(std::string_view){}
};
static_assert(has_set<Bar>);

int main() {
    Bar bar;

    bar.set([](){ struct {
            operator std::string_view() const { return ""; }
        } s; return s; }());
}

We then declare a template function that takes arguments whose type adheres to the constraints defined by the concept:

1
std::string consume(has_super_cool_features `auto`& s);

TODO

1
template <has_super_cool_features T> std::string consume(T & s);