1

Someone have an idea how to check if an arbitrary method is const?

Like:

static_assert(is_const<vector<int>::size>::value, "size is not const");
static_assert(!is_const<vector<int>::push_back>::value, "push_back is const");

Good question T.C :) I am finding an particular overload of the method and I want to only set an modified flag if the method i found is non-const.

Here is my templates and maroes:

#define FRET_DECL_TYPE(Function) \
    template<typename T_, typename ... Args_> \
    struct f_ret##Function { typedef decltype(std::declval<T_&>().Function(std::declval<Args_&&>()...)) type; };

#define RPROP_PROXY_METHOD(Function) \
        FRET_DECL_TYPE(Function) \
        template<typename ... Args> \
        typename f_ret##Function<T, Args...>::type \
        Function(Args&& ... args) const { return this->GetReference().Function(static_cast<Args&&>(args)...); }; \
        template<typename ... Args> \
        typename f_ret##Function<T, Args...>::type \
        Function(Args&& ... args) { this->SetModified(true); return this->GetReference().Function(static_cast<Args&&>(args)...); }; \
Martin Vang
  • 191
  • 1
  • 10
  • 7
    What should `is_const` return on `vector::begin`? – T.C. Feb 16 '15 at 15:29
  • maybe this can help you:http://stackoverflow.com/questions/257288/is-it-possible-to-write-a-c-template-to-check-for-a-functions-existence – swang Feb 16 '15 at 15:30

3 Answers3

3

It can be done using “Walter Brown's void_t trick” although it quickly gets a bit verbose if you need this for a lot of members. You might consider using a macro to avoid repeating the template definitions for each member again and again.

#include <iomanip>
#include <iostream>
#include <type_traits>
#include <vector>

template<typename>
struct void_t
{
  using type = void;
};

template<class C, typename = void>
struct has_const_size : std::false_type {};

template<class C>
struct has_const_size<C, typename void_t<decltype(std::declval<const C>().size())>::type> : std::true_type {};

template<class C, typename = void>
struct has_const_clear : std::false_type {};

template<class C>
struct has_const_clear<C, typename void_t<decltype(std::declval<const C>().clear())>::type> : std::true_type {};


int
main()
{
  std::cout << "std::vector<int>::size()  " << std::boolalpha << has_const_size<std::vector<int>>::value << std::endl;
  std::cout << "std::vector<int>::clear() " << std::boolalpha << has_const_clear<std::vector<int>>::value << std::endl;
}

Output:

std::vector<int>::size()  true
std::vector<int>::clear() false
Community
  • 1
  • 1
5gon12eder
  • 21,864
  • 5
  • 40
  • 85
3

My take at it, open for suggestions.

Edit 1 : ref-qualifiers are missing. Here's a quick-and-dirty solution just to have something that actually works.
Edit 2 : added varargs to the equation. The implementation is getting ugly, but the calling style I find fine.

#include <iostream>

template <class... Ttypes>
struct params {};

#define isConstDuet(q)                                    \
template <class T, class Tret, class... Targs>            \
constexpr bool isConst(Tret (T::*)(Targs...) q,           \
    params<Targs...> = {}) {                              \
    return false;                                         \
}                                                         \
                                                          \
template <class T, class Tret, class... Targs>            \
constexpr bool isConst(Tret (T::*)(Targs...) const q,     \
    params<Targs...> = {}) {                              \
    return true;                                          \
}                                                         \
template <class T, class Tret, class... Targs>            \
constexpr bool isConst(Tret (T::*)(Targs..., ...) q,      \
    params<Targs...> = {}) {                              \
    return false;                                         \
}                                                         \
                                                          \
template <class T, class Tret, class... Targs>            \
constexpr bool isConst(Tret (T::*)(Targs..., ...) const q,\
    params<Targs...> = {}) {                              \
    return true;                                          \
}

isConstDuet()
isConstDuet(&)
isConstDuet(&&)
isConstDuet(volatile)
isConstDuet(volatile&)
isConstDuet(volatile&&)

#undef isConstDuet

struct S {
    void a() {}
    void b() const {}
    void c(int) {}
    void c(float) const {}
    void d() & {}
    void e() && {}
    void f() volatile & {}
    void g() volatile && {}
    void d2() const & {}
    void e2() const && {}
    void f2() const volatile & {}
    void g2() const volatile && {}
    void h(...) {}
    void h2(...) const {}
};

int main() {
    std::cout << std::boolalpha;
    std::cout << isConst(&S::a) << '/' << isConst(&S::b) << '\n';
    std::cout << isConst(&S::c, params<int>{})
       << '/' << isConst(&S::c, params<float>{}) << '\n';
    std::cout << isConst(&S::d) << '/' << isConst(&S::d2) << '\n';
    std::cout << isConst(&S::e) << '/' << isConst(&S::e2) << '\n';
    std::cout << isConst(&S::f) << '/' << isConst(&S::f2) << '\n';
    std::cout << isConst(&S::g) << '/' << isConst(&S::g2) << '\n';
    std::cout << isConst(&S::h) << '/' << isConst(&S::h2) << '\n';
    return 0;
}

Output :

false/true
false/true
false/true
false/true
false/true
false/true
false/true
Quentin
  • 58,778
  • 7
  • 120
  • 175
1

Another possible solution:

#include <vector>

struct Q {
    void push_back(int i) const {}
};

template<typename A, typename B>
struct First {
    typedef A type;
};

template<typename T>
constexpr typename
First<
    bool,
    decltype(std::declval<T const>().push_back(4))
>::type
test(int) {
    return 0;
}

template<typename T>
constexpr bool test(double) {
    return 1;
}

static_assert(test<Q>(0), "bad");

If the method is const, both functions are possible, but the compiler will select the one with int argument to avoid conversions. If it is not const, only the second one is possible.

green lantern
  • 934
  • 6
  • 9