loosechainsaw / liboperator

Detecting C++ operators
1 stars 0 forks source link

naming and semantics #1

Open sehe opened 11 years ago

sehe commented 11 years ago

The naming of the traits seems... wrong.

E.g. 'has_addition_operator' would have been more aptly approximated by something like this:

template<class Class, typename Operand>
class has_addition_operator
{
    static_assert(!std::is_reference<Class>::value                               , "Class must be unqualified");
    static_assert( std::is_reference<Class>::value || std::is_class<Class>::value, "only class types can have member operators");
    static_assert(!std::is_volatile<Class>::value                                , "TODO use Sfinae to limit acceptable signatures");
    //static_assert(!std::is_const<Class>::value                                 , "TODO use Sfinae to limit acceptable signatures");

    static struct no has(...);

    template<class A, typename B> static auto has(A&&, B&&) 
            -> decltype(static_cast<A (A::*)(B)>(&A::operator+));
    template<class A, typename B> static auto has(A&&, B&&) 
            -> decltype(static_cast<A (A::*)(B&&)>(&A::operator+));
    template<class A, typename B> static auto has(A&&, B&&) 
            -> decltype(static_cast<A (A::*)(B const&)>(&A::operator+));

    template<class A, typename B> static auto has(A&&, B&&) 
            -> decltype(static_cast<A (A::*)(B) const>(&A::operator+));
    template<class A, typename B> static auto has(A&&, B&&) 
            -> decltype(static_cast<A (A::*)(B&&) const>(&A::operator+));
    template<class A, typename B> static auto has(A&&, B&&) 
            -> decltype(static_cast<A (A::*)(B const&) const>(&A::operator+));

    template<class A, typename B> static auto has(A&&, B&&) 
            -> decltype(static_cast<A (A::*)(B) volatile>(&A::operator+));
    template<class A, typename B> static auto has(A&&, B&&) 
            -> decltype(static_cast<A (A::*)(B&&) volatile>(&A::operator+));
    template<class A, typename B> static auto has(A&&, B&&) 
            -> decltype(static_cast<A (A::*)(B const&) volatile>(&A::operator+));

    template<class A, typename B> static auto has(A&&, B&&) 
            -> decltype(static_cast<A (A::*)(B) const volatile>(&A::operator+));
    template<class A, typename B> static auto has(A&&, B&&) 
            -> decltype(static_cast<A (A::*)(B&&) const volatile>(&A::operator+));
    template<class A, typename B> static auto has(A&&, B&&) 
            -> decltype(static_cast<A (A::*)(B const&) const volatile>(&A::operator+));

public:
    static constexpr bool value = !std::is_same<decltype(has(std::declval<Class>(), std::declval<Operand>())),no>::value;
};

Note

Alternatively, you could rename the traits, in which case I'd do something like this:

namespace detail
{
    using std::declval;

#define MY_EVIL_MACRO(name, expr) \
    struct test_supports_##name { \
        template<class T> static auto test(T* p) -> decltype((expr), std::true_type()); \
        template<class>   static auto test(...) -> std::false_type; \
    }

    MY_EVIL_MACRO(addition,          declval<T>() + declval<T>());
    MY_EVIL_MACRO(subtraction,       declval<T>() - declval<T>());
    MY_EVIL_MACRO(multiplication,    declval<T>() * declval<T>());
    MY_EVIL_MACRO(modulo,            declval<T>() % declval<T>());
    MY_EVIL_MACRO(shiftleft_by_int,  declval<T>() << 1);
    MY_EVIL_MACRO(shiftright_by_int, declval<T>() >> 1);
}

template<class T> struct supports_addition          : decltype(detail::test_supports_addition         ::test<T>(nullptr)) {};
template<class T> struct supports_subtraction       : decltype(detail::test_supports_subtraction      ::test<T>(nullptr)) {};
template<class T> struct supports_multiplication    : decltype(detail::test_supports_multiplication   ::test<T>(nullptr)) {};
template<class T> struct supports_modulo            : decltype(detail::test_supports_modulo           ::test<T>(nullptr)) {};
template<class T> struct supports_shiftleft_by_int  : decltype(detail::test_supports_shiftleft_by_int ::test<T>(nullptr)) {};
template<class T> struct supports_shiftright_by_int : decltype(detail::test_supports_shiftright_by_int::test<T>(nullptr)) {};

Seems much quicker. Of course, generalize for asymmetrical binary ops

sehe commented 11 years ago

PS.

Also, I think you'd need to use either std::declval<X1>() or std::forward<X1>(a) instead of just a + b in decltype, lest you run into issues with inaccessible copy constructors.

loosechainsaw commented 11 years ago

Thank you for such awesome feedback sehe.

loosechainsaw commented 11 years ago

You do run into issues with ctors. Thus why we use declval. I learnt this the hard way. Would you be keen in contributing or at least critquing and raising issues as you find them your advice and comments have been awesome.

sehe commented 11 years ago

Well, sure. I was mostly being skeptical about the need for a library here. I usually write my traits as I go.

Also, it appears I was misremembering the purposes of the new Boost Libraries in 1.54.0 beta: TTI is nice, but Boost TypeErasure seems to have precisely what you're implementing:

Table 33.3. Binary Operators

Looks pretty comprehensive to me. And there's unary ops, function calleables etc. too :)

loosechainsaw commented 11 years ago

Oh we'll still fun to write and learning heaps so I will finish it

Sent from my iPad

On 15/06/2013, at 2:46 AM, sehe notifications@github.com wrote:

Well, sure. I was mostly being skeptical about the need for a library here. I usually write my traits as I go.

Also, it appears I was misremembering the purposes of the new Boost Libraries in 1.54.0 beta: TTI is nice, but Boost TypeErasure seems to have precisely what you're implementing:

Table 33.3. Binary Operators

addable subtractable multipliable dividable modable bitandable bitorable bitxorable left_shiftable right_shiftable equality_comparable less_than_comparable add_assignable subtract_assignable multiply_assignable divide_assignable mod_assignable bitand_assignable bitor_assignable bitxor_assignable left_shift_assignable right_shift_assignable ostreamable istreamable Looks pretty comprehensive to me. And there's unary ops, function calleables etc. too :)

— Reply to this email directly or view it on GitHub.