Boost logo

Boost :

Subject: Re: [boost] C++11 Metaprogramming
From: Dave Abrahams (dave_at_[hidden])
Date: 2012-04-05 01:17:57


on Wed Apr 04 2012, Sumant Tambe <sutambe-AT-gmail.com> wrote:

> On 3 April 2012 10:44, Dave Abrahams <dave_at_[hidden]> wrote:
>
>>
>> But isn't this a better way to relieve the tedium?
>>
>> #define RETURNS(...) \
>> noexcept(noexcept(decltype(__VA_ARGS__)(std::move(__VA_ARGS__)))) \
>> -> decltype(__VA_ARGS__) \
>> { return (__VA_ARGS__); } \
>> typedef int RETURNS_CAT(RETURNS_, __LINE__)
>>
>> #define RETURNS_CAT_0(x, y) x ## y
>> #define RETURNS_CAT(x, y) RETURNS_CAT_0(x,y)
>>
>> ...
>>
>> auto swap(B& x, B& y) RETURNS(swap(x.a,y.a), swap(x.b,y.b), ...);
>>
>>
> But is that really allowed? I could not get it to work on clang because
> the declaration of swap needs access to the members of the class and the
> class definition is incomplete at that point.

Going back to Howard's original example:

--8<---------------cut here---------------start------------->8---
  struct B
  {
      A a;
  };

  void swap(B& x, B& y) noexcept(__is_nothrow_swappable<A>::value)
  {
      swap(x.a, y.a);
  }
--8<---------------cut here---------------end--------------->8---

B is certainly complete at the point swap is defined. I did have to
make an adjustment to deal with the fact that swap returns void:

--8<---------------cut here---------------start------------->8---
#include <utility>

// Used to create a non-void object that we can pass to std::move
// in case the expression is void.
struct nonvoid
{
    template <class T>
    friend auto operator,(T&& x, nonvoid) noexcept -> T&&;
};

#define RETURNS(...) \
    noexcept(noexcept( \
                 decltype(__VA_ARGS__)( \
                     std::move(((__VA_ARGS__),nonvoid())) \
                         ))) \
    -> decltype(__VA_ARGS__) \
    { return (__VA_ARGS__); } \
    typedef int RETURNS_CAT(RETURNS_, __LINE__)

#define RETURNS_CAT_0(x, y) x ## y
#define RETURNS_CAT(x, y) RETURNS_CAT_0(x,y)

struct A
{
    friend void swap(A&,A&) noexcept {}
};

struct B
{
    A a;
};

auto swap(B& x, B& y)
  RETURNS(swap(x.a,y.a));

int main() {}
--8<---------------cut here---------------end--------------->8---

> Moving the definition of swap outside the class did not help because
> you need access to private members. Making swap friend also does not
> help because friend declaration also needs a matching noexcept
> specification. gcc 4.7 seems to be lenient regarding this check but
> clang is not.

I see: with private members, this swap needs to be a friend and then you
have the incomplete type problem:

--8<---------------cut here---------------start------------->8---
class B
{
    A a;
    
    friend auto swap(B& x, B& y)
      RETURNS(swap(x.a,y.a)); // Error; B is incomplete
};
--8<---------------cut here---------------end--------------->8---

Well, you can get around it by templatizing :-)

--8<---------------cut here---------------start------------->8---
struct A
{
    friend void swap(A&,A&) noexcept {}
};

namespace gcc_bug_workaround { // http://gcc.gnu.org/bugzilla/show_bug.cgi?id=52875
class B
{
    A a;
    
    template <class T>
    friend auto swap(T& x, T& y)
        RETURNS(swap(x.a,y.a)); // Error; B is incomplete
};
}
using namespace gcc_bug_workaround;
--8<---------------cut here---------------end--------------->8---

> Alternatively, using std::tuple could help in reducing verbosity of
> noexcept specification Howard is alluding to.
>

<snip>

> However, I don't think if we could use std::make_tuple to avoid spelling
> out types all over again due to the reasons mentioned above.
>
> I would love to stand corrected.

I think the template trick serves to delay things till the class is
defined, yes?

-- 
Dave Abrahams
BoostPro Computing
http://www.boostpro.com

Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk