In theory, I should be able to use a custom pointer type and deleter in order to have unique_ptr
manage an object that is not a pointer. I tried the following code:
#ifndef UNIQUE_FD_H
#define UNIQUE_FD_H
#include <memory>
#include <unistd.h>
struct unique_fd_deleter {
typedef int pointer; // Internal type is a pointer
void operator()( int fd )
{
close(fd);
}
};
typedef std::unique_ptr<int, unique_fd_deleter> unique_fd;
#endif // UNIQUE_FD_H
This doesn't work (gcc 4.7 with the -std=c++11
parameter). It responds with the following errors:
In file included from /usr/include/c++/4.7/memory:86:0,
from test.cc:6:
/usr/include/c++/4.7/bits/unique_ptr.h: In instantiation of 'std::unique_ptr<_Tp, _Dp>::~unique_ptr() [with _Tp = int; _Dp = unique_fd_deleter]':
test.cc:22:55: required from here
/usr/include/c++/4.7/bits/unique_ptr.h:172:2: error: invalid operands of types 'int' and 'std::nullptr_t' to binary 'operator!='
From delving into the definition of unique_ptr
, I can see two problems that prevent it from working. The first, which seems in clear violation of the standard, is that the destructor for unique_ptr
compares the "pointer" (which is, as per my definition, an int) to nullptr
in order to see whether it is initialized or not. This is in contrast to the way it reports it through the boolean conversion, which is to compare it to "pointer()"
(an uninitialized "pointer"). This is the cause of the errors I am seeing - an integer is not comparable to a nullptr
.
The second problem is that I need some way to tell unique_ptr
what an uninitialized value is. I want the following snippet to work:
unique_fd fd( open(something...) );
if( !fd )
throw errno_exception("Open failed");
For that to work, unique_ptr
needs to know that an "uninitialized value" is -1, as zero is a valid file descriptor.
Is this a bug in gcc
, or am I trying to do something here that simply cannot be done?
Found an answer at cppreference.com. Look in the examples code:
void close_file(std::FILE* fp) { std::fclose(fp); }
...
{
std::unique_ptr<std::FILE, decltype(&close_file)> fp(std::fopen("demo.txt",
"r"),
&close_file);
if(fp) // fopen could have failed; in which case fp holds a null pointer
std::cout << (char)std::fgetc(fp.get()) << '\n';
}// fclose() called here, but only if FILE* is not a null pointer
// (that is, if fopen succeeded)
Tried it in vs2019 and it works! Also tried it with member and lambda:
FileTest.h:
class A
{
std::unique_ptr<std::FILE, std::function<void(std::FILE*)>> fp;
}
FileTest.cpp
void A::OpenFile(const char* fname)
{
fp = std::unique_ptr < std::FILE, std::function<void(std::FILE*)>>(
std::fopen(fname, "wb"),
[](std::FILE * fp) { std::fclose(fp); });
}
The open source Android Framework defines a unique_fd class that might meet your needs: https://android.googlesource.com/platform/system/core/+/c0e6e40/base/include/android-base/unique_fd.h
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With