-
Notifications
You must be signed in to change notification settings - Fork 260
[SUGGESTION] Generalize UFCS x.f(args)
to try f<x>(args)
#307
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 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 template <ctll::fixed_string pattern, typename X >
requires std::is_convertible_v<X, std::string_view>
auto is( X const& x ) -> bool {
return ctre::match<pattern>(x);
} That allows you to test something against regex in the following manner: if email is "[\\-_a-zA-Z0-9.+!%]*@[\\-_a-zA-Z0-9.]*" {
std::cout << "correct email" << std::endl;
} else {
std::cout << "incorrect email" << std::endl;
} I did not propose that, as Herb is unwilling to include external dependencies, and I treat it as an experiment. |
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. |
So... doing a quick and dirty feasibility check, I have modified the #define CPP2_UFCS(FUNCNAME,PARAM1,...) \
[&](auto&& obj, auto&& ...params) CPP2_FORCE_INLINE_LAMBDA -> decltype(auto) { \
if constexpr (requires{ FUNCNAME<PARAM1>(std::forward<decltype(params)>(params)...); }) { \
return FUNCNAME<PARAM1>(std::forward<decltype(params)>(params)...); \
} \
else if constexpr (requires{ std::forward<decltype(obj)>(obj).FUNCNAME(std::forward<decltype(params)>(params)...); }) { \
return std::forward<decltype(obj)>(obj).FUNCNAME(std::forward<decltype(params)>(params)...); \
} else { \
return FUNCNAME(std::forward<decltype(obj)>(obj), std::forward<decltype(params)>(params)...); \
} \
}(PARAM1, __VA_ARGS__) And I have tried to compile the following code: #include "ctre.hpp"
main: () = {
"REGEX".ctre::match("Is this REGEX?");
} And end up with an error:
The issue is that the compiler is evaluating the last else part: return FUNCNAME(std::forward<decltype(obj)>(obj), std::forward<decltype(params)>(params)...); \ And fails as #define CPP2_UFCS(FUNCNAME,PARAM1,...) \
[&](auto&& obj, auto&& ...params) CPP2_FORCE_INLINE_LAMBDA -> decltype(auto) { \
if constexpr (requires{ FUNCNAME<PARAM1>(std::forward<decltype(params)>(params)...); }) { \
return FUNCNAME<PARAM1>(std::forward<decltype(params)>(params)...); \
} \
else if constexpr (requires{ std::forward<decltype(obj)>(obj).FUNCNAME(std::forward<decltype(params)>(params)...); }) { \
return std::forward<decltype(obj)>(obj).FUNCNAME(std::forward<decltype(params)>(params)...); \
} \
}(PARAM1, __VA_ARGS__) 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 |
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 (…) {…}
else {
if (…) {…}
else {…}
} I also see that you rightly use |
I will try that when I get back to my PC. |
This does not work (of course, I have used constexpr versions of ifs): if (…) {…}
else {
if (…) {…}
else {…}
} 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: #include "ctre.hpp"
main: () = {
m := ctre::match<"(?<chars>[a-z]+)([0-9]+)">("abc123");
if m {
std::cout << m.get<"chars">() << std::endl;
std::cout << m.get<2>() << std::endl;
}
} That generates: auto main() -> int{
auto m {ctre::match<"(?<chars>[a-z]+)([0-9]+)">("abc123")};
if (std::move(m)) {
std::cout << CPP2_UFCS_TEMPLATE_0(get, (<"chars">), m) << std::endl;
std::cout << CPP2_UFCS_TEMPLATE_0(get, (<2>), std::move(m)) << std::endl;
}
} That compiles fine and returns:
|
GCC also rejects the middle branch: https://compiler-explorer.com/z/MGqM3Mz5q.
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. template<int> void f(int*);
struct X {
constexpr X(const char(&)[2]) { }
};
template<X> void g(int);
int main() {
CPP2_UFCS(f, 1, nullptr);
CPP2_UFCS(g, "2", 3);
} |
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 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 |
MSVC is available, so I updated ^. General summary:
Support for |
No problem. I committed the same mistake with regularity at the beginning. Now I remember it was due to CRTP. |
Your generated code from #307 (comment) inserts a |
Yes, that is true. It is a bug. |
It looks like an issue with main: () = {
b : bool = true;
if b {
std::cout << b << std::endl;
}
} Generates: auto main() -> int{
bool b {true};
if (std::move(b)) {
std::cout << std::move(b) << std::endl;
}
} I will create a bug report. |
Reported here: #311 |
CTRE works for all compilers with the forward declaration The Actually asserting the test cases shows that forward declaring template<std::same_as<char>> constexpr char h(int) { return 'h'; }
template<int> constexpr auto i = [](std::same_as<bool> auto) { return 'i'; };
static_assert('h' == CPP2_UFCS(h, '4', 5));
static_assert('i' == CPP2_UFCS(i, 4, true));
|
Turns out I forgot to append -template<std::same_as<char>> constexpr char h(int) { return 'h'; }
+template<std::same_as<char> auto> constexpr char h(int) { return 'h'; } 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:
|
|
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 |
Something that can't be done in Cpp2 is implement https://github.com/hanickadot/compile-time-regular-expressions's second syntax, which is valid C++20:
"REGEX".ctre::match(subject)
wouldn't tryctre::match<"REGEX">(subject)
.It's great that
x.f()
works regardless ofx
's type or whetherx
is a literal or not. Which is unlike C++1's UDL, which requireslong double
orlong 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.
The text was updated successfully, but these errors were encountered: