lmi
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [lmi] enable_if boost to std rosetta stone


From: Greg Chicares
Subject: Re: [lmi] enable_if boost to std rosetta stone
Date: Mon, 23 Jan 2017 18:24:31 +0000
User-agent: Mozilla/5.0 (X11; Linux x86_64; rv:45.0) Gecko/20100101 Icedove/45.6.0

On 2017-01-23 01:56, Vadim Zeitlin wrote:
> On Sun, 22 Jan 2017 20:46:47 +0000 Greg Chicares <address@hidden> wrote:
> 
> GC> Can't we get rid of both "::value" (as boost::enable_if does) and
> GC> "::type" (as C++14 std::enable_if_t does) at the same time?
> 
>  I don't see why not... At least my first naïve attempt to do it seems to
> have compiled just fine:
[...]
> I am not sure how to test this, but it looks like this ought to do the
> right thing, am I missing something?

That does seem to work (see unit test below). You'll need a separate
disable_if_t because adding '!' after the first '<' here:
  detail::enable_if_t<std::is_enum<T>>* = nullptr
won't find any "::value" to negate; but some would see that as an
advantage because boost's enable-disable pair is easier to read.

Here's a unit test provided as a patch to lmi's sandbox. To use it
(drop the ".exe" of course for *nix):

  $make unit_tests unit_test_targets=sandbox_test.exe

---------8<--------8<--------8<--------8<--------8<--------8<--------8<-------
diff --git a/sandbox_test.cpp b/sandbox_test.cpp
index 401b86d..1fc3ef1 100644
--- a/sandbox_test.cpp
+++ b/sandbox_test.cpp
@@ -23,8 +23,78 @@
 
 #include "test_tools.hpp"
 
+#include <type_traits>
+
+// Original pair, adapted from 'xml_serializable.tpp'.
+//  - switched template arg order to allow deduction
+//  - the second ("disable_if") one doesn't throw (easier to test)
+
+template<typename Y, typename X>
+inline Y sfinae_cast
+    (X const& x
+    ,typename std::enable_if<std::is_same<X,Y>::value>::type* = nullptr
+    )
+{
+    return x;
+}
+
+template<typename Y, typename X>
+inline Y sfinae_cast
+    (X const&
+    ,typename std::enable_if<!std::is_same<X,Y>::value>::type* = nullptr
+    )
+{
+// Don't throw for this test:
+//    fatal_error() << "Impermissible type conversion." << LMI_FLUSH;
+    return Y();
+}
+
+// Same, but using a terser idiom:
+
+namespace xyzzy
+{
+// Renamed with s/if/iff/ to avoid conflict with C++14.
+    template<typename IsTrue, typename T = void>
+    using enable_iff_t = typename std::enable_if<IsTrue::value,T>::type;
+
+    template<typename IsTrue, typename T = void>
+    using disable_iff_t = typename std::enable_if<!IsTrue::value,T>::type;
+} // namespace xyzzy
+
+template<typename Y, typename X>
+inline Y sfinae_cast_2
+    (X const& x
+    ,typename xyzzy::enable_iff_t<std::is_same<X,Y>>* = nullptr
+    )
+{
+    return x;
+}
+
+template<typename Y, typename X>
+inline Y sfinae_cast_2
+    (X const&
+//  ,typename xyzzy::enable_iff_t<!std::is_same<X,Y> >* = nullptr
+//             error: template argument 1 is invalid ^
+//  i.e., "::type" was elided and cannot be negated here
+//  so a distinct "disable" version is needed:
+    ,typename xyzzy::disable_iff_t<std::is_same<X,Y> >* = nullptr
+    )
+{
+    return Y();
+}
+
 int test_main(int, char*[])
 {
+    BOOST_TEST((4.0     == sfinae_cast<double      >(4.0)));
+    BOOST_TEST((0U      == sfinae_cast<double const>(4.0)));
+    BOOST_TEST((0L      == sfinae_cast<float       >(4.0)));
+    BOOST_TEST((nullptr == sfinae_cast<double*     >(4.0)));
+
+    BOOST_TEST((4.0     == sfinae_cast_2<double      >(4.0)));
+    BOOST_TEST((0U      == sfinae_cast_2<double const>(4.0)));
+    BOOST_TEST((0L      == sfinae_cast_2<float       >(4.0)));
+    BOOST_TEST((nullptr == sfinae_cast_2<double*     >(4.0)));
+
     return 0;
 }
 
