For educational sake I am trying to understand a bit how smart pointers works with custom deleters. So I've written this code on my own that simulates how std::uniqe_ptr works. In fact I also think about changing the deleters template functions into template function classes and specialize one for dynamic arrays. Anyway this is what I've tried:
template <typename T>
void Free(T* p)
{
std::cout << "Free(T*): Freeing memory of a dynamic object...\n";
delete p;
}
template <typename T>
void Del_It(T* p)
{
std::cout << "Del_It(T*): freeing memory of a dynamic object...\n";
delete p;
}
template <typename T, typename Deleter=void(*)(T*)>
class Uniptr
{
public:
Uniptr(T* = nullptr, Deleter = Free<T>);
virtual ~Uniptr();
private:
T* ptr_{nullptr};
Deleter delPtr_{nullptr};
};
template <typename T, typename Deleter>
Uniptr<T, Deleter>::Uniptr(T* p, Deleter d) :
ptr_(p),
delPtr_(d)
{
}
template <typename T, typename Deleter>
Uniptr<T, Deleter>::~Uniptr()
{
delPtr_(ptr_);
delPtr_ = nullptr;
}
int main()
{
Uniptr<int, void(*)(int*)> upi(new int(9), Del_It<int>);
std::cout << "\ndone\n";
}
It looks that it works fine but I need your advice, tips... Thank you
To be honest custom deleter can be everything. Let's make a dirty our hand,
int main()
{
auto f = [](FILE* f){std::fclose(f); std::cout<<"I am closing file\n";};
std::unique_ptr<FILE,decltype(f)>fptr{std::fopen("test.txt","w"), f};
}
What we have done here? We have wrapped the file handling in C via unique_ptr.
What we get it? In case of forget to close the file after the operation, we ensure that it will be closed automatically.
So that, do not think that custom deleter can only use to delete pointer. I can be specified for any application such as a socket, file, etc...
Please check that unique_ptr
Code was taken from File Wrapper
A custom deleter can used to perform some custom clean up operation when the unique pointer is destroyed. In your case you're not doing anything apart from deleting the pointer. Therefore you're not performing any custom clean up. Plus you've got two functions Free and Del_It that do the same thing. If Del_It is meant for dynamically-allocated arrays, it should do delete [] p.
Suppose you're working with legacy code that specifically requires you to call a release() function that frees resources:
class LegacyClass
{
...
void release() {...}
~LegacyClass() {...}
};
You can't change the legacy code (it might even be in shared library), but you have to call the release() function. So you could define a custom deleter to do that:
struct Deleter
{
void operator()(LegacyClass *ptr) const
{
if (ptr)
{
ptr->release();
delete ptr;
}
}
};
With a unique pointer the deleter is actually part of the type:
unique_ptr<T, Deleter>
but for a shared pointer the custom deleter is not part of the type; it's passed into the constructor. The custom deleter can be defined as:
functor
function pointer
lambda
std::function *
*(this one can bloat the size of your unique pointer)
Related
I am writing a C++ wrapper around a C library. Here is an example of my strategy.
// header file
class LibrdfUri { // wrapper around librdf.h librdf_uri*
/*
* If the deleter of std::unique_ptr is an empty
* class then it can do some optimizations and
* not actually store the deleter object.
* Otherwise it has to accommodate extra space for
* the deleter, which is unnecessary
* https://stackoverflow.com/questions/61969200/what-is-the-purpose-of-wrapping-this-private-deleter-function-in-a-struct/61969274#61969274
*/
struct deleter {
// turns deleter into a functor. For passing on to unique_ptr
void operator()(librdf_uri *ptr);
};
// automate management of librdf_uri* lifetime
std::unique_ptr<librdf_uri, deleter> librdf_uri_;
public:
LibrdfUri() = default;
explicit LibrdfUri(const std::string& uri); // construct from string
librdf_uri *get(); // returns the underlying raw pointer
};
// implementation
void LibrdfUri::deleter::operator()(librdf_uri *ptr) {
librdf_free_uri(ptr); // this is the C library function for destruction of librdf_uri
}
LibrdfUri::LibrdfUri(const std::string &uri) {
// create pointer to underlying C library 'object'
librdf_uri_ = std::unique_ptr<librdf_uri, deleter>(
librdf_new_uri(World::getWorld(), (const unsigned char *) uri.c_str()) // World::getWorld is static. Returns a pointer required by librdf_new_uri
);
}
librdf_uri *LibrdfUri::get() {
return librdf_uri_.get();
}
// and is used like so:
LibrdfUri uri("http://uri.com");
librdf_uri* curi = uri.get(); // when needed
This works for the single type librdf_uri* which is a part of the underlying library however I have lots of these. My question is double barrelled. The first part concerns the best general strategy for generalizing this wrapper to other classes while the second is concerns the implementation of that strategy.
Regarding the first part, here are my thoughts:
1. I could implement each class manually like I've done here. This is probably the simplest and least elegant. Yet it still might be my best option. However there is a small amount of code duplication involved, since each CWrapper I write will essentially have the same structure. Not to mention if I need to change something then I'll have to do each class individually.
2. Use an base class (abstract?)
3. Use a template
The second part of my question is basically: if I implement either option 2 or 3 (which I think might even be just a single option) how would I do it?
Here is a (vastly broken) version of what I'm thinking:
template<class LibrdfType>
class CWrapper {
struct deleter { ; //?
void operator()(LibrdfType *ptr) {
// ??
};
}
std::unique_ptr<LibrdfType, deleter> ptr;
public:
CWrapper() = default;
LibrdfType *get() {
ptr.get();
};
};
Then, LibrdfUri and any other C class I need to wrap, would just subclass CWrapper
This is a better deleter:
template<auto f>
using deleter=std::integral_constant< std::decay_t<decltype(f)>, f >;
use:
deleter<librdf_free_uri>
is a stateless deleter that calls librdf_free_uri.
But we don't need that I think. Here is what I might do:
There are 3 pieces of information you need.
How to construct
How to destroy
What type to store
One way is to define ADL baser helpers with famous names that you override to delete/construct.
template<class T>struct tag_t{};
template<class T>constexpr tag_t<T> tag{};
template<class T>
void delete_wrapptr(T*)=delete;
struct cleanup_wrapptr{
template<class T>
void operator()(T* t)const{ delete_wrapptr(t); }
};
template<class T>
using wrapptr=std::unique_ptr<T, cleanup_wrapptr>;
template<class T>
wrapptr<T> make_wrapptr( tag_t<T>, ... )=delete;
now you just have to write overloads for make and delete.
void delete_wrapptr(librdf_uri* ptr){
librdf_free_uri(ptr); // this is the C library function for destruction of librdf_uri
}
librdr_uri* make_wrapptr(tag_t<librdf_uri>, const std::string &uri) {
return librdf_new_uri(World::getWorld(), (const unsigned char *) uri.c_str()); // World::getWorld is static. Returns a pointer required by librdf_new_uri
}
and you can;
wrapptr<librdf_uri> ptr = make_wrapptr(tag<librdf_uri>, uri);
the implementation becomes just overriding those two functions.
make_wrapptr and delete_wrapptr overloads you write need to be visible at creating point, and in the namespace of T, tag_t or cleanup_wrapptr. The implementations can be hidden in a cpp file, but the declaration of the overload cannot.
In normal C++ design, most objects can be deleted either by a delete statement, the free function, or a library-specific equivalent to free. For such objects, the unique_ptr Deleter implementation can be a stateless object that is eliminated through Empty Base Class Optimization. However, some libraries require using another object (which might contain a function pointer or some other context) to delete objects from that library.
typedef struct lib_object lib_object;
struct lib_api {
lib_object (*createInstance)();
void (*freeInstance)(lib_object *o);
};
One could wrap this in unique_ptr by storing a lib_api pointer as a data member in a custom Deleter, but if multiple lib_object instances needed to be managed, e.g. in a container, it would double the memory overhead of tracking the objects. What kind of pattern can be used to maintain RAII principles when dealing with this library, while still remaining memory efficient?
If there is only ever one lib_api object, then you can have your deleter get a static pointer to it.
If there can be more than one lib_api object then you have no choice but to store a pointer to it in the Deleter.
I use a custom deleter template for such objects.
template<typename T, T Function>
struct function_deleter
{
template<typename U>
auto operator()(U&& u) const noexcept(noexcept(Function(std::forward<U>(u))))
{
return Function(std::forward<U>(u));
}
};
then you can have use your deleter call free:
unique_ptr<int, function_deleter<void(*)(void*), &free>> uniq;
And its size is still equal to one pointer. live demo
Come C++17 you'll be able to use auto for non-type template parameters, simplifying the code to:
template<auto Function>
struct function_deleter
{
template<typename U>
auto operator()(U&& u) const noexcept(noexcept(Function(std::forward<U>(u))))
{
return Function(std::forward<U>(u));
}
};
and
unique_ptr<int, function_deleter<&call_free>> uniq;
live demo
Having this, in your case I'd keep a unique_ptr<pair<lib_object, lib_api>> with static deleter supporting this structure.
using lib_pair = pair<lib_object, lib_api>;
void lib_free(lib_pair* p){
p->second.freeInstance(p->first);
delete p;
}
using unique_lib_ptr = unique_ptr<lib_pair, function_deleter<void(*)(lib_pair*), &lib_free>>
This seems more cache-heavy but might just be your thing.
There should exist a more elegant solution but I would write something like
template <class ContainerType>
class TObjectContainer : public ContainerType {
public:
TObjectContainer() = default;
TObjectContainer(const TObjectContainer&); // should call createCopy
TObjectContainer(TObjectContainer&&) = default;
~TObjectContainer()
{ for (lib_object* element : *this)
(*freeInstance)(element);
}
private:
void (*freeInstance)(lib_object *o);
};
typedef TObjectContainer<std::vector<lib_object*>> ObjectVector;
It does not use unique_ptr but it should basically do the job.
Note that your are likely to overload every removal method like clear to call freeInstance or pop_back to return your original std::unique_ptr.
I am trying to implement a memory management system to deal with pointers being stored in maps.
struct refmanager{ //since this class is only for inheritance
//and not for polymorphism their does not need to be a virtual destructor
int count;
refmanager():count(0){}
};
The first idea I had was to inherit the above struct into classes I am going to insert into maps as pointers.
template <class P> void ref(P ptr)
{
ptr->count+=1;
cout<<"increasing ref count\n";
}
template <class P> void deref(P ptr)
{
ptr->count-=1;
cout<<"decreasing ref count\n";
if (ptr->count==0)
delete ptr;
}
Than I was going to use the above template functions to increase and decrease the reference count. To make the system automatic I was going to use the template functions below as replacements for the normal map methods (note this is not complete and the clear map method was written for my test case and is not generic).
template <class M, class K, class V> void mapinsert(M &map, K key, V value)
{
ref(value);
map.insert(pair<K, V>(key, value));
}
template <class T> void clearmap(T input)
{
deref(input[1]);
input.clear();
}
From preliminary testing this idea works. But I don't have the knowledge to know if this will lead to possible disaster later. Can someone with more experience please let me know if this memory management concept is any good and if not when, where and why will it fail?
I only look at the space release of reference object (delete ptr), but where is the allocation?
You must ensure every reference objects are allocated in heap, not in stack.
value in ref(value); is a pointer? Because value is the template defined type, it may be NOT a pointer.
Apparently I cant put code in comments. Anyway this was in response to Charlies answer. By the way he is right and the method above is doable but is not the safest method. Below is a safer method.
you can produce a refmanger class with this code:
class refmanager{
private:
int count;
public:
refmanager():count(0){}
virtual ~refmanager() {}
void ref(refmanager* ptr)
{
ptr->count+=1;
}
void deref(refmanager* ptr)
{
ptr->count-=1;
if (ptr->count==0)
delete ptr;
}
};
The refmanager class can be inherited by classes which need to have their pointers reference managed.
The code below will now only compile when V and T are objects which contain the ref and deref methods with the correct format. In other words you are ensuring the code will not crash unexpectedly in the middle of running it.
template <class M, class K, class V> void mapinsert(M &map, K key, V value)
{
value->ref(value);
map.insert(pair<K, V>(key, value));
}
template <class T> void clearmap(T input)
{
input[1]->deref(input[1]);
input.clear();
}
I am creating a class which interops with some Windows API code, now one of the pointers I have to initialize is done by calling a native function which initializes it.
My pointers are of type std::unique_ptr with a custom deleter, which calls the WinAPI deleter function provided, however I cannot pass the unique_ptr with the & address-of operator to the init-function. Why?
I have created a sample that demonstrates my problem:
#include <memory>
struct foo
{
int x;
};
struct custom_deleter {};
void init_foo(foo** init)
{
*init = new foo();
}
int main()
{
std::unique_ptr<foo, custom_deleter> foo_ptr;
init_foo(&foo_ptr);
}
The compiler barks and says:
source.cpp: In function 'int main()':
source.cpp:19:21: error: cannot convert 'std::unique_ptr<foo, custom_deleter>*' to 'foo**' for argument '1' to 'void init_foo(foo**)'
Somewhere under the covers, unique_ptr<foo> has a data member of type foo*.
However, it's not legitimate for a user of the class to directly modify that data member. Doing so would not necessarily preserve the class invariants of unique_ptr, in particular it wouldn't free the old pointer value (if any). In your special case you don't need that to happen, because the previous value is 0, but in general it should happen.
For that reason unique_ptr doesn't provide access to the data member, only to a copy of its value (via get() and operator->). You can't get a foo** out of your unique_ptr.
You could instead write:
foo *tmp;
init_foo(&tmp);
std::unique_ptr<foo, custom_deleter> foo_ptr(tmp);
This is exception-safe for the same reason that std::unique_ptr<foo, custom_deleter> foo_ptr(new foo()); is exception-safe: unique_ptr guarantees that whatever you pass in to its constructor will eventually get deleted using the deleter.
Btw, doesn't custom_deleter need an operator()(foo*)? Or have I missed something?
Steve has already explained what the technical problem is, however, the underlying problem goes much deeper: The code employs an idiom helpful when you deal with naked pointers. Why does this code do two-step initialization (first create the object, then initialize it) in the first place? Since you want to use smart pointers, I'd suggest you carefully adapt the code:
foo* init_foo()
{
return new foo();
}
int main()
{
std::unique_ptr<foo, custom_deleter> foo_ptr( init_foo() );
}
Of course, renaming init_foo() to create_foo() and having it return a std::unique_ptr<foo> directly would be better. Also, when you use two-step initialization, it's often advisable to consider using a class to wrap the data.
You can use the following trick:
template<class T>
class ptr_setter
{
public:
ptr_setter(T& Ptr): m_Ptr{Ptr} {}
~ptr_setter() { m_Ptr.reset(m_RawPtr); }
ptr_setter(const ptr_setter&) = delete;
ptr_setter& operator=(const ptr_setter&) = delete;
auto operator&() { return &m_RawPtr; }
private:
T& m_Ptr;
typename T::pointer m_RawPtr{};
};
// Macro will not be needed with C++17 class template deduction.
// If you dislike macros (as all normal people should)
// it's possible to replace it with a helper function,
// although this would make the code a little more complex.
#define ptr_setter(ptr) ptr_setter<decltype(ptr)>(ptr)
and then:
std::unique_ptr<foo, custom_deleter> foo_ptr;
init_foo(&ptr_setter(foo_ptr));
I eventually came up with an approach that allows to initialise unique_ptr's with a code like this:
struct TOpenSSLDeleter { ... }; // Your custom deleter
std::unique_ptr<EVP_MD_CTX, TOpenSSLDeleter> Ctx;
...
Ctx = MakeUnique(EVP_MD_CTX_create()); // MakeUnique() accepts raw pointer
And here is the solution:
template <class X>
struct TUniquePtrInitHelper {
TUniquePtrInitHelper(X *Raw) noexcept {
m_Raw = Raw;
}
template <class T, class D>
operator std::unique_ptr<T, D>() const noexcept {
return std::unique_ptr<T, D>(m_Raw);
}
private:
X *m_Raw;
};
template <class X>
TUniquePtrInitHelper<X> MakeUnique(X *Raw) noexcept {
return {Raw};
}
I have an auto pointer class and in the constructor I am passing in a pointer. I want to be able to separate new from new[] in the constructor so that I can properly call delete or delete[] in the destructor. Can this be done through template specialization? I don't want to have to pass in a boolean in the constructor.
template <typename T>
class MyAutoPtr
{
public:
MyAutoPtr(T* aPtr);
};
// in use:
MyAutoPtr<int> ptr(new int);
MyAutoPtr<int> ptr2(new int[10]);
Unfortunately, no. Both return the same type, T*. Consider using builder functions that call an appropriate overloaded constructor:
template <typename T>
class MyAutoPtr
{
public:
MyAutoPtr(T* aPtr, bool array = false);
};
template <typename T>
MyAutoPtr<T> make_ptr() {
return MyAutoPtr<T>(new T(), false);
}
template <typename T>
MyAutoPtr<T> make_ptr(size_t size) {
return MyAutoPtr<T>(new T[size], true);
}
Now you can instantiate objects as follows:
MyAutoPtr<int> ptr = make_ptr<int>();
MyAutoPtr<int> ptr2 = make_ptr<int>(10);
std::unique_ptr in C++0x will have a specialization for dynamic arrays, somewhat like shown below. However, it will be the user's task to instantiate an appropriate instance. At language level there is no way to distinguish one pointer from another.
template <class T>
class pointer
{
T* p;
public:
pointer(T* ptr = 0): p(ptr) {}
~pointer() { delete p; }
//... rest of pointer interface
};
template <class T>
class pointer<T[]>
{
T* p;
public:
pointer(T* ptr = 0): p(ptr) {}
~pointer() { delete [] p; }
//... rest of pointer and array interface
};
int main()
{
pointer<int> single(new int);
pointer<int[]> array(new int[10]);
}
Furthermore, it might not be that good to load one class with so various tasks. For example, boost has shared_ptr and shared_array.
On the other hand, you could use a specific make function.
template <class T>
MyAutoPtr<T> make();
template <class T>
MyAutoPtr<T> make(size_t n);
Of course, this means that you have the appropriate logic behind, but it's encapsulated. You can also add overload taking a T to copy the object passed into the pointer newly created etc...
Finally, it can also be done with overloads of the constructor... the point is not to call the new outside.
I think the real solution is to get rid of your own autopointer class and to get rid of the use of C-style arrays. I know this has been said many, many times before, but there really isn't much point in using C-style arrays any more. Just about everything you can do with them can be done using std::vector or with boost::array. And both of these create distinct types, so you can overload on them.
It is not possible since new int[X] yields a pointer to the initial element of the array. It has the same type as int*.
One of the common solutions is to use deleters. Add one more template argument to your class so you could pass custom deleter for your pointer. It'll make your class more universal. You could create default deleter like the following:
struct default_deleter
{
template<typename T>
void operator()( T* aPtr ) { delete aPtr; }
};
And for arrays you could pass custom deleter:
struct array_deleter
{
template<typename T>
void operator()( T* aPtr ) { delete[] aPtr; }
};
The simplest implementation will be:
template <typename T, typename D>
class MyAutoPtr
{
public:
MyAutoPtr(T* aPtr, D deleter = default_deleter() ) : ptr_(aPtr), deleter_(deleter) {};
~MyAutoPtr() { deleter_(ptr_); }
protected:
D deleter_;
T* ptr_;
};
Then you could use it as follows:
MyAutoPtr<int, array_deleter> ptr2(new int[10], array_deleter() );
You could make your class more complex so it could deduce type for deleter.
new[] is specifically defined to have pointer value despite the array-to-pointer implicit conversion that would kick in anyway.
But I don't think you're out of luck. After all, your example isn't managing a pointer to an int, it's managing a pointer to an int[10]. So the ideal way is
MyAutoPtr<int[10]> ptr2(new int[10]);
As Red-Nosed Unicorn mentions, new int[10] does not create a C-style array. It will if your compiler complies to the C standard as well, but C++ allows C-style arrays to be more than C-style arrays in C. Anyway, new will create you a C-style array if you ask like this:
MyAutoPtr<int[10]> ptr2(new int [1] [10]);
Unfortunately, delete contents; will not work even with int (*contents)[10];. The compiler is allowed to do the right thing: the standard doesn't specify that the array is converted to a pointer as with new, and I believe I recall GCC substituting delete[] and emitting a warning. But it's undefined behavior.
So, you will need two destructors, one to call delete and one to call delete[]. Since you can't partially specialize a function, the functionality demands a partially specialized helper
template< class T > struct smartptr_dtor {
void operator()( T *ptr ) { delete ptr; }
};
template< class T, size_t N > struct smartptr_dtor< T[N] > {
void operator()( T (*ptr) [N] ) { delete [] ptr; }
};
template< class T >
void proper_delete( T *p ) {
smartptr_dtor< T >()( p );
}
which for some reason I just subjected myself to ;v)
Unfortunately, this doesn't work with dynamic-sized arrays, so I'm going to write up another answer.
Second attempt…
It's quite easy to make a smart pointer class smart about arrays. As you suspected, you don't need a runtime flag or argument to the constructor if you know it's an array to begin with. The only problem is that new and new[] have identical return types, so they cannot pass this information to the smart pointer class.
template< class T, bool is_array = false >
struct smartptr {
T *storage;
smartptr( T *in_st ) : storage( in_st ) {}
~smartptr() {
if ( is_array ) delete [] storage; // one of these
else delete storage; // is dead code, optimized out
}
};
smartptr< int > sp( new int );
smartptr< int, true > sp2( new int[5] );
An alternative to the bool flag is to overload the meaning of T[] as Visitor mentions std::unique_ptr does in C++0x.
template< class T >
struct smartptr {
T *storage;
smartptr( T *in_st ) : storage( in_st ) {}
~smartptr() { delete storage; }
};
template< class T > // partial specialization
struct smartptr< T [] > {
T *storage; // "T[]" has nothing to do with storage or anything else
smartptr( T *in_st ) : storage( in_st ) {}
~smartptr() { delete [] storage; }
};
smartptr< int > sp( new int );
smartptr< int[] > sp2( new int[5] );