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