cppfront icon indicating copy to clipboard operation
cppfront copied to clipboard

Caveat: There's little else in the C stdlib that allocates a resource

Open filipsajdak opened this issue 2 years ago • 2 comments

@hsutter regarding your question: https://github.com/hsutter/cppfront/blob/bf5998a5e9145f11a935c2700b1db50f6e2de2fa/include/cpp2util.h#L809

I am currently trying to use cppfront to build my project (that's why I send PRs - I am implementing what I am missing).

I use something like your c_raii (I call it scope_exit and I have a function on_scope_exit that produces it) for handling popen (here I am using UFCS chaining from here: https://github.com/hsutter/cppfront/pull/18):

execute: (cmd : std::string) -> auto = {
    std_out := popen(cmd.c_str(), "r").on_scope_exit(pclose);
    buf : std::array<char, 1024> = ();

    output : std::string = ();

    read_size := fread(buf.begin(), 1, buf.size(), std_out);
    while read_size > 0 next read_size = fread(buf.begin(), 1, buf.size(), std_out) {
        output += std::string(buf.begin(), read_size); 
    }
    return output;
}

and to remove temporary directories on scope exit

create_temporary_directory: () -> auto = {
    tmp := fs::temp_directory_path();
    build_dir := on_scope_exit(tmp / "md_tests/build", :(p : fs::path) = { 
        fs::remove_all(p); 
        std::cout << "removed tmp directory: (p)$" << std::endl;
    } );
    fs::create_directories(build_dir);
    std::cout << "created tmp directory: (fs::path(build_dir))$" << std::endl;
    return build_dir;
}

My current implementation of on_scope_exit():

template <typename T, typename D>
struct scope_exit {
    scope_exit(T v, D d) 
        : value(v)
        , deleter(d)
    {
    }

    ~scope_exit() {
        deleter(value);
    }

    operator T&() { return value; }

private:
    T value;
    D deleter; 
};

on_scope_exit: (forward v : _, forward d : _) -> auto = {
    return scope_exit(v,d);
}

I will check your implementation and will let you know.

filipsajdak avatar Oct 02 '22 02:10 filipsajdak

Thanks, let me know what you find!

hsutter avatar Oct 05 '22 00:10 hsutter

I checked your implementation. I don't understand why you are using (D)(x); in the lambda for the destructor but it seems to work with C functions.

auto good = c_raii(std::fopen("dd", "w"), std::close); // works

but this doesn't:

auto bad = c_raii(std::fopen("dd", "w"), [](FILE* x) { std::fclose(x); } ); // doesn't work

The above is useful when additional actions are needed or for logging purposes.

I know that you care about real cases so I check other C functions from POSIX that might shed some light on potential use cases.

I found functions for handling POSIX shared memory objects:

int shm_open(const char *name, int oflag, mode_t mode);
int shm_unlink(const char *name);

This is an interesting case as the return type is a file descriptor that is used for the next actions (so T will be int) but unlinking needs to be done on the name argument. That makes cpp2::c_raii useless for that case as it would need to use e.g. lambda with a capture list that will not match the function pointer.

Is there a reason why the c_raii doesn't look like the code below?

template<typename T, typename D>
class c_raii {
    T t;
    D dtor;
public:
    c_raii( T t_, D d_) // maybe forwarding the arguments would be better
        : t{ t_ }
        , dtor{ d_ }
    { }

    ~c_raii() { dtor(t); }

    operator T&() { return t; }
//...
};

filipsajdak avatar Oct 08 '22 21:10 filipsajdak

Good idea, thanks! Applied.

hsutter avatar Dec 16 '22 01:12 hsutter