Discussion:
[std-proposals] Daft: Unified Call Syntax via user-side opt-in
m***@gmail.com
2018-08-14 06:03:09 UTC
Permalink
Hello, I have written a proposal for Unified Call Syntax, based on the
previous discussion.

https://1drv.ms/u/s!AifghXUnpCc8hAaU7HpR2avsms3b


Feedback is welcome.


PS. This is the fifth attempt to post, hence the link instead of an
attachment.
--
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+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/bbef13e4-2d0e-4433-b80a-9f2070d349de%40isocpp.org.
y***@gmail.com
2018-08-17 19:31:27 UTC
Permalink
hello.
i am read thread master proposal.
i am not native,maybe getting incomplete.

i think.
i want to change compile time mechanics.so change to 2pass.
because one for UCS.and other for implementation.
1pass cant solve cross reference.and cant use same source's under code.

to 2pass is good effect for UCS.
yes.it makes some load.
many load can reduce by recently feature.
if it cant be done the type issue.

elder talk about compile time lookup?
so maybe solve by 2pass.
it is not force.it is be max.
i think so.
---
i am add file.this file not be for discussion's mainline.
if you are interested.try playing.
--
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+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/0f66b714-d935-451e-90e7-4b094d55a655%40isocpp.org.
m***@gmail.com
2018-08-18 13:31:32 UTC
Permalink
Post by y***@gmail.com
hello.
i am read thread master proposal.
i am not native,maybe getting incomplete.
i think.
i want to change compile time mechanics.so change to 2pass.
because one for UCS.and other for implementation.
1pass cant solve cross reference.and cant use same source's under code.
to 2pass is good effect for UCS.
yes.it makes some load.
many load can reduce by recently feature.
if it cant be done the type issue.
elder talk about compile time lookup?
so maybe solve by 2pass.
it is not force.it is be max.
i think so.
---
i am add file.this file not be for discussion's mainline.
if you are interested.try playing.
Note that this is not UCS as you combine two objects. This is more similar
to "overloading the dot operator" idea (especially the non-Bjarne
alternative) as you make one object to pretend to be another.
--
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+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/1c51a10e-5831-474b-88b9-588466eb09ab%40isocpp.org.
Barry Revzin
2018-08-19 01:14:06 UTC
Permalink
Post by m***@gmail.com
Hello, I have written a proposal for Unified Call Syntax, based on the
previous discussion.
https://1drv.ms/u/s!AifghXUnpCc8hAaU7HpR2avsms3b
It seems like this paper is very focused on attempting to solve the
problems with the previous UFCS proposals, rather than trying to solve the
problems that UFCS was trying to solve. As a result, it doesn't actually
solve the original problems. Here is part of the motivation for N4474:

Having two call syntaxes makes it hard to write generic code: which syntax
Post by m***@gmail.com
can we assume from a template argument type? When we start writing
concepts, we will either have to support both syntaxes (verbose,
potentially doubling the size of concepts) or make assumptions about how
objects of certain binds of types are to be invoked (and we may be wrong
close to 50% of the time). Today, we already have the problem that many
standard-library types are supported by two functions, such as, begin(x)
and x.begin(), and swap(x,y) and x.swap(y). This is an increasing problem
and from N4165:

It is a well-known and long-standing problem that C++ has two incompatible
Post by m***@gmail.com
calling syntaxes:[...] Unfortunately, this means that calling code must
know whether a function is a member function or not. In particular, this
syntactic difference defeats writing generic code which must select a
single syntax and therefore has no reasonable and direct way to invoke a
function f on an object x without first knowing whether f is a member
function or not for that type. Because there is no single syntax that can
invoke both, it is difficult or impossible to write generic code that can
adapt
and from N4174:

This note explores the possibility of providing a uniform call syntax by
Post by m***@gmail.com
giving member functions preference over non-member functions. Offering the
choice between the x.f(y) and f(x,y) notations with different meanings
means that different people will chose differently for their function
definitions, so that users have to know the choice and write calls
appropriately. This gives users more opportunities for making mistakes,
makes it harder to write generic code, and has led to replication when
people define both a member and a non-member function to express the same
thing. I suggest that providing different meanings to the two syntaxes
offers no significant advantage
Now, I don't think generic code is the *only* motivation for having this
kind of feature - but it was a big motivation for the original papers.
Basically, being able to, in generic code, write either x.f(y) or f(x, y)
and have that work regardless if X provides a member f or a non-member f.

With your proposal - I still can't do this. In order for f(x, y) to find
x.f(y), I would have to already know the X::f exists and bring it in with a
using-declaration. But, if I already know it exists, why wouldn't I just
write x.f(y) to begin with? I can't write:

