220 20190 <ms4ec0$nur$1@ger.gmane.org> article
Path: news.gmane.org!not-for-mail
From: Matthew Woehlke <mwoehlke.floss@gmail.com>
Newsgroups: gmane.comp.lang.c++.isocpp.proposals
Subject: Re: Inline Classes (was "Properties" support in C++)
Date: Tue, 01 Sep 2015 10:56:59 -0400
Lines: 211
Approved: news@gmane.org
Message-ID: <ms4ec0$nur$1@ger.gmane.org>
References: <CAOU91OOayk=UD8LvRk4xRGgcqrkLQLgGXJFCYK4bHtAjgRbhcg@mail.gmail.com> <6882b7df-80e2-40e7-960a-b39b11af2af1@isocpp.org> <ms27sa$4pr$1@ger.gmane.org> <55E4F93E.6010301@gmail.com>
Reply-To: std-proposals@isocpp.org
NNTP-Posting-Host: plane.gmane.org
Mime-Version: 1.0
Content-Type: text/plain; charset=UTF-8
X-Trace: ger.gmane.org 1441119445 24798 80.91.229.3 (1 Sep 2015 14:57:25 GMT)
X-Complaints-To: usenet@ger.gmane.org
NNTP-Posting-Date: Tue, 1 Sep 2015 14:57:25 +0000 (UTC)
To: std-proposals@isocpp.org
Original-X-From: std-proposals+bncBC37LBFWUIFBBSXZS2XQKGQEIEAAWWI@isocpp.org Tue Sep 01 16:57:16 2015
Return-path: <std-proposals+bncBC37LBFWUIFBBSXZS2XQKGQEIEAAWWI@isocpp.org>
Envelope-to: gclcip-std-proposals@m.gmane.org
Original-Received: from mail-wi0-f198.google.com ([209.85.212.198])
	by plane.gmane.org with esmtp (Exim 4.69)
	(envelope-from <std-proposals+bncBC37LBFWUIFBBSXZS2XQKGQEIEAAWWI@isocpp.org>)
	id 1ZWmzr-0004DR-LF
	for gclcip-std-proposals@m.gmane.org; Tue, 01 Sep 2015 16:57:15 +0200
Original-Received: by wicgc1 with SMTP id gc1sf12006041wic.3
        for <gclcip-std-proposals@m.gmane.org>; Tue, 01 Sep 2015 07:57:15 -0700 (PDT)
X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
        d=1e100.net; s=20130820;
        h=x-gm-message-state:to:from:subject:date:lines:message-id:references
         :mime-version:content-type:user-agent:in-reply-to:x-original-sender
         :x-original-authentication-results:reply-to:precedence:mailing-list
         :list-id:x-spam-checked-in-group:list-post:list-help:list-archive
         :list-subscribe:list-unsubscribe;
        bh=mQYWfGKfVZBpF3PaIzvPgVUpZiajOVeKzLM5xbXoEog=;
        b=F2VelX1xK0uKGWKdqGljSv4o26kR0plKIOJSkJfxxigvlrpbpXs/4h+EjG0/4E4HWs
         t/f+1qTxTq4ehRjHQFDmI+ZwKQGIfG69/IAE+BGk7HSVfiLYhs0+giLMIRVP8zTrdMhf
         ty34Np1Rz6zpwG+iMy9U/H2ActB/0E2MQh63w05ud+CLsWJDjOMutmkL7ZeDLQpQxR/N
         yiWD5b+SsmIS2UfFNrkT7ZTfAgxKl0ZMfGKeFKq/ZzQvGDFxFBh1kuPYqUvazPaaJAXQ
         yocOo2eiY5iHs4rQ6+QP+UThvl8vbzwdBOLyKn8jamvdgKQ362x5A+AI6JQ+zbzcSDDy
         
X-Gm-Message-State: ALoCoQld1kaUK0v8x4/TAaR+sQBFNxxzXVI5tDoPZGFQFqhQpIKt4UKNFY4HGypPsVzE3o41lly0
X-Received: by 10.180.83.226 with SMTP id t2mr946703wiy.5.1441119435291;
        Tue, 01 Sep 2015 07:57:15 -0700 (PDT)
X-BeenThere: std-proposals@isocpp.org
Original-Received: by 10.152.20.9 with SMTP id j9ls652604lae.39.gmail; Tue, 01 Sep 2015
 07:57:13 -0700 (PDT)
X-Received: by 10.152.43.228 with SMTP id z4mr10365339lal.99.1441119433902;
        Tue, 01 Sep 2015 07:57:13 -0700 (PDT)
Original-Received: from plane.gmane.org (plane.gmane.org. [80.91.229.3])
        by mx.google.com with ESMTPS id e1si16619987lbs.96.2015.09.01.07.57.13
        for <std-proposals@isocpp.org>
        (version=TLSv1 cipher=RC4-SHA bits=128/128);
        Tue, 01 Sep 2015 07:57:13 -0700 (PDT)
Received-SPF: pass (google.com: domain of gclcip-std-proposals@m.gmane.org designates 80.91.229.3 as permitted sender) client-ip=80.91.229.3;
Original-Received: from list by plane.gmane.org with local (Exim 4.69)
	(envelope-from <gclcip-std-proposals@m.gmane.org>)
	id 1ZWmzn-0004AO-8f
	for std-proposals@isocpp.org; Tue, 01 Sep 2015 16:57:11 +0200
Original-Received: from tripoint.kitware.com ([66.194.253.20])
        by main.gmane.org with esmtp (Gmexim 0.1 (Debian))
        id 1AlnuQ-0007hv-00
        for <std-proposals@isocpp.org>; Tue, 01 Sep 2015 16:57:11 +0200
Original-Received: from mwoehlke.floss by tripoint.kitware.com with local (Gmexim 0.1 (Debian))
        id 1AlnuQ-0007hv-00
        for <std-proposals@isocpp.org>; Tue, 01 Sep 2015 16:57:11 +0200
X-Injected-Via-Gmane: http://gmane.org/
Original-Lines: 202
Original-X-Complaints-To: usenet@ger.gmane.org
X-Gmane-NNTP-Posting-Host: tripoint.kitware.com
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:31.0) Gecko/20100101 Thunderbird/31.7.0
In-Reply-To: <55E4F93E.6010301@gmail.com>
X-Original-Sender: mwoehlke.floss@gmail.com
X-Original-Authentication-Results: mx.google.com;       spf=pass (google.com:
 domain of gclcip-std-proposals@m.gmane.org designates 80.91.229.3 as
 permitted sender) smtp.mailfrom=gclcip-std-proposals@m.gmane.org;
       dmarc=fail (p=NONE dis=NONE) header.from=gmail.com
Precedence: list
Mailing-list: list std-proposals@isocpp.org; contact std-proposals+owners@isocpp.org
List-ID: <std-proposals.isocpp.org>
X-Spam-Checked-In-Group: std-proposals@isocpp.org
X-Google-Group-Id: 399137483710
List-Post: <http://groups.google.com/a/isocpp.org/group/std-proposals/post>, <mailto:std-proposals@isocpp.org>
List-Help: <http://support.google.com/a/isocpp.org/bin/topic.py?topic=25838>, <mailto:std-proposals+help@isocpp.org>
List-Archive: <http://groups.google.com/a/isocpp.org/group/std-proposals/>
List-Subscribe: <http://groups.google.com/a/isocpp.org/group/std-proposals/subscribe>,
 <mailto:std-proposals+subscribe@isocpp.org>
List-Unsubscribe: <mailto:googlegroups-manage+399137483710+unsubscribe@googlegroups.com>,
 <http://groups.google.com/a/isocpp.org/group/std-proposals/subscribe>
Xref: news.gmane.org gmane.comp.lang.c++.isocpp.proposals:20190
Archived-At: <http://permalink.gmane.org/gmane.comp.lang.c++.isocpp.proposals/20190>

On 2015-08-31 21:02, Miro Knejp wrote:
> *(3)* Should it be possible to obtain a pointer/reference to the inline
> subobject itself?
>
> Con:
> * If empty inline subobjects have no storage then multiple instances can
> have the same address.

This is one reason to look at them not as subobjects but as derived
types. Consider:

  class Foo { ... };
  class Bar : protected Foo { ... };

I can have a Foo* and a Bar* pointing to the same actual object, which
are identical. This is very similar to the case of inline classes,
except that I can have many inline classes that are siblings rather than
an inheritance chain. In both cases, all have the same address.

> * In the context of properties (under the assumption they are
> implemented with this technique) it is potentially a leaking
> abstraction. Other languages don't allow you to pass around the actual
> property itself, only the result of their getter. But then, these
> languages usually don't have the concept of references. It might be an
> interesting tool to pass a property by-reference to a function and have
> that function operate on the property instead of only its value. But it
> poses the ambiguity of when to deduce the property object and when the
> property's value.

I think the loophole that needs to be closed here is ensuring that
trying to take a property by value calls a conversion operator. Taking a
property by reference should be transparently equivalent to taking its
value, except that the call to the getter gets shifted. (And potentially
called more than once.)

Come to think of it, this (lvalue conversion) may be a general problem
not limited to inline class properties.

> *(4)* Should empty inline subobjects have storage?

Yes, as that's one of the purposes of the feature.

> *(5)* Should separate inline subobjects be allowed to have the same type?
> Pro: It's convenient.
> Con:
> * If they aren't empty and access their state in member functions the
> compiler must find a way to distinguish them.

....which is why I wouldn't permit non-empty inline classes. Basically,
as soon as you allow them to be non-empty, it's hard to see how you can
retain any benefit from them being inline vs. degenerating into plain
old concrete classes.

> * If every inline subobject has a different type and (3) is resolved as
> yes then it is possible to safely static_cast between the containing
> instance and the inline subobject.

If every object has a different type, I don't see the benefit to them
having (non-static data) members in the first place. Just move the
members you want to put in them to the containing concrete class.

> *(6)* It should be possible to declare re-usable inline classes outside
> of the containing class. This means these inline classes defined at
> namespace scope must be based on template instantiations as they are
> only complete types when embedded in a containing class and final name
> lookup and offset-patching can take place.

They don't need to be templated. They just act like namespaces.

> inline class P {
>   int i;
> public:
>   void set(int j) { i = j; }
>   int get() { return i; }
>   void bar() { this->baz(); }
> };

Assuming that this is the complete context (which I believe was your
intent), this code will not compile, because P has no member 'baz'.
There is no delayed look-up. If you want to use this as a base class
e.g. for a property, use CRTP and reinterpret_cast 'this' to the derived
type.

Given a freestanding inline class, the only things you can access with
'this' are member functions of that class and its base inline classes
(if any), and static members of the same.

> It is basically syntax for compiler-generated CRTP (which is a useful
> pattern on its own and this might actually make it more accessible).

....which is something I'd like to see, but orthogonally. If we make CRTP
better, we shouldn't limit the improvement to inline classes.

> *(5)* My answer here is no, there cannot be two inline subobjects of the
> same type.
> 
> The problem:
> class B {
>   P x, y;
>   void baz();
> };
> B b;
> auto& x = b.x;
> auto& y = b.y;
> x.get();
> y.get();
> This poses a conflict because x and y have different offsets. The
> compiler has to either store the offset inside b.x and b.y or generate
> different types for x and y with different get/set implementations. The
> latter results in references to B::x and B::y to be incompatible, which
> is surprising because from the code they seem to have the same.
> 
> My proposed solution is to introduce a new type for each inline class if
> you try to re-use some shared inline type.
> class C {
>   inline class X : public P { } x; // Instantiate P<C, 0>
>   inline class Y : public P { } y; // Instantiate P<C, 4>
>   inline class Z {
>     // ...
>   } z1, z2; // ill-formed: only one instance per type
>   void baz();
> };

I don't see why the first should work but the second should be illegal.

That said, I think this could work, but I'm nervous about the amount of
"magic" involved. Maybe it would be better to try to get inline classes
in as storage-free and later improve them to permit storage?

>   inline class : public P { } x*; // ill-formed

I'm not sure I agree (assuming you meant '*x'). Pointers to inline
classes are legal, so why not allow a class member that is such a pointer?

  class Foo
  {
  public:
    inline class : public P {} *x = _x;

  private:
    decltype(*x) _x;
  }

I don't know *why* you'd ever do this, but I don't see a problem.

>   inline class : public P { } x[5]; // probably ill-formed because
> (&x[0] - this) != (&x[1] - this)

This should fall under the same rule as 'P x, y'; see comments above.
(Pretty useless with storage-free inline classes, but...)

> If viewed under the light of a possible implementation for properties
> then it may become a leaking abstraction. However I believe there is as
> much value in passing a property by-reference as is passing a regular
> value by-reference. It allows the callee modifying access (whether that
> is good or bad is left undecided).

Assuming of course that the reference is non-const, in which case one
MUST pass the property reference anyway.

Repeating myself a bit here, but I see two possibilities: either the
callee wants a mutable reference, and so the only way to make the call
is to pass a reference to the property anyway, or else it takes a const
reference and passing the property reference could anyway only change
the program semantics if the callee uses the value multiple times, and
the value changes in the mean time.

> Assuming properties were implicitly convertible to the underlying
> type (by whichever means)

....which is sort of the point of why I defined the getter as a
conversion operator :-). (Notice I never made it explicit.)

> argument deduction results in the inline subobject backing the
> property.

Probably we would want to develop techniques to trigger conversion of a
property-like inline class as a deduced type. If derived from
std::property, this shouldn't be difficult. We almost certainly want
also a type trait to identify inline classes. The trick would be
automagically identifying the type of a single conversion operator.

(Including a utility class for this purpose in the proposal probably
wouldn't be amiss...)

> class A {
>   // x is not supposed to be a property
>   inline class X { ... } x;
>   // y is supposed to be a property
>   inline class Y { operator int(); Y& operator=(int); ... } y;
> };
> template<class T> void foo(T);
> A a;
> foo(a.x); // ill-formed, cannot copy inline class
> foo(a.y); // also ill-formed but shouldn't be: the intention is to pass
> a.y's value

I'm inclined to believe that attempting to copy/assign an inline class
in a deduced context should attempt to invoke a conversion operator.

-- 
Matthew

-- 

--- 
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.

.
