I have heard people say that "C++ doesn't need placement delete because it wouldn't do anything."
Consider the following code:
#include <cstdlib>
#include <cstdio>
#include <new>
////////////////////////////////////////////////////////////////
template<typename T, typename... ARGS>
T* customNew1(ARGS&&... args) {
printf("customNew1...\n");
auto ret = new T { std::forward<ARGS>(args)... };
printf("OK\n\n");
return ret;
}
template<typename T>
void customDelete1(T *ptr) {
printf("customDelete1...\n");
delete ptr;
printf("OK\n\n");
}
////////////////////////////////
template<typename T, typename... ARGS>
T* customNew2(ARGS&&... args) {
printf("customNew2 alloc...\n");
void *buf = std::malloc(sizeof(T));
printf("customNew2 construct...\n");
auto ret = ::new(buf) T { std::forward<ARGS>(args)... };
printf("OK\n\n");
return ret;
}
template<typename T>
void customDelete2(T *ptr) {
printf("customDelete2 destruct...\n");
// what I want: a "placement delete" which calls the destructor and returns the address that should be passed to the deallocation function
// e.g.
//
// void* ptrToFree = ::delete(ptr);
// std::free(ptrToFree);
//
// equally fine would be a "magic" operator that allows one to obtain said address without actually calling the destructor:
//
// void* ptrToFree = get_deallocation_address_of(ptr);
// ptr->~T();
// std::free(ptrToFree);
ptr->~T();
printf("customDelete2 free...\n");
std::free(ptr);
printf("OK\n\n");
}
////////////////////////////////////////////////////////////////
struct A {
int a;
A() : a(0) {
printf("A()\n");
}
virtual ~A() {
printf("~A()\n");
}
};
struct B {
int b;
B() : b(0) {
printf("B()\n");
}
virtual ~B() {
printf("~B()\n");
}
};
struct C : A, B {
int c;
C() : c(0) {
printf("C()\n");
}
~C() {
printf("~C()\n");
}
};
////////////////////////////////////////////////////////////////
int main() {
C *c1 = customNew1<C>();
A *a1 = c1;
B *b1 = c1;
// Assume c and a will be the same but b is offset
printf("c: %x\n", c1);
printf("a: %x\n", a1);
printf("b: %x\n", b1);
printf("\n");
customDelete1(b1); // <- this will work, the delete expression offsets b1 before deallocing
printf("--------------\n\n");
C *c2 = customNew2<C>();
A *a2 = c2;
B *b2 = c2;
printf("c: %x\n", c2);
printf("a: %x\n", a2);
printf("b: %x\n", b2);
printf("\n");
// customDelete2(b2); // <- this will break
customDelete2(a2); // <- this will work because a2 happens to point at the same address as c2
printf("--------------\n\n");
return 0;
}
As you can see here the destructors, being virtual, are all called properly, but the deallocation of b2 will still fail because b2 points at a different address than c2.
Note that a similar problem arises when one uses placement new[] to construct an array of objects, as described here:
Global "placement" delete[]
However this can be worked around without much trouble by simply saving the array size at the head of your block of memory and handling the array constructor/destructor calls manually in a loop using single object placement new/explicit destructor calls.
On the other hand, I cannot think of any graceful way to solve the problem with multiple inheritance. The "magic" code which retrieves the original pointer from the base pointer within the delete expression is implementation specific, and there's no simple way of "doing it manually" like you can with arrays.
Here is another situation where this becomes a problem, with an ugly hack to work around it:
#include <cstdlib>
#include <cstdio>
#include <new>
////////////////////////////////////////////////////////////////
// imagine this is a library in which all allocations/deallocations must be handled by this base interface
class Alloc {
public:
virtual void* alloc(std::size_t sz) =0;
virtual void free(void *ptr) =0;
};
// here is version which uses the normal allocation functions
class NormalAlloc : public Alloc {
public:
void* alloc(std::size_t sz) override final {
return std::malloc(sz);
}
void free(void *ptr) override final {
std::free(ptr);
}
};
// imagine we have a bunch of other versions like this that use different allocation schemes/memory heaps/etc.
class SuperEfficientAlloc : public Alloc {
void* alloc(std::size_t sz) override final {
// some routine for allocating super efficient memory...
(void)sz;
return nullptr;
}
void free(void *ptr) override final {
// some routine for freeing super efficient memory...
(void)ptr;
}
};
// etc...
////////////////////////////////
// in this library we will never call new or delete, instead we will always use the below functions
// this is used instead of new...
template<typename T, typename... ARGS>
T* customNew(Alloc &alloc, ARGS&&... args) {
printf("customNew alloc...\n");
void *buf = alloc.alloc(sizeof(T));
printf("customNew construct...\n");
auto ret = ::new(buf) T { std::forward<ARGS>(args)... };
printf("OK\n\n");
return ret;
}
// um...
thread_local Alloc *stupidHack = nullptr;
// unfortunately we also have to replace the global delete in order for this hack to work
void operator delete(void *ptr) {
if (stupidHack) {
// the ptr that gets passed here is pointing at the right spot thanks to the delete expression below
// alloc has been stored in "stupidHack" since it can't be passed as an argument...
printf("customDelete free # %x...\n", ptr);
stupidHack->free(ptr);
stupidHack = nullptr;
} else {
// well fug :-D
}
}
// ...and this is used instead of delete
template<typename T>
void customDelete(Alloc &alloc, T *ptr) {
printf("customDelete destruct # %x...\n", ptr);
// set this here so we can use it in operator delete above
stupidHack = &alloc;
// this calls the destructor and offsets the pointer to the right spot to be dealloc'd
delete ptr;
printf("OK\n\n");
}
////////////////////////////////////////////////////////////////
struct A {
int a;
A() : a(0) {
printf("A()\n");
}
virtual ~A() {
printf("~A()\n");
}
};
struct B {
int b;
B() : b(0) {
printf("B()\n");
}
virtual ~B() {
printf("~B()\n");
}
};
struct C : A, B {
int c;
C() : c(0) {
printf("C()\n");
}
~C() {
printf("~C()\n");
}
};
////////////////////////////////////////////////////////////////
int main() {
NormalAlloc alloc;
C *c = customNew<C>(alloc);
A *a = c;
B *b = c;
printf("c: %x\n", c);
printf("a: %x\n", a);
printf("b: %x\n", b);
printf("\n");
// now it works
customDelete(alloc, b);
printf("--------------\n\n");
return 0;
}
This isn't a question really more of just a rant as I'm fairly sure that no magic operator or platform independent method to obtain the address exists. At the company where I work we had a library that used custom allocators with the hack above which worked okay until we had to link it statically with another program that needed to replace global new/delete. Our current solution is simply to ban the deleting of an object through a pointer to a base that can't be shown to always have the same address as the most derived object, but this seems a bit unfortunate. "ptr->~T(); free(ptr);" seems to be a common enough pattern and many people seem to think it's equivalent to a delete expression, but it's not. I'm curious if anyone else has encountered this problem and how they managed to solve it.
If p points to an object of polymorphic class type, you can get the address of the most derived object using dynamic_cast<void*>(p). Thus your customDelete2 can be implemented as follows:
template <class T>
void customDelete2(const T *ptr) {
const void* ptr_to_free = dynamic_cast<const void*>(ptr);
ptr->~T();
std::free(const_cast<void*>(ptr_to_free));
}
(Yes, you can dynamically allocate const objects.)
Since this will only compile for a polymorphic class type, you might want to remove the dynamic_cast to a helper function:
template <class T>
const void* get_complete_object_address(const T* p, std::true_type) {
return dynamic_cast<const void*>(p);
}
template <class T>
const void* get_complete_object_address(const T* p, std::false_type) {
return p;
}
template <class T>
void customDelete2(const T *ptr) {
const void* ptr_to_free = get_complete_object_address(
ptr,
std::integral_constant<bool, std::is_polymorphic<T>::value>{}
);
ptr->~T();
free(const_cast<void*>(ptr_to_free));
}
Related
I'll try showing what I want with an example code:
template <typename T>
class myclass
{
public:
myclass(int x, int y);
// code
};
main()
{
myclass<cat>* ptr1 = NULL;
myclass<dog>* ptr2 = NULL;
if (/*condition*/)
{
ptr1 = new myclass<cat>(1,1);
delete ptr2;
current_ptr = ptr1; // how to declare
}
else
{
ptr2 = new myclass<dog>(1,2);
delete ptr1;
current_ptr = ptr2; // how to declare
}
current_ptr->some_method(); //work with it
}
I want use template class with different arguments. How do that?
Templates are a compile time construct. When the template is instantiated, it creates unrelated classes.
As such, myclass<cat> and myclass<dog> are totally different, unrelated classes with you cannot exchange pointers between them.
To mix them up at runtime, you need some form of runtime polymorphism. The simplest is when you know all types in advance, then you can use variants:
#include <variant>
int main() {
std::variant<myclass<cat>, myclass<dog>> obj;
if (/* runtime condition */) {
obj = myclass<cat>{};
} else {
obj = myclass<dog>{};
}
// work with it:
std::visit(
[](auto const& myclass_obj) {
// in here, myclass_obj is either a myclass<cat> or myclass<dog>
// So the code has to be valid for both, unless using if constexpr.
if constexpr (std::is_same_v<decltype(myclass_obj), myclass<cat> const&>) {
// cat specific code
} else {
// dog specific code
}
},
obj
);
}
If you want to continue with pointers, you need a base class:
class mybase {
public:
virtual ~mybase() = default;
virtual someMethod() = 0;
};
template<typename T>
class myclass : public mybase {
public:
myclass(int x, int y) {}
void someMethod() override {
}
};
Then declare your pointer:
mybase* ptr = nullptr;
The context: I wrote some tools for archiving data, in a similar way of archives from boost. Then, as example, I can write this kind of code :
class A
{
private:
double a;
public:
A() : a(3.14159)
{}
A(const A& a_) : a(a_.a) {}
virtual ~A()
{}
virtual A* clone() const = 0; // Then, A is virtual
virtual void save(O_Archive& oa) const //
{ //
oa << this->a; // INTERESTING
} // PART OF THE
virtual void load(I_archive& ia) // CLASS
{ //
ia >> this->a; //
} //
};
O_Archive& operator << (O_Archive& oa, const A& a)
{
a.save(oa);
return oa;
}
I_Archive& operator >> (I_Archive& ia, A& a)
{
a.load(ia);
return ia;
}
class B : public A
{
private:
double b;
public:
B() : A(), b(1.0) {}
B(const B& b_) : A(b_), b(b_.b) {}
virtual ~B() {}
virtual A* clone() const
{
return new B(*this);
}
void save(O_Archive& oa) const //
{ //
this->A::save(oa); //
oa << this->b; // INTERESTING
} // PART OF THE
void load(I_Archive& ia) // CLASS
{ //
this->A::load(ia); //
ia >> this->b; //
} //
};
// Consider classes 'C' and 'D' similar to 'B'
void example_save(O_Archive& oa)
{
A* p1 = new B;
A* p2 = new C;
D* p3 = new D;
oa << Archive::declare_derived<A,B,C,D>();
oa << p1 << p2; // Automatically detect the inheritance
oa << p3; // Store the instance as a usual pointer
}
void example_load(I_Archive& ia)
{
A* p1 = 0;
A* p2 = 0;
B* p3 = 0;
ia << Archive::declare_derived<A,B,C,D>();
ia >> p1 >> p2;
ia >> p3;
}
Where is the problem ? This works with several functions like the following load_pointer function in the class I_Archive in charge of checking if the pointer was allocated, if it was an instance with a derived type, or simply a usual pointer.
template <typename T>
void I_Archive::load_pointer(T*& p)
{
delete p;
bool allocated;
this->load_bool(allocated);
if(allocated)
{
bool inheriance;
this->load_bool(inheriance);
if(inheriance)
{
unsigned long int i;
this->load_unsigned_long_int(i);
p = boost::static_pointer_cast< const Archive::allocator<T> >(this->alloc[&typeid(T)][i])->allocate();
}
else
p = new T; // ERROR AT THIS LINE
*this >> *p;
}
else
p = 0;
}
My problem: Actually, my code doesn't compile with the following error on the line p = new T; :
error: cannot allocate an object of abstract type ‘A’.
I was first surprised, but I really well understand why I have this error : when the function load_pointer is called on p1, the instruction new T becomes new A which is forbidden, even if the instruction is never run if the type is abstract.
My question: I can't find a way to correctly use templates to avoid my error. Is there a possible workaround to do that or to say to the compiler "I know what I'm doing, you'll never have to instanciate an abstract type" ?
Important note: I can't work with C++11 for compatibility reason.
The trait you're looking for is std::is_abstract. As you mentioned, you cannot use C++11, but you can use its implementation from boost.
You can then use is_abstract together with std::enable_if (again, due to your restriction of not using C++11, you can just take the example implementation from here) to implement it similarly to this:
#include <iostream>
#include <type_traits>
struct A {
virtual void f() = 0;
};
struct B : A {
void f() override {}
};
template<typename T>
std::enable_if_t<std::is_abstract<T>::value, T*> allocate()
{
return nullptr;
}
template<typename T>
std::enable_if_t<!std::is_abstract<T>::value, T*> allocate()
{
return new T;
}
// Test
template<typename T>
T* test_alloc()
{
return allocate<T>();
}
int main()
{
std::cout << test_alloc<A>() << "\n"; // Outputs nullptr
std::cout << test_alloc<B>() << "\n"; // Outputs an address
}
LIVE
I'm trying to make something like this work:
struct holder {
std::function<void()> destroyer;
template<typename T>
holder(T) = delete;
template<typename T>
holder(std::enable_if< WAS CREATED WITH new > pointer) {
destroyer = [=] { delete pointer; };
};
template<typename T>
holder(std::enable_if< WAS CREATED WITH new[] > array) {
destroyer = [=] { delete[] array; };
};
virtual ~holder() {
destroyer();
};
};
In a way that I could then simply make return new test; and return = new test[10]; on a function that would return holder. But I found out that it won't ever be treated as an array, as operator new[] returns a pointer.
Is there any way to achieve the desired result?
Thanks! :)
It is impossible; whether or not new or new[] was used is not part of the pointer's type information.
The only way I know of is through placement-new:
#include <new>
#include <iostream>
struct A
{
void* operator new(std::size_t n, void* ptr)
{
std::cout << "operator new()\n";
return ptr;
}
void* operator new[](std::size_t n, void* ptr)
{
std::cout << "operator new[]\n";
return ptr;
}
};
int main()
{
A* ptr;
new (ptr) A();
new (ptr) A[5];
}
Consider the following piece of code:
int three() {
return 3;
}
template <typename T>
class Foo {
private:
T* ptr;
public:
void bar(T& t) { ptr = new T(t); }
void bar(const T& t) { ptr = new T(t); }
void bar(T&& t) { (*ptr) = t; } // <--- Unsafe!
};
int main() {
Foo<int> foo;
int a = 3;
const int b = 3;
foo.bar(a); // <--- Calls Foo::bar(T& t)
foo.bar(b); // <--- Calls Foo::bar(const T& t)
foo.bar(three()); // <--- Calls Foo::bar(T&& t); Runs fine, but only if either of the other two are called first!
return 0;
}
My question is, why does the third overload Foo::bar(T&& t) crash the program? What exactly is happening here? Does the parameter t get destroyed after the function returns?
Furthermore, let's assume that the template parameter T was a very large object with a very costly copy constructor. Is there any way to use RValue References to assign it to Foo::ptr without directly accessing this pointer and making a copy?
In this line
void bar(T&& t) { (*ptr) = t; } // <--- Unsafe!
you can dereference an uninitialized pointer. This is undefined behavior.
You must call one of the two other version of bar first because you need to create the memory for your object.
So I would do ptr = new T(std::move(t));.
If your type T supports moving the move constructor will get called.
Update
I would suggest something like that. Not sure if you need the pointer type within foo:
template <typename T>
class Foo {
private:
T obj;
public:
void bar(T& t) { obj = t; } // assignment
void bar(const T& t) { obj = t; } // assignment
void bar(T&& t) { obj = std::move(t); } // move assign
};
This would avoid memory leaks which are also quite easy with your approach.
If you really need the pointer in your class foo how about that:
template <typename T>
class Foo {
private:
T* ptr;
public:
Foo():ptr(nullptr){}
~Foo(){delete ptr;}
void bar(T& t) {
if(ptr)
(*ptr) = t;
else
ptr = new T(t);
}
void bar(const T& t) {
if(ptr)
(*ptr) = t;
else
ptr = new T(t);
}
void bar(T&& t) {
if(ptr)
(*ptr) = std::move(t);
else
ptr = new T(std::move(t));
}
};
There's no reason for that to fail in that code. ptr will point to an existing int object created by the previous calls to bar and the third overload will just assign the new value to that object.
However, if you did this instead:
int main() {
Foo<int> foo;
int a = 3;
const int b = 3;
foo.bar(three()); // <--- UB
return 0;
}
That foo.bar(three()); line would have undefined behaviour (which does not imply any exception), because ptr would not be a valid pointer to an int object.
Assuming that you only called foo.bar(three()); without the other two calls:
Why did you think that'd work? Your code is essentially equivalent to this:
int * p;
*p = 3;
That's undefined behaviour, because p isn't pointing to a valid variable of type int.
The "unsafe"thing, here is that, before assigning to ptr a new object, you should worry about the destiny of what ptr actually points to.
foo.bar(three());
is unsafe in the sense that you have to grant -before calling it- that ptr actually point to something. In your case it points to what was created by foo.bar(b);
But foobar(b) makes ptr to point to a new object forgetting the one created by foobar(a)
A more proper code can be
template<class T>
class Foo
{
T* p;
public:
Foo() :p() {}
~Foo() { delete p; }
void bar(T& t) { delete p; ptr = new T(t); }
void bar(const T& t) { delete p; ptr = new T(t); }
void bar(T&& t)
{
if(!ptr) ptr = new T(std::move(t));
else (*ptr) = std::move(t);
}
}
;
Is there a way to prevent shared_from_this() call for a stack-allocated object ?
The enable_shared_from_this<> in the base classes list is a strong indicator for class user, but is there a way to enforce the correct usage ?
Example code:
class C : public enable_shared_from_this<C>
{
public:
shared_ptr<C> method() { return shared_from_this(); }
};
void func()
{
C c;
shared_ptr<C> ptr = c.method(); // exception coming from shared_from_this()
}
So to protect against this problem you can make your constructors private and only provide creation functions that return shared_ptr - this way the object can't be allocated on the stack, like this:
class C : public enable_shared_from_this<C>
{
public:
static shared_ptr<C> create() { return shared_ptr<C>(new C() ); }
shared_ptr<C> method() { shared_from_this(); }
private:
C() {...}
// Make operator= and C(const C&) private unimplemented
// so the used cant do bad things like C c( * c_ptr );
C& operator=( const C & );
C( const C & );
};
void func()
{
C c; // This doesn't compile
shared_ptr<C> ptr = c.method(); // So you can never get this
}
void altfunc()
{
shared_ptr<C> c_ptr = C::create();
C & c_ref = *c;
shared_ptr<C> ptr = c_ref.method(); // OK
}
If you find yourself wishing for an operator= you can provide a clone function using a private implemented copy constructor, something like this
// This goes in class C
shared_ptr<C> C::clone() const
{
return shared_ptr<C>( new C(*this) );
}
// This is how you can use it
shared_ptr<C> c2 = c1->clone();
I found the solution.
In Dune library they use stack-compatible enable_shared_from_this class template adaptation.
Check the original source code here or check my example with the quick copy&paste & cleanup of code:
#include <cstdio>
#include <cassert>
#include <memory>
#include <iostream>
template<class T>
struct null_deleter
{
void operator() (T*) const {}
};
template<typename T>
inline std::shared_ptr<T> stackobject_to_shared_ptr(T & t)
{
return std::shared_ptr<T>(&t, null_deleter<T>());
}
template<typename T, typename T2>
inline std::shared_ptr<T2> stackobject_to_shared_ptr(T & t)
{
return std::shared_ptr<T2>(dynamic_cast<T2*>(&t), null_deleter<T2>());
}
template<typename T>
class stack_compatible_enable_shared_from_this
: public std::enable_shared_from_this<T>
{
public:
std::shared_ptr<T> shared_from_this()
{
try
{
return std::enable_shared_from_this<T>::shared_from_this();
}
catch (std::bad_weak_ptr&)
{
_local_ptr = stackobject_to_shared_ptr(*static_cast<T*>(this));
return _local_ptr;
}
}
std::shared_ptr<const T> shared_from_this() const
{
try
{
return std::enable_shared_from_this<T>::shared_from_this();
}
catch (std::bad_weak_ptr&)
{
_local_ptr = stackobject_to_shared_ptr(*const_cast<T*>(static_cast<const T*>(this)));
return _local_ptr;
}
}
private:
mutable std::shared_ptr<T> _local_ptr;
};
struct MyObj : public stack_compatible_enable_shared_from_this<MyObj>{};
int main (int argc, char **argv) {
//std::shared_ptr<MyObj> so = std::make_shared<MyObj>(6);
MyObj o{};
auto * so = &o;
{
auto l = std::weak_ptr<MyObj>(so->shared_from_this());
auto shared = l.lock();
if (shared) { } //use it
}
}