--------->8-------->8-------->8-------->8-------->8-------->8-------->8-------

Now I wonder about two things. First, given that the Committee added
std::enable_if_t to C++14 to provide a less verbose idiom, why didn't
they go all the way and do what you've done?

Second, if I remove the code that you suggested and I mangled above
from its namespace, bringing it into the global namespace, it no longer
works. Is this some obvious mechanical error on my part, or am I missing
some subtlety? I'll present this as a full patch to HEAD rather than a
patch-upon-a-patch because I think that'll be clearer:

---------8<--------8<--------8<--------8<--------8<--------8<--------8<-------
diff --git a/sandbox_test.cpp b/sandbox_test.cpp
index 401b86d..cbfe761 100644
--- a/sandbox_test.cpp
+++ b/sandbox_test.cpp
@@ -23,8 +23,75 @@
 
 #include "test_tools.hpp"
 
+#include <type_traits>
+
+// Original pair, adapted from 'xml_serializable.tpp'.
+//  - switched template arg order to allow deduction
+//  - the second ("disable_if") one doesn't throw (easier to test)
+
+template<typename Y, typename X>
+inline Y sfinae_cast
+    (X const& x
+    ,typename std::enable_if<std::is_same<X,Y>::value>::type* = nullptr
+    )
+{
+    return x;
+}
+
+template<typename Y, typename X>
+inline Y sfinae_cast
+    (X const&
+    ,typename std::enable_if<!std::is_same<X,Y>::value>::type* = nullptr
+    )
+{
+// Don't throw for this test:
+//    fatal_error() << "Impermissible type conversion." << LMI_FLUSH;
+    return Y();
+}
+
+// Same, but using a terser idiom:
+
+// Renamed with s/if/iff/ to avoid conflict with C++14.
+    template<typename IsTrue, typename T = void>
+    using enable_iff_t = typename std::enable_if<IsTrue::value,T>::type;
+
+    template<typename IsTrue, typename T = void>
+    using disable_iff_t = typename std::enable_if<!IsTrue::value,T>::type;
+
+template<typename Y, typename X>
+inline Y sfinae_cast_2
+    (X const& x
+    ,typename enable_iff_t<std::is_same<X,Y>>* = nullptr
+    )
+{
+    return x;
+}
+
+template<typename Y, typename X>
+inline Y sfinae_cast_2
+    (X const&
+//  ,typename enable_iff_t<!std::is_same<X,Y> >* = nullptr
+//             error: template argument 1 is invalid ^
+//  i.e., "::type" was elided and cannot be negated here
+//  so a distinct "disable" version is needed:
+    ,typename disable_iff_t<std::is_same<X,Y> >* = nullptr
+    )
+{
+    return Y();
+}
+
 int test_main(int, char*[])
 {
+    BOOST_TEST((4.0     == sfinae_cast<double      >(4.0)));
+    BOOST_TEST((0U      == sfinae_cast<double const>(4.0)));
+    BOOST_TEST((0L      == sfinae_cast<float       >(4.0)));
+    BOOST_TEST((nullptr == sfinae_cast<double*     >(4.0)));
+
+    BOOST_TEST((4.0     == sfinae_cast_2<double      >(4.0)));
+    BOOST_TEST((0U      == sfinae_cast_2<double const>(4.0)));
+    BOOST_TEST((0L      == sfinae_cast_2<float       >(4.0)));
+    BOOST_TEST((nullptr == sfinae_cast_2<double*     >(4.0)));
+
     return 0;
 }
 
--------->8-------->8-------->8-------->8-------->8-------->8-------->8-------




reply via email to

[Prev in Thread] Current Thread [Next in Thread]