You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[SUGGESTION] Generalize UFCS x.f(args) to try f<x>(args) #307
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.
[...]
-- #284 (comment)
It's desirable to solve the problem using a more general language feature
It's great that x.f() works regardless of x's type or whether x is a literal or not. Which is unlike C++1's UDL, which requires long double or long long parameter for a numeric UDL, and a literal argument for the nicer syntax.
What do you think about adding the above attempt to the UFCS feature?
Will your feature suggestion eliminate X% of security vulnerabilities of a given kind in current C++ code? No (my guess).
Will your feature suggestion automate or eliminate X% of current C++ guidance literature? No (my guess)
Describe alternatives you've considered.
Supporting UDLs, like C++1.
I am happy that you bring CRTE - to be honest, one of my first contributions to the cppfront was is/as implementation. I did one experiment with CRTE and provided is overload for CRTE regex:
template <ctll::fixed_string pattern, typename X >
requires std::is_convertible_v<X, std::string_view>
autois( X const& x ) -> bool {
return ctre::match<pattern>(x);
}
That allows you to test something against regex in the following manner:
I noticed that the README doesn't mention a UFCS proposal. IIRC, Herb and Bjarne have worked on them together. I wanted to see what it said about this issue.
CRTE
You have the R and T backwards. I wonder if that has to do with the repository's renaming from CTRE. I haven't been following it.
I did not propose that, as Herb is unwilling to include external dependencies, and I treat it as an experiment.
That's good. is is supposed to be overloadable. The question would be, is overloading currently supported?
That's weird. I feel like I've seen this before (was it here?). What if you wrapped the inner if-else in the else of the first if?
if (…) {…}
else {
if (…) {…}
else {…}
}
I also see that you rightly use PARAM1 rather than forwarding obj. It's a shame that PARAM1 has to be materialized even though it may remain unused, in this form of CPP2_UFCS. (This means that you may want to mark obj as [[maybe_unused]]).
Considering the inconsistent diagnosis between Clang and GCC (MSVC accepts), not using the macro-pasted template name as a template in a discarded statement must be IFNDR.
Forward declaring a function with the template name works for GCC in the simpler cases: https://compiler-explorer.com/z/nrK66xzvP; Clang diagnoses redefinition of '<variable template>' as different kind of symbol. Interestingly, GCC gives the same diagnosis as Clang when forward declaring as void FUNCNAME(...);. MSVC starts rejecting MF UFCS if FUNCNAME isn't a function at global scope, defining that FUNCNAME at global scope as any other kind of entity breaks all compilers. Despite f and h being function templates, it seems that h being constrained makes it not work without the forward declaration. And forward declaring doesn't help with CTRE: https://compiler-explorer.com/z/T9Kx7fGvW.
Another thing that came to mind as possible points against this suggestion are reasons for the rejection of "constexpr argument" proposals. The points I remember are f(x) not being obvious that it does one instantiation per value of x, and/or the compile-time costs of that. The same would apply to UFCS on x.f() performing f<x>().
Actually asserting the test cases shows that forward declaring FUNCTION doesn't help. Remove it, and h and i are diagnosed consistently on all compilers: https://compiler-explorer.com/z/b3YKqM8vd.
GCC : <source>:11:24: error: no matching function for call to 'h(char, int)'
Clang: <source>:35:36: error: no matching function for call to 'h'
MSVC : <source>(35): error C2672: 'h': no matching overloaded function found
GCC : <source>:11:24: error: missing template arguments before '(' token
Clang: <source>:36:36: error: use of variable template 'i' requires template arguments
MSVC : <source>(36): error C3245: 'i': use of a variable template requires template argument list
using namespace ctre; makes sense. UFCS shouldn't work on a qualified name (although only GCC seems to reject that case). Using the _ctre UDL also needs a using declaration.
h and i are diagnosed
Turns out I forgot to append auto to h's template parameter to make it an NTTP. It was a type-constraint. So it works now: https://compiler-explorer.com/z/8Mf9bonnE.
I'm torn on this issue. I still think it'd be great if UFCS could subsume UDL's use cases with more generality. But without C++1 reflection, it seems it'd only be possible to support function templates. Even then, with the new branch in the macro, a variable named after a member function breaks UFCS, an overload for the new template branch breaks the discarded free function branch (commented lines at https://compiler-explorer.com/z/rT9cP3oMn give consistent diagnostics between all compilers), and there's probably more unknowns. And then there's:
Another thing that came to mind as possible points against this suggestion are reasons for the rejection of "constexpr argument" proposals. The points I remember are f(x) not being obvious that it does one instantiation per value of x, and/or the compile-time costs of that. The same would apply to UFCS on x.f() performing f<x>().
-- #307 (comment)
Thanks everyone for this discussion. It sounds to me like this would still be hard/fragile to implement. But on a more principled level, right now we have a clear distinction where the member function call syntax x.f() does UFCS which has as a major benefit that the argument comes first (for easier Intellisense etc.), and the ordinary function call syntax f(x) doesn't do UFCS. I worry that f<x>() would be blurring that line by pushing UFCS into the other ordinary function call syntax, and also not get the Intellisense benefits. So I'll close this one for now, but if you think I'm missing something please feel free to reopen. Thanks for understanding, and for the insights above!
Activity
filipsajdak commentedon Mar 29, 2023
I can prepare a POC to have some tests on that.
I am happy that you bring CRTE - to be honest, one of my first contributions to the cppfront was is/as implementation. I did one experiment with CRTE and provided
isoverload for CRTE regex:That allows you to test something against regex in the following manner:
I did not propose that, as Herb is unwilling to include external dependencies, and I treat it as an experiment.
JohelEGP commentedon Mar 29, 2023
I noticed that the README doesn't mention a UFCS proposal. IIRC, Herb and Bjarne have worked on them together. I wanted to see what it said about this issue.
You have the R and T backwards. I wonder if that has to do with the repository's renaming from CTRE. I haven't been following it.
That's good.
isis supposed to be overloadable. The question would be, is overloading currently supported?filipsajdak commentedon Mar 29, 2023
So... doing a quick and dirty feasibility check, I have modified the
CPP2_UFCSmacro to check what you asked it looks like this:And I have tried to compile the following code:
And end up with an error:
The issue is that the compiler is evaluating the last else part:
And fails as
FUNCNAMEis a template. When I remove the last else section, it compiles fine. So, the following implementation of CPP2_UFCS:It works, but we lost a match to the free function. We need to solve this issue if you want to proceed with this suggestion.
PS: sorry for misspelling CTRE with CRTE
JohelEGP commentedon Mar 29, 2023
That's weird. I feel like I've seen this before (was it here?). What if you wrapped the inner if-else in the else of the first
if?I also see that you rightly use
PARAM1rather than forwardingobj. It's a shame thatPARAM1has to be materialized even though it may remain unused, in this form ofCPP2_UFCS. (This means that you may want to markobjas[[maybe_unused]]).filipsajdak commentedon Mar 29, 2023
I will try that when I get back to my PC.
filipsajdak commentedon Mar 29, 2023
This does not work (of course, I have used constexpr versions of ifs):
If you are interested in running that, I can spend some time finding a solution.
On the other hand, it is just syntactic sugar - I would like to focus more on more exciting topics.
You can call this with the following code:
That generates:
That compiles fine and returns:
JohelEGP commentedon Mar 29, 2023
GCC also rejects the middle branch: https://compiler-explorer.com/z/MGqM3Mz5q.
With
CPP2_UFCS_TEMPLATE: https://compiler-explorer.com/z/W1odG3hYb.I'm interested, too. Not particularly about CTRE. I just used it as an example I knew would be familiar.
Yeah. One that pulls it weight over UDLs. Simpler cases already work, so let's not dismiss this just yet: https://compiler-explorer.com/z/fbcT33W8W.
JohelEGP commentedon Mar 30, 2023
Considering the inconsistent diagnosis between Clang and GCC (MSVC accepts), not using the macro-pasted template name as a template in a discarded statement must be IFNDR.
Forward declaring a function with the template name works for GCC in the simpler cases: https://compiler-explorer.com/z/nrK66xzvP; Clang diagnoses
redefinition of '<variable template>' as different kind of symbol. Interestingly, GCC gives the same diagnosis as Clang when forward declaring asvoid FUNCNAME(...);. MSVC starts rejecting MF UFCS ifFUNCNAMEisn't a function at global scope, defining thatFUNCNAMEat global scope as any other kind of entity breaks all compilers. Despitefandhbeing function templates, it seems thathbeing constrained makes it not work without the forward declaration. And forward declaring doesn't help withCTRE: https://compiler-explorer.com/z/T9Kx7fGvW.Another thing that came to mind as possible points against this suggestion are reasons for the rejection of "constexpr argument" proposals. The points I remember are
f(x)not being obvious that it does one instantiation per value ofx, and/or the compile-time costs of that. The same would apply to UFCS onx.f()performingf<x>().JohelEGP commentedon Mar 31, 2023
MSVC is available, so I updated ^.
General summary:
FUNCTIONnames a variable template, Clang fails.FUNCTIONis a MF.Support for
x.f()translating tof<x>()whenfis not a function template would require C++1 reflection.JohelEGP commentedon Mar 31, 2023
No problem. I committed the same mistake with regularity at the beginning. Now I remember it was due to CRTP.
JohelEGP commentedon Mar 31, 2023
Your generated code from #307 (comment) inserts a
if (std::move(m))without it being its last use.filipsajdak commentedon Mar 31, 2023
Yes, that is true. It is a bug.
filipsajdak commentedon Mar 31, 2023
It looks like an issue with
if. It also manifests in a simple case:main: () = { b : bool = true; if b { std::cout << b << std::endl; } }Generates:
I will create a bug report.
filipsajdak commentedon Mar 31, 2023
Reported here: #311
JohelEGP commentedon Apr 2, 2023
CTRE works for all compilers with the forward declaration
void FUNCNAME(…);andusing namespace ctre;: https://compiler-explorer.com/z/1nePoxMvd.The
jproblem on MSVC is worked around by inserting the new branch for UFCS in the middle: https://compiler-explorer.com/z/K9T8zrhfP.Actually asserting the test cases shows that forward declaring
FUNCTIONdoesn't help. Remove it, andhandiare diagnosed consistently on all compilers: https://compiler-explorer.com/z/b3YKqM8vd.JohelEGP commentedon Apr 2, 2023
using namespace ctre;makes sense. UFCS shouldn't work on a qualified name (although only GCC seems to reject that case). Using the_ctreUDL also needs ausingdeclaration.Turns out I forgot to append
autotoh's template parameter to make it an NTTP. It was a type-constraint. So it works now: https://compiler-explorer.com/z/8Mf9bonnE.I'm torn on this issue. I still think it'd be great if UFCS could subsume UDL's use cases with more generality. But without C++1 reflection, it seems it'd only be possible to support function templates. Even then, with the new branch in the macro, a variable named after a member function breaks UFCS, an overload for the new template branch breaks the discarded free function branch (commented lines at https://compiler-explorer.com/z/rT9cP3oMn give consistent diagnostics between all compilers), and there's probably more unknowns. And then there's:
JohelEGP commentedon Apr 4, 2023
h<'4'>(5)was ambiguous, so it wasn't discarded. Redone: https://compiler-explorer.com/z/7zWcM9PbK.hsutter commentedon Apr 9, 2023
Thanks everyone for this discussion. It sounds to me like this would still be hard/fragile to implement. But on a more principled level, right now we have a clear distinction where the member function call syntax
x.f()does UFCS which has as a major benefit that the argument comes first (for easier Intellisense etc.), and the ordinary function call syntaxf(x)doesn't do UFCS. I worry thatf<x>()would be blurring that line by pushing UFCS into the other ordinary function call syntax, and also not get the Intellisense benefits. So I'll close this one for now, but if you think I'm missing something please feel free to reopen. Thanks for understanding, and for the insights above!