cppfront
cppfront copied to clipboard
UFCS: function call starts UFCS chaining
On the stdio example there is a call to c-function:
myfile := fopen("xyzzy", "w");
myfile.fprintf( "Hello with UFCS!" );
myfile.fclose();
This is great but we can do better than that. What I don't like about this approach is the lost opportunity to save us from resource leak. What I would like to do is to have a possibility to wrap the return FILE*
into something that will clean things up.
I have implemented the feature that starts UFCS chaining on the function call of syntax func1().func2() (it can go recursively further). That brings us more interesting features like wrapping things into smart pointers in one call.
#include <vector>
#include <memory>
template <typename T, typename R>
auto on_scope_exit(T* f, R(*close)(T*)) {
return std::unique_ptr<T, decltype(close)>{f, close};
}
main: () -> int = {
f2 := fopen("variable2.txt", "w").on_scope_exit(fclose);
f2.get().fprintf("you can handle smart_ptrs without changing behaviour of UFCS");
m := fopen("manual.txt", "w");
m.fprintf("Manual handling still works");
m.fclose();
}
That can go even further. Thanks to https://github.com/hsutter/cppfront/pull/13 the code can be even simpler:
f2 := fopen("variable2.txt", "w").on_scope_exit(fclose);
f2.fprintf("you can handle smart_ptrs without changing behaviour of UFCS");
m := fopen("manual.txt", "w");
m.fprintf("Manual handling still works");
m.fclose();
f := fopen("variable.txt", "w").on_scope_exit(fclose);
f.fprintf("This one use variable and\n");
f.fprintf(" separate\n");
f.fprintf(" calls\n");
f.fprintf(" of fprintfs");
fopen("one_liner.txt", "w").on_scope_exit(fclose)
.fprintf("This is oneliner!!\n\nAnd it just close automatically");
}
The last use case (one-liner) makes me think that UFCS brings some risk of leaking resources when chained. When results of fopen
will be used by fprintf
directly the pointer to file will be lost.
// bad use
fopen("one_liner.txt", "w").fprintf("there is a leak on the end of this function");
I will investigate if there is a possibility to block potentially leaking combinations. Smart pointers are safe and we can eliminate cases when rvalue pointers are passed to a chained function.
// bad use fopen("one_liner.txt", "w").fprintf("there is a leak on the end of this function");
I will investigate if there is a possibility to block potentially leaking combinations.
https://github.com/isocpp/CppCoreGuidelines/blob/master/docs/Lifetime.pdf should take care of that. It mentions:
Additionally, for convenient adoption without modifying existing standard library headers, the following well-known standard types are treated as-if annotated as Owners: stack, queue, priority_queue, optional, variant, any, and regex.
It could do similarly for fopen
. If it were my_fopen
instead, I think you're out of luck if you don't wrap it safely.
Another interesting extension can be proposed to make calling functions like fprintf
that are not returning FILE*
and stops the chaining.
visit: (in v:_, in callable:_) -> auto = {
return callable(v);
}
main: () -> int = {
fopen("one_liner.txt", "w").on_scope_exit(fclose).get().visit(:(e:_) = {
e.fprintf("1) This is oneliner!!\n\n");
e.fprintf("2) This is oneliner!!\n\n");
e.fprintf("3) This is oneliner!!\n\n");
e.fprintf("4) This is oneliner!!\n\n");
e.fprintf("5) This is oneliner!!\n\n");
e.fprintf("6) This is oneliner!!\n\n");
});
}
(Repeating from #17) Interim ack: Thanks! Yes, I've been meaning to add chaining. I was thinking of implementing it later in the same function and remove this special case part of the function (I dislike that there are two paths that deal with . operator differently)... let me keep this PR open without action until I get a chance to look at it again later.
Picking this up: I'm willing to consider UFCS chaining, even as an additional section within the function. Before I look at this more closely... do you want to pursue this now? if yes does it need to be rebased including to mirror the updates I just made to the current UFCS code (we want to stay consistent)? Thanks!
Adding bidirectional xrefs: How does this relate to #17?
This is for a case when there is a free function call and the chaining starts on the function result.
#17 is for the cases when the first call is done on the object by calling its method.
I am pretty sure it needs to be rebased. I can do that tomorrow.
This PR is replaced by: https://github.com/hsutter/cppfront/pull/169