Skip to content

UFCS try to get pointer using get() after other matches failed #13

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

Conversation

filipsajdak
Copy link
Contributor

The current implementation of UFCS is not handling smart pointers well. For example, using fopen the code looks like this:

main: () -> int = {
    myfile := fopen("file-opened-with-c-function.txt", "w");
    myfile.fprintf("Unified Function Call Syntax Rocks!\n\n");
    myfile.fclose();
}

there is a need to call fclose at the end of the scope. We can introduce better_fopen that will return unique_prt with fclose as a deleted.

main: () -> int = {
    myfile := better_fopen("file-opened-with-c-function.txt", "w");
    myfile.fprintf("Unified Function Call Syntax Rocks!\n\n");
}

That will ensure closing but will not work with UFCS.

In this change, I have introduced an additional check (after other matches failed) if there is a call to the get() method that matches a function signature. If yes then the underlying raw pointer is taken using get() method and used to match function syntax.

@filipsajdak filipsajdak force-pushed the fsajdak-ufcs-works-unroll-smart-pointers-and-optionals branch 2 times, most recently from 90cd42d to e8a48b9 Compare September 26, 2022 00:49
On MacOSX 12.3 SDK there is no <memory_resource> header
there is <experimental/memory_resource> - check with __has_include
to include existing one.

There was no <cuchar> and <stop_token> - add checking if it is present.
In example of using fopen the code looks like:

main: () -> int = {
    myfile := fopen("file-opened-with-c-function.txt", "w");
    myfile.fprintf("Unified Function Call Syntax Rocks!\n\n");
    myfile.fclose();
}

there is a need to call fclose at the end of scope.

We can introduce better_fopen that will return unique_prt
with fclose as a deleter.

main: () -> int = {
    myfile := better_fopen("file-opened-with-c-function.txt", "w");
    myfile.fprintf("Unified Function Call Syntax Rocks!\n\n");
}

That will ensure closing but will not work with UFCS.
In this change we added a check
(after other matches failed) if there is a call to get() method
that match a function signature.
@filipsajdak filipsajdak force-pushed the fsajdak-ufcs-works-unroll-smart-pointers-and-optionals branch from e8a48b9 to 4cd6e71 Compare September 26, 2022 01:08
@jcanizales
Copy link

Is this just a convenience for not having to write:

myfile.get().fprintf("Unified Function Call Syntax Rocks!\n\n");

?

I don't think I feel super good about the language letting us pass invisibly the raw pointer of a smart pointer to a function. That function could be making and storing copies of the pointer, all the while on the calling side everything looks safe.

@jcanizales
Copy link

On the other hand, C++ already allows:

void buggy_function(const std::unique_ptr<T>& i_look_fine_to_the_caller) {
  T* = i_look_fine_to_the_caller.get();
  ...
}

so 🤷

@filipsajdak
Copy link
Contributor Author

@jcanizales I made that before #17.

I still thinks that it is worth experimenting with

@hsutter
Copy link
Owner

hsutter commented Sep 29, 2022

Thanks -- I agree with a way to automate the fclose too in example like this. I don't think making UFCS automatically deref a specific smart pointer is the best way to do that though... that feels like magic and a special case.

I would try an RAII wrapper, perhaps something that could be used like this:

myfile := cpp2::fopen( "file-opened-with-c-function.txt", "w" );

I think I've seen such little wrapper libs floating around the internets but I can't find one just now... here's a very rough cut at it:

// in cpp2util.h
namespace cpp2 {

template<typename T>
class c_raii {
    T t;
    void (*dtor)(void*);
public:
    template<typename D>
    c_raii( T t_, D d )
        : t{ t_ }
        , dtor{ [](void* x) { (D)(x); } }
    { }

    ~c_raii() { dtor(t); }

    operator T&() { return t; }

    c_raii(c_raii const&)           = delete;
    auto operator=(c_raii const&) = delete;
};

}

// in a .cpp2 file
main: () -> int = {
    myfile := cpp2::c_raii( fopen("file-opened-with-c-function.txt", "w"), fclose& );
    myfile.fprintf("Unified Function Call Syntax %s!\n\n", "Rockzz");
}

Then better_fopen could be implemented as:

// in cpp2util.h
namespace cpp2 {
auto fopen( const char* filename, const char* mode ) {
    return c_raii( std::fopen(filename, mode), &fclose );
}
}

And you get:

// in a .cpp2 file
main: () -> int = {
    myfile := cpp2::fopen( "file-opened-with-c-function.txt", "w" );
    myfile.fprintf("Unified Function Call Syntax %s!\n\n", "Rockzz");
}

DISCLAIMERS:

  • I'm writing the RAII helper in Cpp1 syntax because I haven't implemented classes yet in Cpp2 syntax.
  • As I mentioned in the talk, in the context of Cpp2 I'm generally against language feature requests/changes unless they can be shown to improve simplicity, safety, or toolability in a quantifiable way. The reason I spent time on this suggestion is because it helps improve safety of using the C standard library and forgetting to release C-style resources is a known bug farm -- that makes contemplating a general c_raii wrapper more than a "drive-by" language enhancement, and I've already been considering namespace cpp2 passthrough to improve safety for features in the C (and some C++) standard library.

So while I've engaged on this suggestion, everyone please do accept my apologies in advance to many other suggestions that I'm surely going to answer with "I honestly do think that's an interesting idea, but I'm saying no to spending time on it (as with a number of my own such interesting ideas!) because I need to stay disciplined and focus only on quantifiable simplicity/safety/toolability improvements right now" -- thanks in advance for understanding.

@JohelEGP
Copy link
Contributor

Doesn't gsl::final_action cover what c_raii does?

@hsutter
Copy link
Owner

hsutter commented Sep 29, 2022

They're related but not quite the same. A major difference is that you can't easily store a gsl::final_action as a data member as c_raii needs to do. So you use the type-erasure-via-lambda trick instead because you can store a non-capturing lambda as an opaque function pointer data member (it's still type-safe because when the lambda is created it knows and remembers the type).

@hsutter hsutter self-assigned this Sep 29, 2022
@JohelEGP
Copy link
Contributor

The second disclaimer is worth adding as a "suggestion" template issue.

@filipsajdak
Copy link
Contributor Author

filipsajdak commented Sep 29, 2022

@hsutter Please look at #17 and #18 - it is about chaining function calls that allow you to write:

#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();
}

I agree that my first proposal was too magical. After that, I have been experimenting more with cppfront and after implementing function chaining I realize that it allows adding a call to get() in the middle which solves the issue.

I guess that function chaining is a better solution to my original problem and it could go further:

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");
    });
}

@hsutter hsutter closed this in 76a3cca Oct 1, 2022
@hsutter
Copy link
Owner

hsutter commented Oct 1, 2022

The second disclaimer is worth adding as a "suggestion" template issue.

Great suggestion. Done, thanks.

Azmah-Bad pushed a commit to Azmah-Bad/cppfront that referenced this pull request Feb 24, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants