-
Notifications
You must be signed in to change notification settings - Fork 260
[SUGGESTION] Multiple suffixes for literals, a revisit of Uniform Function Call Syntax #284
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
I've to correct some parts of my suggestion.
If using free functions for defining user defined literals seems too much radical change (if we consider binary compatibility with C++1), we should keep using
The goal of my suggestion is about to allow multiple suffixes on literals (for example |
Writing
Why not |
This might be out there, but with unified function call syntax, could we treat literals and built in types the same as user types and call 1.gram() which would be equivalent to gram(1)? Then we don't need to introduce custom operators or new syntax. It would naturally allow chaining too.
On 17 March 2023 09:28:00 Abhinav00 ***@***.***> wrote:
Writing ' to use literal suffix seems extra work, what if we just define
operator gram :(value :int)
Why not , instead of '?
—
Reply to this email directly, view it on GitHub<#284 (comment)>, or unsubscribe<https://github.com/notifications/unsubscribe-auth/AALUZQO5VIS7XY43ZSBSOUTW4QVB5ANCNFSM6AAAAAAV5MNURU>.
You are receiving this because you are subscribed to this thread.Message ID: ***@***.***>
|
Because
|
Yes, you're right. Literals, member functions and free functions do really the same thing, but in a different style (syntax), and they help programmers to convey more expressively the concept of code. |
Might! Anything that eases the use of units, indirectly allow the compiler to catch bugs that we all face every so often. Sure, those bugs of ours aren't as glamorous as the space probe that missed Mars because someone wrote code in non-international units. But they might lead to security vulnerabilities. |
Thanks @msadeqhe for the suggestion, and thanks [edit: Well, this turned into a fascinating little detour, and I think I like the result! [edit:
But it didn't already work for integers because "1." and "1.f" were valid floating point literals, so that I say "were" and "couldn't" because I just changed that... I've long disliked that the C floating point literal syntax allowed omitting the I've now pushed the above commit that:
With the above commit, the following now works too:
Note this is also safer, because you know right away you parsed a valid... you don't have to do run-time error checking as with the first string_view example in case someone passes a string that doesn't actually contain an integer (or only an integer). I think this is an interesting path to explore... let me know what you think as you play around with using this style for unit-type literals. (*) see sample implementations at https://stackoverflow.com/questions/56634507/safely-convert-stdstring-view-to-int-like-stoi-or-atoi |
Thanks @hsutter. I have a question, will C++2 support User-defined Literals? I've tried to overload
Does it mean we should use UFCS instead of User-defined Suffixes? |
It's probably a probably with Edit: Still doesn't do anything, so its not about Edit 2: I was wrong |
Yes,
|
For authoring UDLs: I hadn't decided whether or not to support authoring them, and this thread convinces me we should try using UFCS for that first. It's desirable to solve the problem using a more general language feature, if it works well also for this use case, rather than adding a special-purpose language feature. For consuming existing Cpp1 UDLs: I haven't made any special effort to either support or prevent consuming them, and I generally want things like that to work for compatibility so that Cpp2 can consume existing Cpp1 libraries with high fidelity. I did a quick test using the cppreference UDL page's Coliru example code for UDLs, and it works with
|
While I agree with that, if we already have literals in the langauge, why not just go ahead and let users define theirs? |
@AbhinavK00 you can still define it on the cpp1 side and use it in cpp2. In my project, I have been playing with this interoperability between cpp2 and cpp1 (it is a feature of cpp2). When there will be enough evidence for any solution it can be introduced into the cpp2. The best way to support that is to write code that uses it (or port some code to cpp2) and show that it works well or badly. |
Just wanted to point out that the idea of using UFCS for this was @SebastianTroy 's, not mine 🙂 |
@jcanizales @SebastianTroy Eek, thanks -- corrected, and thanks again for the idea and discussion. |
Nae bosh =]
On 22 March 2023 19:40:03 Herb Sutter ***@***.***> wrote:
@jcanizales<https://github.com/jcanizales> @SebastianTroy<https://github.com/SebastianTroy> Eek, thanks -- corrected, and thanks again for the idea and discussion.
—
Reply to this email directly, view it on GitHub<#284 (comment)>, or unsubscribe<https://github.com/notifications/unsubscribe-auth/AALUZQOA2UDFNZRDJIZH2PTW5NIRBANCNFSM6AAAAAAV5MNURU>.
You are receiving this because you were mentioned.Message ID: ***@***.***>
|
Moved to #307. |
Maybe this works by chance, but you can use |
That's interesting. I think it's a bug. AFAIK a type is not supposed to be called like a member function with UFCS, and there isn't a C++ style cast in Cpp2: x0: = Type(2); // It's a Cpp1-style cast and object construction.
x1: = 2.Type(); // It's not UFCS. |
I don't think Cpp2 intends to ban |
But Cpp2 is context-free. If |
Grammatically, An example where grammar matters is the |
Thanks for info. I don't have enough knowledge about how compilers and transpilers work, but |
It also works with function objects: https://cpp2.godbolt.org/z/44zGx8TMq. |
That's right. Function objects |
See #193 (comment) starting from "Re function-style construction notation:". Quote
-- Extract from #193 (comment). |
Thanks. So eventually |
Note that in expressions cppfront currently supports both Although I didn't initially intend the latter function-style use, I haven't seen anything inherently wrong with it, and so I've left it in because it seems useful. This thread shows another useful case... I don't think At first blush, it seems clean and general to me that |
It's surprising to me, because:
|
Also it would conflict with multiple inheritance in Cpp2, it depends on how we would access base types: Base1: type = {
operator=: (out this) = {}
operator=: (out this, v: x) = {}
operator(): (this) -> int = 0;
}
Base2: type = {
operator=: (out this) = {}
operator=: (out this, v: x) = {}
operator(): (this) -> int = 0;
}
x: type = {
this: Base1 = ();
this: Base2 = ();
variable: Base1 = ();
operator(): (this) -> int = {
// It calls operator().
m: = this.variable();
// Does it call operator() from Base1?
// or calls the constructor with `Base1(this)`?
// It's ambiguous because of UFCS on types.
n: = this.Base1();
return 0;
}
} In example above, It would complicate the language, similar to how |
I couldn't find main reasons that why it's surprising to me. Now I've found them:
So UFCS on types would unify
A: type = {
X: type = {}
}
B: type = {
operator=: (out this, a: A) = {}
}
main: () = {
a: A = ();
// It works.
// It's equal to `B(a)`.
m: = a.B();
// a.B() == B(a)
// ERROR! It doesn't work.
// It must be `A::X()`.
n: = a.X();
// a.X() != A::X(a)
} So UFCS on types is in contrast to the purpose of |
Isn't a constructor just a function that returns an instance of a type?
On 16 May 2023 06:17:52 Sadeq ***@***.***> wrote:
It's surprising for me, because:
1. UFCS is about Unifying Function Call Syntax, and suddenly it works with types.
2. It doesn't feel expressive enough for a context-free language. : Type = (args) creates a variable, but A(args) (also a.A(args)) may create a variable or may call a function (or function object).
3. It's like accessing a base class within multiple inheritance, e.g. a.Base::call() in Cpp1.
I couldn't find the real reason that why it's surprising for me. Now I find it:
1. UFCS is about to unify a.function(args) (member functions) with function(a, args) (non-member functions). It's important to note that both of them are valid syntax. On the other hand, a.Type(args) is a syntax sugar to Type(a, args). But the problem with a.Type(args) is that itself is not a valid syntax! It's not a nested type! It must be a::Type(args) to be a valid syntax. So UFCS on types would unify an invalid syntax a.Type(args) (nested type which has to be a::Type(args)) with Type(arg) (object construction). So UFCS on types are semantically incorrect.
—
Reply to this email directly, view it on GitHub<#284 (comment)>, or unsubscribe<https://github.com/notifications/unsubscribe-auth/AALUZQODTB3KJK43ADHTRQDXGMEXRANCNFSM6AAAAAAV5MNURU>.
You are receiving this because you were mentioned.Message ID: ***@***.***>
|
|
Member functions and types are completely different, but unwillingly they will impact each other. It's is in contrast with UFCS for functions in which it only impacts on what function to call. abc: type = {
klass: (this) = {}
}
klass: type = {
operator=: (out this, v: abc) = {}
}
main: () = {
a: abc = ();
// It conflicts...
// Does it call the constructor of `klass`?
// or it calls the member function `klass`?
a.klass();
} In this example, the meaning of
@SebastianTroy, That's not the whole story. It has side effects especially when UFCS comes in. I think you read my comments via email, therefore you didn't read my examples because I've edited my comments and added them later.
@AbhinavK00, I hope they would be changed to |
In contrast, But a.Type() --> operator=(out this, a)
a.func() --> func(a) // `this = a` for member functions |
There is a difference between |
@JohelEGP That's an interesting implementation detail. |
@hsutter, there are some discussion about The following paragraphs are the related parts of my comments from there to here. Inherently types are not callables, they are block of related declarations. Whereas their constructors are callable, but their
To make this behaviour expressive enough, the syntax for calling constructors have to be different. The most similar concept to constructors are UDLs, therefore constructors can be called like they are generalized simpler UDLs. On the other hand, |
C++23 have both @hsutter, by making the syntax of object creation from Ab: type = {
// static operator()
operator(): (i: int, j: int) -> bool = i == j;
// static operator[]
operator[]: (i: int, j: int) -> bool = i == j;
};
ab: Ab = ();
m: bool = Ab(1, 2); // It calls `static operator()` directly.
n: bool = ab(1, 2); // It calls `static operator()` too.
x: bool = Ab[1, 2]; // It calls `static operator[]` directly.
y: bool = ab[1, 2]; // It calls `static operator[]` too. |
To enable authoring the |
… UFCS on numeric literals (1) I find `1.0 and `1.0f` clearer than `1.` and `1.f` anyway, so that's a small plus. (2) This was the only thing in the way of using UFCS on numeric literals, so that's another maybe-medium plus. And the latter enables writing unit libraries like `42.gram()`, as proposed in hsutter#284's comment thread, which is an interesting use of UFCS on literals. So I'll mark this as closes hsutter#284.
In our life, every quantity can have multiple suffixes. For example:
In C++1, optional
'
may be inserted between the digits of integer and floating-point literals as a separator. Therefore I propose that every suffix optionally may start with'
and mandatory'
must be inserted between them as a separator for multiple suffixes. Now, let's write the code for the above cases:Also built-in suffixes optionaly may start with
'
. They can be uppercase or lowercase. The built-in integer suffixes areL
,LL
,Z
,U
,UL
,ULL
andUZ
(ifU
is provided, it must be the first in C++2, but it can be the last in C++1). The built-in floating-point suffixes areF
,L
,F16
,F32
,F64
,F128
andBF16
as defined in C++23.Encoding prefixes of character literals and string literals have to be changed to be suffix (similar to C# language), the first suffix have to be without
'
, because the end of character literals and string literals already have a quote:As an extra feature, boolean literals may have suffixes, but it's really a special case for libraries:
In addition, we can write user defined suffixes after built-in suffixes:
Now, the question is what does it have common to uniform function call syntax. To answer this question, the following example is useful:
So, definitely suffixes are the same as member functions or free functions. We don't need an
operator""
to create user defined literals, each member function with the right signature can be used as suffix. Also free functions with the right signature can be used as suffix. For example:No, suffixes should never get any extra parameter, it will ruin its purpose of being less verbose (being without parenthesis), and they should not modify
this
parameter.Will your feature suggestion improve performance or expressiveness?
Yes, because user defined literals can improve expressivity of the programmer:
Maybe it improves performance too, because for built-in suffixes the true type of the literal will be sent to the member function call:
If we rewrite the above example with
operator""
in C++1, then the type ofvalue
parameter must belong double
instead offloat
, becauseoperator""
only accepts a parameter of typeunsigned long long int
orlong double
.Uniform function call syntax will also improve generic programming for suffixes.
Will your feature suggestion eliminate X% of security vulnerabilities of a given kind in current C++ code?
No.
Will your feature suggestion automate or eliminate X% of current C++ guidance literature?
Yes. My feature suggestion will remove unnecessary extra concept
operator""
, and it will integrate its usage with normal member functions or free functions. Also It improves code readability.The purpose of suffix notation is for units or quantities, member function notation is for object-style behaviors or states, and free function notation is for computing.
Describe why you've considered this feature request.
Currently I'm trying to suggest a new string literal for C++2. This suggestion will affect my suggestion about string literals. I hope to read your feedbacks here, therefore I can improve it or I should give up.
The text was updated successfully, but these errors were encountered: