Home > Blockchain >  How can I retrieve the return value of pclose when the unique_ptr is destroyed?
How can I retrieve the return value of pclose when the unique_ptr is destroyed?

Time:05-25

As @user17732522 pointed out that the deleter of std::unique_ptr is supposed to be callable with exactly one pointer as argument.

How can I retrieve the return value of pclose when the unique_ptr is destroyed?

This code snippet does not compile,

#include<memory>
#include<iostream>
#include<string>
#include<cstdio>

int ExecCmd(const std::string& cmd, std::string& output)
{
    int ret = 0;

    {
        std::unique_ptr<FILE, void(*)(FILE*, int*)> pipe(popen(cmd.c_str(), "r"), [](FILE* file, int* ret_ptr){
                              if(NULL==file)
                              {
                                  *ret_ptr = -1;
                              } 
                              else 
                              {
                                  *ret_ptr = pclose(file);
                              }
                         });
    }

    return ret;
}

int main()
{

}

Whereas this code snippet below compiles.

#include<memory>
#include<iostream>
#include<string>
#include<cstdio>

int ExecCmd(const std::string& cmd, std::string& output)
{
    int ret = 0;

    {
        std::unique_ptr<FILE, void(*)(FILE*)> pipe(popen(cmd.c_str(), "r"), [](FILE* file){
                              if(NULL==file)
                              {
                                 
                              } 
                              else 
                              {
                                    pclose(file);
                              }
                         });
    }

    return ret;
}

int main()
{

}

Here is what the compiler complains about the former code snippet:

In file included from /opt/compiler-explorer/gcc-11.3.0/include/c  /11.3.0/memory:76,
                 from <source>:1:
/opt/compiler-explorer/gcc-11.3.0/include/c  /11.3.0/bits/unique_ptr.h: In instantiation of 'std::unique_ptr<_Tp, _Dp>::~unique_ptr() [with _Tp = _IO_FILE; _Dp = void (*)(_IO_FILE*, int*)]':
<source>:20:27:   required from here
/opt/compiler-explorer/gcc-11.3.0/include/c  /11.3.0/bits/unique_ptr.h:357:63: error: static assertion failed: unique_ptr's deleter must be invocable with a pointer
  357 |         static_assert(__is_invocable<deleter_type&, pointer>::value,
      |                                                               ^~~~~
/opt/compiler-explorer/gcc-11.3.0/include/c  /11.3.0/bits/unique_ptr.h:357:63: note: 'std::integral_constant<bool, false>::value' evaluates to false
/opt/compiler-explorer/gcc-11.3.0/include/c  /11.3.0/bits/unique_ptr.h:361:24: error: too few arguments to function
  361 |           get_deleter()(std::move(__ptr));
      |           ~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~

I tried this code snippet to acquire the return value, but it does not compile:

#include<memory>
#include<iostream>
#include<string>
#include<cstdio>

int ExecCmd(const std::string& cmd, std::string& output)
{
    int ret = 0;

    {
        std::unique_ptr<FILE, void(*)(FILE*)> pipe(popen(cmd.c_str(), "r"), [&ret](FILE* file){
                              if(NULL==file)
                              {
                                  ret = -1;
                              } 
                              else 
                              {
                                  ret = pclose(file);
                              }
                         });
    }

    return ret;
}

int main()
{

}

Could anybody shed some light on this matter?

CodePudding user response:

Since you can only provide a custom deleter which can be callable with exactly one pointer as argument, you can not have the lambda with two arguments in it as deleter.

Your lambda with capture will also not work, as it can not be converted to a pointer to a function (i.e. only stateless lambdas can be converted to free function pointers type)


How can I retrieve the return value of pclose when the std::unique_ptr is destroyed?

Using lambda

int ExecCmd(const std::string& cmd, std::string& output)
{
    int ret = 0;
    const auto deleter = [&ret](FILE* file) { ret = file ? pclose(file) : 0; };
    {
        std::unique_ptr<FILE, decltype(deleter)> pipe(popen(cmd.c_str(), "r"), deleter);
    }
    return ret;
}

See a demo

CodePudding user response:

Thanks to @Jelo, @Some programmer dude, @user17732522.

For the readers of this post, this code snippet works, too.

#include<memory>
#include<iostream>
#include<string>
#include<stdio.h>
#include<functional>

using std::placeholders::_1;

void ClosePipe(FILE* file, int& ret)
{
    if(NULL==file)
    {
          ret = -1;
    } 
    else 
    {
          ret = pclose(file);
    }

    return;
}

int ExecCmd(const std::string& cmd, std::string& output)
{
    int ret = 0;

    auto delete_binded = std::bind(ClosePipe, _1, std::ref(ret));

    {
        std::unique_ptr<FILE, decltype(delete_binded) > pipe{popen(cmd.c_str(), "r"),  delete_binded};
    }

    return ret;
}

int main()
{

}

CodePudding user response:

As you're not using the output, it can be removed. You'll need to pass ret by reference to the deleter so that it can be modified by the result. Ternary operators can help to reduce if blocks as fp will evaluate to false if it is a null pointer. You can acquire the type of deleter with decltype so that it can be easily passed to the template of std::unique_ptr.

int execCmd(const std::string& cmd) {
    int ret{};
    auto deleter = [&ret](FILE* fp) { ret = fp ? pclose(fp) : 0; };
    {
        std::unique_ptr<FILE, decltype(deleter)> pipe{popen(cmd.c_str(), "r"),
                                                  deleter};
    }
    return ret;
}
  • Related