C++ Logo


Advanced search

Re: Idea: "friend" scope

From: Matthew Woehlke <mwoehlke.floss_at_[hidden]>
Date: Fri, 21 Jun 2019 13:24:15 -0400
On 21/06/2019 12.16, Arthur O'Dwyer wrote:
> On Fri, Jun 21, 2019 at 11:32 AM Matthew Woehlke wrote:
>> On 20/06/2019 18.59, Arthur O'Dwyer via Std-Proposals wrote:
>>> If you're allowed to rewrite the implementation of `create`, then [...]
>>> static std::shared_ptr<a> create(int x) {
>>> struct Wrapper {
>>> int x;
>>> operator a() const { return a(x); }
>>> };
>>> return std::make_shared<a>(Wrapper{x});
>>> }
>> [...] it seems that this generates worse code?
> No, the codegen is identical.
> https://godbolt.org/z/tVI70Q
> Remember that when comparing codegen, you have to use the same optimization
> level and the same compiler.

Ack... had the flags right, wrong compiler...

I don't think having the Wrapper take copies of the constructor
arguments is going to work for my use case (some of said arguments are
e.g. smart pointers, and part of the point of this exercise is to
eliminate unnecessary reference counting operations), however, I think
taking a reference to the same should work...

>> The scope applies to *all code*, including recursively called code,
>> within the scope. (Well, sort of;
> Well, not! You're trying to use the word "scope" to mean something other
> than "scope". In C++, "scope" means, like, a lexical scope: the scope
> between a left-brace and a right-brace. What you're describing is a way to
> *dynamically* turn off access control, so that for example `foo()` compiles
> when called from `baz()`, but doesn't compile when called from `bar()`.

Right. That's why it's a moderately (or maybe *not* so moderately) crazy
idea :-).

> class B { int m; };
> B b;
> void foo() { b.m = 42; }
> void bar() { foo(); }
> void baz() { friend { foo(); } }

Wait... what? I don't know what that last line means, aside I suppose
from "ill-formed". You used 'friend' outside of a class context. I'm not
proposing to allow that. (Did you mean for `baz` to be a member of `B`?)

> That's not something that can *ever* work, with separate compilation. We
> can't have two different versions of the code for `foo` floating around out
> there.

Right; the compiler would have to either inline or do some magic such
that `baz` actually calls `foo_with_friend_B`.

>> The reason I think this *may* be plausible is that compilers generate a
>> call stack when an access protection violation occurs. For example:
>> in <implementation details>
>> required by <more implementation details>
>> ...
>> required by `std::make_shared<a>(T...)` (with `T...` = `{int}`)
>> error: `a::a(int)` is protected
> That's not a *call *stack [...]> What it is is an *instantiation
> *stack.
Okay. I wasn't familiar with that terminology; sorry.

> Anyway, given that the codegen for `Wrapper<a>` is identical, is this all a
> moot point now?

Maybe. I'm going to fiddle with that idea and see if I can make it work
in my own code. (BTW, want to add that to the SO question? Otherwise I
will, once I have it working...)


Received on 2019-06-21 12:26:06