当 unique_ptr 被销毁时,如何检索 pclose 的 return 值?

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

正如@user17732522 指出的那样,std::unique_ptr 的删除器应该可以用 恰好一个指针 作为参数来调用。

当 unique_ptr 被销毁时,如何取回 pclose 的 return 值?

这个code snippet不编译,

#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()
{

}

而下面的 code snippet 编译。

#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()
{

}

这是编译器对前一个代码片段的抱怨:

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));
      |           ~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~

我试过这个 code snippet 来获取 return 值,但它没有编译:

#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()
{

}

有人能解释一下这件事吗?

因为您只能提供一个 custom deleter which can be callable with exactly one pointer 作为参数,所以您不能将带有两个参数的 lambda 作为删除器。

你的带捕获的 lambda 也将不起作用,因为 (即只有无状态的 lambda 可以转换为自由函数指针类型)


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

使用 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

感谢@Jelo,@Some 程序员,@user17732522。

对于这篇 post 的读者来说,this code snippet 也很有效。

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

using std::placeholders::_1;

void ClosePipe(FILE* file, int& ret)
{
    ret = pclose(file);

    return;
}

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

    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()
{

}

由于您不使用 output,可以将其删除。您需要通过引用传递 ret 到删除器,以便它可以被结果修改。三元运算符可以帮助减少 if 块,因为如果 fp 是空指针,则计算结果为 false。你可以用decltype获取deleter的类型,这样它就可以很容易地传递给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;
}