template <typename T>
void algo(T t) {
using T::f; // what if there is no T::f?
f(t, Y{});
}

We would have to add something about being able to write dependent
using-declarations and having them just do nothing instead of failing
(which is, I think, questionable), but then every function template would
just be littered with all of these using-declarations for every possible
unqualified call? That seems fairly hostile to library writers.

And then, in order for x.f(y) to find f(x, y), I need to redeclare the
type? This definitely won't work in generic code. Where would I do it? What
if it's a nested type or a template specialization or ... ?

Additionally, I'm not sure this opt-in syntax meaningfully addresses the
concern that I have seen from library authors about allowing x.f(y) to find
f(x, y) - which is that now if class X adds anything named "f" at all, even
if it's a private member that users aren't intended to be aware of, it
breaks user code. This:

Declared this way, if lib::Image ever gets a 'save' method, we will get a
Post by m***@gmail.com
redefinition error, instead of silent change of implementation.
Doesn't really address their concern. User code is *still broken *by a
seemingly irrelevant change. Sure, the error might be better - you could
clearly say that "X::f is a member function but you're trying to say it
doesn't exist" instead of "error: trying to access private member int"
which would be incredibly confusing. And if an X::f was added that was
public and completely compatible with ::f, you would get an error instead
of silently potentially-different code, which is good. But the point
remains that user code is still broken.

So I'm not sure this is the right direction.

Barry
--
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+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/e1e54ed9-0186-42ce-a48e-d739e481047d%40isocpp.org.
NicTheNZer
2018-08-19 01:33:00 UTC
Permalink
Post by Barry Revzin
With your proposal - I still can't do this. In order for f(x, y) to find
x.f(y), I would have to already know the X::f exists and bring it in with a
using-declaration. But, if I already know it exists, why wouldn't I just
template <typename T>
void algo(T t) {
using T::f; // what if there is no T::f?
f(t, Y{});
}
We would have to add something about being able to write dependent
using-declarations and having them just do nothing instead of failing
(which is, I think, questionable), but then every function template would
just be littered with all of these using-declarations for every possible
unqualified call? That seems fairly hostile to library writers.
I had a thought recently on the behaviour of using T::f; when there is no
such method. If using T::f is simply ignored where there are no matching
members then it makes it much easier to remove functions from a type and
just keep the ones you want (as available). So I thought if T is a
dependent context (same as when forwarding references apply) without such
members then using T::f should generate no errors but just do nothing.

There are other issues around universal call syntax, private members and
the like though as you mentioned.
--
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+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/862b5312-4661-4fc0-b4b3-2b78d2fddcc5%40isocpp.org.
m***@gmail.com
2018-08-19 12:23:48 UTC
Permalink
Post by Barry Revzin
Post by m***@gmail.com
Hello, I have written a proposal for Unified Call Syntax, based on the
previous discussion.
https://1drv.ms/u/s!AifghXUnpCc8hAaU7HpR2avsms3b
It seems like this paper is very focused on attempting to solve the
problems with the previous UFCS proposals, rather than trying to solve the
problems that UFCS was trying to solve. As a result, it doesn't actually
Having two call syntaxes makes it hard to write generic code: which syntax
Post by m***@gmail.com
can we assume from a template argument type? When we start writing
concepts, we will either have to support both syntaxes (verbose,
potentially doubling the size of concepts) or make assumptions about how
objects of certain binds of types are to be invoked (and we may be wrong
close to 50% of the time). Today, we already have the problem that many
standard-library types are supported by two functions, such as, begin(x)
and x.begin(), and swap(x,y) and x.swap(y). This is an increasing problem
It is a well-known and long-standing problem that C++ has two incompatible
Post by m***@gmail.com
calling syntaxes:[...] Unfortunately, this means that calling code must
know whether a function is a member function or not. In particular, this
syntactic difference defeats writing generic code which must select a
single syntax and therefore has no reasonable and direct way to invoke a
function f on an object x without first knowing whether f is a member
function or not for that type. Because there is no single syntax that can
invoke both, it is difficult or impossible to write generic code that can
adapt
This note explores the possibility of providing a uniform call syntax by
Post by m***@gmail.com
giving member functions preference over non-member functions. Offering the
choice between the x.f(y) and f(x,y) notations with different meanings
means that different people will chose differently for their function
definitions, so that users have to know the choice and write calls
appropriately. This gives users more opportunities for making mistakes,
makes it harder to write generic code, and has led to replication when
people define both a member and a non-member function to express the same
thing. I suggest that providing different meanings to the two syntaxes
offers no significant advantage
Now, I don't think generic code is the *only* motivation for having this
kind of feature - but it was a big motivation for the original papers.
Basically, being able to, in generic code, write either x.f(y) or f(x, y)
and have that work regardless if X provides a member f or a non-member f.
True, this was the main motivation of the said papers, but this was not the
original motivation.
The original motivation was not about templates, it was about extending
classes one have no access to.
Of course template code made all this *much* more attractive and the
possibility to solve it without an opt in - even more so.
This, however, has failed.

Yet, as the template code can only be truly served by having a default UFC,
the proposal envisions the possibility of eventually switching some default
on, and having a syntax overriding the defaults to match the user needs.
It also envisions migrating the code over a period of time.

The proposal does not exclude UCS as per the original papers.

Meanwhile, the users receive the original feature request!
Post by Barry Revzin
With your proposal - I still can't do this. In order for f(x, y) to find
x.f(y), I would have to already know the X::f exists and bring it in with a
using-declaration. But, if I already know it exists, why wouldn't I just
template <typename T>
void algo(T t) {
using T::f; // what if there is no T::f?
f(t, Y{});
}
We would have to add something about being able to write dependent
using-declarations and having them just do nothing instead of failing
(which is, I think, questionable), but then every function template would
just be littered with all of these using-declarations for every possible
unqualified call? That seems fairly hostile to library writers.
And then, in order for x.f(y) to find f(x, y), I need to redeclare the
type? This definitely won't work in generic code. Where would I do it? What
if it's a nested type or a template specialization or ... ?
No, this is *user side*, library writes write how they please, knowing the
user can *always* enable the opposite syntax.

// lib

template <typename T> //< assume a concept for T to make the interface
official
void algo(T t) {
f(t, Y{});
}

// user

class C { void f(Y) {} }

// User sees the concept for T, or worst cases, sees an error as f is not
found

// using C::f; //< user choice 1
// void f(Y) {} //< user choice 2

algo(C());

Library writers should not care much about UFC!

In future we *might* make looking into the class default, we *might* use
and improve the syntax to alter the behavior in some way.
The point is having a place to communicate the link b/w the free world and
member world.
Post by Barry Revzin
Additionally, I'm not sure this opt-in syntax meaningfully addresses the
concern that I have seen from library authors about allowing x.f(y) to find
f(x, y) - which is that now if class X adds anything named "f" at all, even
if it's a private member that users aren't intended to be aware of, it
Declared this way, if lib::Image ever gets a 'save' method, we will get a
Post by m***@gmail.com
redefinition error, instead of silent change of implementation.
There are not many details in the proposal, but private members should not
affect the extended interface.

I know current call rules check access last, but this does not have to be
the case when free functions are imported.

class C {};

void f(C&) {}

class C using
{
using f; //< overload only against the public interface
};

// later

class C { void f(C&) {} };

c.f(); //< still calling the free function, we would have errored out
anyway!

Isn't that always correct? Yeah, the free hides the member, but a private
member, not callable anyway.

The only place where this could create problems is when c.f() is called
from C itself, but C has control over what it declares and what it imports.
Post by Barry Revzin
Doesn't really address their concern. User code is *still broken *by a
seemingly irrelevant change. Sure, the error might be better - you could
clearly say that "X::f is a member function but you're trying to say it
doesn't exist" instead of "error: trying to access private member int"
which would be incredibly confusing. And if an X::f was added that was
public and completely compatible with ::f, you would get an error instead
of silently potentially-different code, which is good. But the point
remains that user code is still broken.
There are multiple stages to pass to get the code broken, and each and
every one is a *user choice:*


1. The user imports his functions into a foreign class public interface
2. The user does NOT accept the future implementation of the function by
the class itself
3. The user uses the dot notation
4. The user does *not* use qualified call.

Compared to a default UCS we have TWO new stages of consent!

And BTW the code can be broken similarly today

namespace lib {
template<class T>
void f(T) { std::cout<<__PRETTY_FUNCTION__<<'\n';}
}

// user

void f(int) { std::cout<<__PRETTY_FUNCTION__<<'\n'; }
using lib::f;

f(5);

If lib decides to add f(int) user code breaks.
Post by Barry Revzin
So I'm not sure this is the right direction.
Barry
--
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+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/35b2400e-b308-4dc3-b2aa-7631908adc98%40isocpp.org.
m***@gmail.com
2018-08-28 08:16:43 UTC
Permalink
In the recent days I was reading a series of posts by
<https://quuxplusone.github.io/blog/2018/03/19/customization-points-for-functions/#arthur-o-dwyer>Andrzej
Krzemieński (link
<https://akrzemi1.wordpress.com/2015/11/19/overload-resolution/>, link
<https://akrzemi1.wordpress.com/2016/01/16/a-customizable-framework/>) and
Arthur O’Dwyer (link
<https://quuxplusone.github.io/blog/2018/03/19/customization-points-for-functions/>,
link
<https://quuxplusone.github.io/blog/2018/08/23/customization-point-or-named-function-pick-one/>
)

To cut the long story short the issue is having a function, that the type
is able to "specialize" for itself.

The classic example is std::swap - the user overloads the function in its
namespace then uses using when calling the function in generic code

Although easy to do, two major drawbacks are pointed out in the posts.

1. The specialization depends on ADL, so if a foreign type is specialized
its namespace must be opened. This is problematic for two reasons
- Std namespace (and probably others) is forbidden to be reopened
except when explicitly allowed
- The type might silently change namespace. Here is the example given
by Krzemieński :
namespace boost
{
template <typename T>
class optional;
}

changed to:

namespace boost
{
namespace optional_ns
{
template <typename T>
class optional;
}
using namespace optional_ns;
}

2. The using part is easy to miss, using a qualified call instead, missing
on the specializations completely.

Granted there are ways around these deficiencies and they are investigated
in detail in the posts.



It accrued to me the proposed UFC could be used to alleviate the issues and
with a tweak can be an interesting alternative.

Consider

namespace my {

void func(std::string) {}

}

class std::string using
{
my::func;
};

Now, here is the tweak, we make the func discoverable *without* a using
declaration or qualification

auto s = "hello"s;

s.func(); //< works
//func(s); //< does not work, as always

This allows us to write trivially customizable code - the user just have to
add a member function *or* a free standing one, in whatever namespace, and
import it into the class/type:

namespace some {

template<class T>
void something(T val) { val.to_string(); } *//< no fuss*

}

namespace my {
class Class
{
string to_string() {...}
};
}

namespace utils {

string to_string(int) {...}

}

typename int using
{
utils::to_string;
}

some::something(my::Class{}); //< OK
some::something(5); //< OK

Now, in the case int has already has to_string imported

typename int using
{
some_dude::to_string;
}

some::something(5); //< ambiguity

We can use the using to select the desired function

using some_dude::to_string;

some::something(5); //< OK

In other words, using declaration does not bring the function into scope,
it is already there by being imported, but it used to hide other functions.


Thoughts?
--
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+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/215c9613-b977-49dc-9564-12be1742da64%40isocpp.org.
m***@gmail.com
2018-09-01 09:09:48 UTC
Permalink
Updated the file to accommodate the comments made thus far + added a small
section about pointers.
Post by m***@gmail.com
Hello, I have written a proposal for Unified Call Syntax, based on the
previous discussion.
https://1drv.ms/u/s!AifghXUnpCc8hAaU7HpR2avsms3b
Feedback is welcome.
PS. This is the fifth attempt to post, hence the link instead of an
attachment.
--
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+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/6bdac20e-4b60-40ec-aebc-ebe25173eb68%40isocpp.org.
m***@gmail.com
2018-10-23 09:14:30 UTC
Permalink
Updated the file to include the observation this technique might be used to
support "Operator Dot"-type of access.

Example

template <class T>
class reference_wrapper {
public:
reference_wrapper(T& ref) noexcept : _ptr(std::addressof(ref)) {}
reference_wrapper(T&&) = delete;
reference_wrapper(const reference_wrapper&) noexcept = default;
reference_wrapper& operator=(const reference_wrapper& x) noexcept =
default;

// access
operator T& () const noexcept { return *_ptr; }
...

private:
T* _ptr;
};

// deduction guides
template<class T>
reference_wrapper(reference_wrapper<T>) -> reference_wrapper<T>;

// associated members
template <class T>
class reference_wrapper using
{
T::*; //< introduce all members from the class we wrap (because we are
convertible to it!)
};

// usage

std::string s = "hello";
auto rs = reference_wrapper(s);

rs[0]; //< std::string::operator[]

The last call will be as-if

1) std::string operator[](const std::string&, std::size_t); //< "free
function"
2) operator[](rs.operator std::string&(), 0); //< call

The updated file also has a section about a possible extension to have
associated function have a different name

typename std::meta::info using
{
name = name_of; //< rename
type = type_of; //<
}

info.type().name();

instead of

info.type_of().name_of();
Post by m***@gmail.com
Hello, I have written a proposal for Unified Call Syntax, based on the
previous discussion.
https://1drv.ms/u/s!AifghXUnpCc8hAaU7HpR2avsms3b
Feedback is welcome.
PS. This is the fifth attempt to post, hence the link instead of an
attachment.
--
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+***@isocpp.org.
To post to this group, send email to std-***@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/8a9089b8-c4f7-4fff-bfce-4bce30e7c46c%40isocpp.org.
Loading...