Make_shared - own implementation - c++

I am trying to make my own implementation of shared_ptr. I have problems with make_shared. The main feature of std::make_shared that it allocates counter block and object in continuous block of memory. How I can do the same?
I tried do something like that:
template<class T>
class shared_ptr
{
private:
class _ref_cntr
{
private:
long counter;
public:
_ref_cntr() :
counter(1)
{
}
void inc()
{
++counter;
}
void dec()
{
if (counter == 0)
{
throw std::logic_error("already zero");
}
--counter;
}
long use_count() const
{
return counter;
}
};
template<class _T>
struct _object_and_block
{
_T object;
_ref_cntr cntr_block;
template<class ... Args>
_object_and_block(Args && ...args) :
object(args...)
{
}
};
T* _obj_ptr;
_ref_cntr* _ref_counter;
void _check_delete_ptr()
{
if (_obj_ptr == nullptr)
{
return;
}
_ref_counter->dec();
if (_ref_counter->use_count() == 0)
{
_delete_ptr();
}
_obj_ptr = nullptr;
_ref_counter = nullptr;
}
void _delete_ptr()
{
delete _ref_counter;
delete _obj_ptr;
}
template<class _T, class ... Args>
friend shared_ptr<_T> make_shared(Args && ... args);
public:
shared_ptr() :
_obj_ptr(nullptr),
_ref_counter(nullptr)
{
}
template<class _T>
explicit shared_ptr(_T* ptr)
{
_ref_counter = new counter_block();
_obj_ptr = ptr;
}
template<class _T>
shared_ptr(const shared_ptr<_T> & other)
{
*this = other;
}
template<class _T>
shared_ptr<T> & operator=(const shared_ptr<_T> & other)
{
_obj_ptr = other._obj_ptr;
_ref_counter = other._ref_counter;
_ref_counter->inc();
return *this;
}
~shared_ptr()
{
_check_delete_ptr();
}
};
template<class T, class ... Args>
shared_ptr<T> make_shared(Args && ... args)
{
shared_ptr<T> ptr;
auto tmp_object = new shared_ptr<T>::_object_and_block<T>(args...);
ptr._obj_ptr = &tmp_object->object;
ptr._ref_counter = &tmp_object->cntr_block;
return ptr;
}
But when I delete object and counter block, the invalid heap block exception occurs.

N.B. _T is a reserved name and you must not use it for names of your own types/variables/parameters etc.
The problem is here:
void _delete_ptr()
{
delete _ref_counter;
delete _obj_ptr;
}
This is wrong for the make_shared case because you didn't allocate two separate objects.
The approach taken for make_shared in Boost's and GCC's shared_ptr is to use a new derived type of control block, which includes the reference counts in the base class and adds storage space for the managed object in the derived type. If you make _ref_cntr responsible for deleting the object via a virtual function then the derived type can override that virtual function to do something different (e.g. just use an explicit destructor call to destroy the object without freeing the storage).
If you give _ref_cntr a virtual destructor then delete _ref_counter will correctly destroy the derived type, so it should become something like:
void _delete_ptr()
{
_ref_counter->dispose();
delete _ref_counter;
}
Although if you don't plan to add weak_ptr support then there is no need to separate the destruction of the managed object and the control block, you can just have the control block's destructor do both:
void _delete_ptr()
{
delete _ref_counter;
}
Your current design fails to support an important property of shared_ptr, which is that the template<class Y> explicit shared_ptr(Y* ptr) constructor must remember the original type of ptr and call delete on that, not on _obj_ptr (which has been converted to T*). See the note in the docs for the corresponding constructor of boost::shared_ptr. To make that work the _ref_cntr needs to use type-erasure to store the original pointer, separate from the _obj_ptr in the shared_ptr object, so that _ref_cntr::dispose() can delete the correct value. That change in the design is also needed to support the aliasing constructor.
class _ref_cntr
{
private:
long counter;
public:
_ref_cntr() :
counter(1)
{
}
virtual ~_ref_cntr() { dispose(); }
void inc()
{
++counter;
}
void dec()
{
if (counter == 0)
{
throw std::logic_error("already zero");
}
--counter;
}
long use_count() const
{
return counter;
}
virtual void dispose() = 0;
};
template<class Y>
struct _ptr_and_block : _ref_cntr
{
Y* _ptr;
explicit _ptr_and_block(Y* p) : _ptr(p) { }
virtual void dispose() { delete _ptr; }
};
template<class Y>
struct _object_and_block : _ref_cntr
{
Y object;
template<class ... Args>
_object_and_block(Args && ...args) :
object(args...)
{
}
virtual void dispose() { /* no-op */ }
};
With this design, make_shared becomes:
template<class T, class ... Args>
shared_ptr<T> make_shared(Args && ... args)
{
shared_ptr<T> ptr;
auto tmp_object = new shared_ptr<T>::_object_and_block<T>(args...);
ptr._obj_ptr = &tmp_object->object;
ptr._ref_counter = tmp_object;
return ptr;
}
So _ref_counter points to the allocated control block and when you do delete _ref_counter that means you you have a correctly-matched new/delete pair that allocates and deallocates the same object, instead of creating one object with new then trying to delete two different objects.
To add weak_ptr support you need to add a second count to the control block, and move the call to dispose() out of the destructor, so it is called when the first count goes to zero (e.g. in dec()) and only call the destructor when the second count goes to zero. Then to do all that in a thread-safe way adds a lot of subtle complexity that would take much longer to explain than this answer.
Also, this part of your implementation is wrong and leaks memory:
void _check_delete_ptr()
{
if (_obj_ptr == nullptr)
{
return;
}
It's possible to constructor a shared_ptr with a null pointer, e.g. shared_ptr<int>((int*)nullptr), in which case the constructor will allocate a control block, but because _obj_ptr is null you will never delete the control block.

Related

Can I call a virtual destructor using a function pointer?

I have class Data which can hold a pointer to an object. I want to be able to call its destructor manually later on, for which I need its address stored in a variable but it seems that taking the address of constructor/destructor is forbidden. Is there any way around this ?
struct Data {
union {
long i;
float f;
void* data_ptr;
} _data;
std::type_index _typeIndex;
void (*_destructor_ptr)();
template<typename T>
void Init() {
if constexpr (std::is_integral<T>::value) {
//
}
else if constexpr (std::is_floating_point<T>::value) {
//
}
else {
_data.data_ptr = new T;
_typeIndex = std::type_index(typeid(T));
_destructor_ptr = &T::~T; // << -- can't do this
}
}
Store a lambda, suitably converted:
void (*_destructor_ptr)(void *v);
// ...
_destructor_ptr = [](void* v) { delete static_cast<T*>(v); };
Note that you must pass _data.data_ptr for v. If you intend to store a plain function pointer, the lambda may not capture or implicitly refer to _data.data_ptr.
There's also this solution if your compiler doesn't support lambdas:
template<typename T>
struct DestructorHelper {
static void Destroy(void * v) {
delete static_cast<T*>(v);
}
};
and use it as:
_destructor_ptr = &DestructorHelper<T>::Destroy;
I'll also add a solution for smart pointers:
template <class T>
struct DataPtrDeleter
{
void operator()(void * p) { delete (T*)p; }
}
std::shared_ptr<void*> data_ptr(new T, DataPtrDeleter<T>());
//or
std::unique_ptr<void*, DataPtrDeleter<T>> data_ptr(new T, DataPtrDeleter<T>());

Non-copying std::shared_ptr<boost::any>?

I store "instances of different types" with "shared ownership". That's what I currently do:
class Destructible {
public:
virtual ~Destructible() = default;
};
// UGLY
class MyType1 : public Destructible { ... };
class MyTypeN : public Destructible { ... };
class Storage {
std::vector<std::shared_ptr<Destructible>> objects_;
...
}
I'd love to switch to boost::any, removing all these conformances and gaining the ability to store instances of truly any type. Also I like boost::any interface and boost::any_cast.
But my types don't satisfy ValueType requirements, they are not copyable. What is the best (preferably existing) solution for this problem? Something like shared_any_ptr, which captures destructor at creation, has type erasure, reference counter and can do any_cast.
Edit: boost::any allows creation with move, but I'd prefer not to even move and use pointers.
Edit2: I also use make_shared extensively, so something make_shared_any_ptr would come in handy.
This isn't tricky with shared pointers. We can even avoid multiple allocations.
struct any_block {
any_block(any_block const&)=delete;
template<class T>
T* try_get() {
if (!info || !ptr) return nullptr;
if (std::type_index(typeid(T)) != std::type_index(*info)) return nullptr;
return static_cast<T*>(ptr);
}
template<class T>
T const* try_get() const {
if (!info || !ptr) return nullptr;
if (std::type_index(typeid(T)) != std::type_index(*info)) return nullptr;
return static_cast<T const*>(ptr);
}
~any_block() {
cleanup();
}
protected:
void cleanup(){
if (dtor) dtor(this);
dtor=0;
}
any_block() {}
std::type_info const* info = nullptr;
void* ptr = nullptr;
void(*dtor)(any_block*) = nullptr;
};
template<class T>
struct any_block_made:any_block {
std::aligned_storage_t<sizeof(T), alignof(T)> data;
any_block_made() {}
~any_block_made() {}
T* get_unsafe() {
return static_cast<T*>((void*)&data);
}
template<class...Args>
void emplace(Args&&...args) {
ptr = ::new((void*)get_unsafe()) T(std::forward<Args>(args)...);
info = &typeid(T);
dtor = [](any_block* self){
static_cast<any_block_made<T>*>(self)->get_unsafe()->~T();
};
}
};
template<class D>
struct any_block_dtor:any_block {
std::aligned_storage_t<sizeof(D), alignof(D)> dtor_data;
any_block_dtor() {}
~any_block_dtor() {
cleanup();
if (info) dtor_unsafe()->~D();
}
D* dtor_unsafe() {
return static_cast<D*>((void*)&dtor_data);
}
template<class T, class D0>
void init(T* t, D0&& d) {
::new( (void*)dtor_unsafe() ) D(std::forward<D0>(d));
info = &typeid(T);
ptr = t;
dtor = [](any_block* s) {
auto* self = static_cast<any_block_dtor<D>*>(s);
(*self->dtor_unsafe())( static_cast<T*>(self->ptr) );
};
}
};
using any_ptr = std::shared_ptr<any_block>;
template<class T, class...Args>
any_ptr
make_any_ptr(Args&&...args) {
auto r = std::make_shared<any_block_made<T>>();
if (!r) return nullptr;
r->emplace(std::forward<Args>(args)...);
return r;
}
template<class T, class D=std::default_delete<T>>
any_ptr wrap_any_ptr( T* t, D&& d = {} ) {
auto r = std::make_shared<any_block_dtor<std::decay_t<D>>>();
if (!r) return nullptr;
r->init( t, std::forward<D>(d) );
return r;
}
you'd have to implement any_cast, but with try_get<T> it should be easy.
There may be some corner cases like const T that the above doesn't handle.
template<class T>
std::shared_ptr<T>
crystalize_any_ptr( any_ptr ptr ) {
if (!ptr) return nullptr;
T* pt = ptr->try_get<T>();
if (!pt) return nullptr;
return {pt, ptr}; // aliasing constructor
}
This lets you take a any_ptr and turn it into a shared_ptr<T> if the types match without copying anything.
live example.
You'll notice how similar any_block_made and any_block_dtor is. I believe that this is why at least one major shared_ptr in a std library reuses the spot the deleter lives in for make_shared itself.
I could probably do similar, and reduce binary size here. In addition, the T/D parameter of any_block_made and any_block_dtor is really just about how big and aligned the block of memory we play with is, and what exactly type erasued helper I store in the dtor pointer in the parent. A compiler/linker with COMDAT folding (MSVC or GOLD) may eliminate the binary bloat here, but with a bit of care I could do it myself.

Custom std::function-like implementation for both free and member functions

I needed something like std::function but then I found this which is faster (as the author claims) and can even be compared by the == operator. I adapted it to allow a dynamic return type and arguments, like this:
template<typename TReturn, typename... TArgs>
class Delegate {};
template<typename TReturn, typename... TArgs>
class Delegate<TReturn(TArgs...)> final
{
private:
typedef void* InstancePointer;
typedef TReturn (*InternalFunction)(InstancePointer, TArgs...);
private:
// Turns a free function into our internal function stub
template <TReturn (*FreeFunction)(TArgs...)>
static TReturn FreeFunctionStub(InstancePointer instance, TArgs... args) {
// We don't need the instance pointer because we're dealing with free functions
return (FreeFunction)(std::forward<TArgs>(args)...);
}
// Turns a member function into our internal function stub
template <class TClass, TReturn (TClass::*MemberFunction)(TArgs...)>
static TReturn MemberFunctionStub(InstancePointer instance, TArgs... args) {
// Cast the instance pointer back into the original class instance
return (static_cast<TClass*>(instance)->*MemberFunction)(std::forward<TArgs>(args)...);
}
public:
Delegate() = default;
// Resets this delegate to a new free function
template <TReturn(*FreeFunction)(TArgs...)>
void reset() {
m_instance = nullptr;
m_function = &FreeFunctionStub<FreeFunction>;
}
// Resets this delegate to a new member function
template <class TClass, TReturn(TClass::*MemberFunction)(TArgs...)>
void reset(TClass* instance) {
m_instance = instance;
m_function = &MemberFunctionStub<TClass, MemberFunction>;
}
// Resets this delegate to a new free function
void specialReset(TReturn(*FreeFunction)(TArgs...)) {
m_instance = nullptr;
m_function = ???
}
// Resets this delegate to a new member function
template<class TClass>
void specialReset(TClass *instance, TReturn(TClass::*MemberFunction)(TArgs...)) {
m_instance = instance;
m_function = ???
}
// Invokes this delegate
TReturn invoke(TArgs... args) const {
if (m_function == nullptr)
throw new std::runtime_error(""Unbound delegate! Call reset() first."");
return m_function(m_instance, std::forward<TArgs>(args)...);
}
private:
InstancePointer m_instance;
InternalFunction m_function;
};
The usage goes like that:
Delegate<void()> del1;
Delegate<int(double)> del2;
del1.reset<&someParameterlessVoidFreeFunction>();
del1.invoke();
del2.reset<SomeClass, &SomeClass::someIntMemberFunction>(&someClassInstance);
del2.invoke(24.2);
What I am trying to do is to achieve something like this (IMO, much cleaner and intuitive):
Delegate<void()> del1;
Delegate<int(double)> del2;
del1.reset(&someParameterlessVoidFreeFunction);
del1.invoke();
del2.reset(&SomeClass::someIntMemberFunction, &someClassInstance);
del2.invoke(24.2);
However, I dont quite understand the m_function concept. What I am trying to achive is even possible? How could I do that?
Also, what exactly is the <TReturn(TArgs...)> part of class Delegate and why do we need to define a class Delegate {}; first?
Right, so after days of researchs, developing and testing, I made up something that does accomplish a faster std::function/std::bind with a much better syntax.
For those wondering, take a look at Delegator. I can't paste the whole code here because it got too big to be posted here. Also, at this link you'll always have the lastest version.
With Delegator, you can do the following:
Delegate<void(void)> d1; // Blank delegate
Delegate<void(void)> d2(&freeFunction); // Delegate to a free function
Delegate<void(void)> d3(&SomeClass::someClassStaticFunction); // Delegate to a static function
Delegate<void(void)> d4(&SomeClass::someClassFunction, &someClassInstance); // Delegate to a member function
Delegate<void(void)> d5(&SomeClass::someClassConstFunction, &someClassInstance); // Delegate to a member const function
d1.reset(); // Resets the delegate
d1.reset(&freeFunction); // Resets the delegate to a free function
d1.reset(&SomeClass::someClassStaticFunction); // Resets the delegate to a static function
d1.reset(&SomeClass::someClassFunction, &someClassInstance); // Resets the delegate to a member function
d1.reset(&SomeClass::someClassConstFunction, &someClassInstance); // Resets the delegate to a member const function
d1.reset(&d2); // Resets the delegate to d2's state
Basically the solution for your syntax is to get closer to what we'd do to implement std::function and sacrifice speed for flexibility (due to use of run-time polymorphism instead of pure templating)
The first call can be solved by adding another member variable to your Delegate class to hold free functions:
template<typename TReturn, typename... TArgs>
class Delegate<TReturn(TArgs...)> final
{
// ...
typedef TReturn(*FreeFunctionType)(TArgs...);
FreeFunctionType m_free_function = nullptr;
};
Now you can set m_free_function within specialReset, while resetting the other members:
void specialReset(TReturn(*FreeFunction)(TArgs...)) {
m_instance = nullptr;
m_function = nullptr;
m_free_function = FreeFunction;
}
And calling it performs an additional null check:
TReturn invoke(TArgs... args) const {
if (m_function == nullptr && m_free_function == nullptr)
throw new std::runtime_error("Unbound delegate! Call reset() first.");
else if (m_function)
return m_function(m_instance, std::forward<TArgs>(args)...);
else return m_free_function(std::forward<TArgs>(args)...);
}
and a test:
void foo()
{
std::cout << "Called foo()\n";
}
int main()
{
Delegate<void()> del1;
del1.specialReset(&::foo);
del1.invoke();
}
The second one is a little trickier.
The idea for solving this is to declare an abstract base class pointer that we can derive from later for making our call:
template<typename TReturn, typename... TArgs>
class Delegate<TReturn(TArgs...)> final
{
private:
//...
struct ICall
{
virtual TReturn doCall(TArgs... args) = 0;
};
std::unique_ptr<ICall> caller;
};
Usually this pointer will be nullptr, which we can check for, but otherwise we can call the function in invoke:
// Invokes this delegate
TReturn invoke(TArgs... args) const {
if (m_function == nullptr && m_free_function == nullptr && caller == nullptr)
throw new std::runtime_error("Unbound delegate! Call reset() first.");
else if (m_function)
return m_function(m_instance, std::forward<TArgs>(args)...);
else if (caller)
return caller->doCall(std::forward<TArgs>(args)...);
else
return m_free_function(std::forward<TArgs>(args)...);
}
And finally the implementation of our second specialReset where we create a derived class and set our pointer:
template<class TClass>
void specialReset(TClass *instance, TReturn(TClass::*MemberFunc)(TArgs...)) {
m_instance = nullptr;
m_function = nullptr;
m_free_function = nullptr;
struct DerivedCall : public ICall
{
DerivedCall(TClass* _instance, TReturn(TClass::*_func)(TArgs...)) : m_instance(_instance), m_func(_func){}
TReturn doCall(TArgs... args) override
{
return (m_instance->*m_func)(std::forward<TArgs>(args)...);
}
TClass* m_instance;
TReturn(TClass::*m_func)(TArgs...);
};
caller = std::make_unique<DerivedCall>(instance, MemberFunc);
}
And a test:
struct A{
int foo(double){std::cout << "Called A::foo\n"; return 42;}
};
int main()
{
Delegate<int(double)> del2;
A a;
del2.specialReset(&a, &A::foo);
del2.invoke(24.2);
}
Demo
I'm sure someone more clever than I can come up with something better. As I said. We're probably already losing the precious speed that you advocated for over std::function because of the runtimeyness of this all, and I'm not sure what it does for the operator== (It probably became infeasible the second I created m_free_function). We cannot use specialReset with lambdas here (...yet).

Is there a way to return an abstraction from a function without using new (for performance reasons)

For example I have some function pet_maker() that creates and returns a Cat or a Dog as a base Pet. I want to call this function many many times, and do something with the Pet returned.
Traditionally I would new the Cat or Dog in pet_maker() and return a pointer to it, however the new call is much slower than doing everything on the stack.
Is there a neat way anyone can think of to return as an abstraction without having to do the new every time the function is called, or is there some other way that I can quickly create and return abstractions?
Using new is pretty much inevitable if you want polymorphism. But the reason new works slowly is because it looks for free memory every time. What you could do is write your own operator new, which could, in theory, for example use pre-allocated memory chunks and be very fast.
This article covers many aspects of what you might need.
Each allocation is an overhead so you may get benefits by allocating whole arrays of objects rather than one object at a time.
You could use std::deque to achieve this:
class Pet { public: virtual ~Pet() {} virtual std::string talk() const = 0; };
class Cat: public Pet { std::string talk() const override { return "meow"; }};
class Dog: public Pet { std::string talk() const override { return "woof"; }};
class Pig: public Pet { std::string talk() const override { return "oink"; }};
class PetMaker
{
// std::deque never re-allocates when adding
// elements which is important when distributing
// pointers to the elements
std::deque<Cat> cats;
std::deque<Dog> dogs;
std::deque<Pig> pigs;
public:
Pet* make()
{
switch(std::rand() % 3)
{
case 0:
cats.emplace_back();
return &cats.back();
case 1:
dogs.emplace_back();
return &dogs.back();
}
pigs.emplace_back();
return &pigs.back();
}
};
int main()
{
std::srand(std::time(0));
PetMaker maker;
std::vector<Pet*> pets;
for(auto i = 0; i < 100; ++i)
pets.push_back(maker.make());
for(auto pet: pets)
std::cout << pet->talk() << '\n';
}
The reason to use a std::deque is that it never reallocates its elements when you add new ones so the pointers that you distribute always remain valid until the PetMaker itself is deleted.
An added benefit to this over allocating objects individually is that they don't need to be deleted or placed in a smart pointer, the std::deque manages their lifetime.
Is there a neat way anyone can think of to return as an abstraction without having to do the new every time the function is called, or is there some other way that I can quickly create and return abstractions?
TL;DR: The function need not allocate if there is already sufficient memory to work with.
A simple way would be to create a smart pointer that is slightly different from its siblings: it would contain a buffer in which it would store the object. We can even make it non-nullable!
Long version:
I'll present the rough draft in reverse order, from the motivation to the tricky details:
class Pet {
public:
virtual ~Pet() {}
virtual void say() = 0;
};
class Cat: public Pet {
public:
virtual void say() override { std::cout << "Miaou\n"; }
};
class Dog: public Pet {
public:
virtual void say() override { std::cout << "Woof\n"; }
};
template <>
struct polymorphic_value_memory<Pet> {
static size_t const capacity = sizeof(Dog);
static size_t const alignment = alignof(Dog);
};
typedef polymorphic_value<Pet> any_pet;
any_pet pet_factory(std::string const& name) {
if (name == "Cat") { return any_pet::build<Cat>(); }
if (name == "Dog") { return any_pet::build<Dog>(); }
throw std::runtime_error("Unknown pet name");
}
int main() {
any_pet pet = pet_factory("Cat");
pet->say();
pet = pet_factory("Dog");
pet->say();
pet = pet_factory("Cat");
pet->say();
}
The expected output:
Miaou
Woof
Miaou
which you can find here.
Note that it is required to specify the maximum size and alignment of the derived values that can be supported. No way around that.
Of course, we statically check whether the caller would attempt to build a value with an inappropriate type to avoid any unpleasantness.
The main disadvantage, of course, is that it must be at least as big (and aligned) as its largest variant, and all this must be predicted ahead of time. This is thus not a silver bullet, but performance-wise the absence of memory-allocation can rock.
How does it work? Using this high-level class (and the helper):
// To be specialized for each base class:
// - provide capacity member (size_t)
// - provide alignment member (size_t)
template <typename> struct polymorphic_value_memory;
template <typename T,
typename CA = CopyAssignableTag,
typename CC = CopyConstructibleTag,
typename MA = MoveAssignableTag,
typename MC = MoveConstructibleTag>
class polymorphic_value {
static size_t const capacity = polymorphic_value_memory<T>::capacity;
static size_t const alignment = polymorphic_value_memory<T>::alignment;
static bool const move_constructible = std::is_same<MC, MoveConstructibleTag>::value;
static bool const move_assignable = std::is_same<MA, MoveAssignableTag>::value;
static bool const copy_constructible = std::is_same<CC, CopyConstructibleTag>::value;
static bool const copy_assignable = std::is_same<CA, CopyAssignableTag>::value;
typedef typename std::aligned_storage<capacity, alignment>::type storage_type;
public:
template <typename U, typename... Args>
static polymorphic_value build(Args&&... args) {
static_assert(
sizeof(U) <= capacity,
"Cannot host such a large type."
);
static_assert(
alignof(U) <= alignment,
"Cannot host such a largely aligned type."
);
polymorphic_value result{NoneTag{}};
result.m_vtable = &build_vtable<T, U, MC, CC, MA, CA>();
new (result.get_ptr()) U(std::forward<Args>(args)...);
return result;
}
polymorphic_value(polymorphic_value&& other): m_vtable(other.m_vtable), m_storage() {
static_assert(
move_constructible,
"Cannot move construct this value."
);
(*m_vtable->move_construct)(&other.m_storage, &m_storage);
m_vtable = other.m_vtable;
}
polymorphic_value& operator=(polymorphic_value&& other) {
static_assert(
move_assignable || move_constructible,
"Cannot move assign this value."
);
if (move_assignable && m_vtable == other.m_vtable)
{
(*m_vtable->move_assign)(&other.m_storage, &m_storage);
}
else
{
(*m_vtable->destroy)(&m_storage);
m_vtable = other.m_vtable;
(*m_vtable->move_construct)(&other.m_storage, &m_storage);
}
return *this;
}
polymorphic_value(polymorphic_value const& other): m_vtable(other.m_vtable), m_storage() {
static_assert(
copy_constructible,
"Cannot copy construct this value."
);
(*m_vtable->copy_construct)(&other.m_storage, &m_storage);
}
polymorphic_value& operator=(polymorphic_value const& other) {
static_assert(
copy_assignable || (copy_constructible && move_constructible),
"Cannot copy assign this value."
);
if (copy_assignable && m_vtable == other.m_vtable)
{
(*m_vtable->copy_assign)(&other.m_storage, &m_storage);
return *this;
}
// Exception safety
storage_type tmp;
(*other.m_vtable->copy_construct)(&other.m_storage, &tmp);
if (move_assignable && m_vtable == other.m_vtable)
{
(*m_vtable->move_assign)(&tmp, &m_storage);
}
else
{
(*m_vtable->destroy)(&m_storage);
m_vtable = other.m_vtable;
(*m_vtable->move_construct)(&tmp, &m_storage);
}
return *this;
}
~polymorphic_value() { (*m_vtable->destroy)(&m_storage); }
T& get() { return *this->get_ptr(); }
T const& get() const { return *this->get_ptr(); }
T* operator->() { return this->get_ptr(); }
T const* operator->() const { return this->get_ptr(); }
T& operator*() { return this->get(); }
T const& operator*() const { return this->get(); }
private:
polymorphic_value(NoneTag): m_vtable(0), m_storage() {}
T* get_ptr() { return reinterpret_cast<T*>(&m_storage); }
T const* get_ptr() const { return reinterpret_cast<T const*>(&m_storage); }
polymorphic_value_vtable const* m_vtable;
storage_type m_storage;
}; // class polymorphic_value
Essentially, this is just like any STL container. The bulk of the complexity is in redefining the construction, move, copy and destruction. It's otherwise quite simple.
There are two points of note:
I use a tag-based approach to handling capabilities:
for example, a copy constructor is only available if the CopyConstructibleTag is passed
if the CopyConstructibleTag is passed, all types passed to build must be copy constructible
Some operations are provided even if the objects do not have the capability, as long as some alternative way of providing them exist
Obviously, all methods preserve the invariant that the polymorphic_value is never empty.
There is also a tricky detail related to assignments: assignment is only well-defined if both objects are of the same dynamic type, which we check with the m_vtable == other.m_vtable checks.
For completeness, the missing pieces used to power up this class:
//
// VTable, with nullable methods for run-time detection of capabilities
//
struct NoneTag {};
struct MoveConstructibleTag {};
struct CopyConstructibleTag {};
struct MoveAssignableTag {};
struct CopyAssignableTag {};
struct polymorphic_value_vtable {
typedef void (*move_construct_type)(void* src, void* dst);
typedef void (*copy_construct_type)(void const* src, void* dst);
typedef void (*move_assign_type)(void* src, void* dst);
typedef void (*copy_assign_type)(void const* src, void* dst);
typedef void (*destroy_type)(void* dst);
move_construct_type move_construct;
copy_construct_type copy_construct;
move_assign_type move_assign;
copy_assign_type copy_assign;
destroy_type destroy;
};
template <typename Base, typename Derived>
void core_move_construct_function(void* src, void* dst) {
Derived* derived = reinterpret_cast<Derived*>(src);
new (reinterpret_cast<Base*>(dst)) Derived(std::move(*derived));
} // core_move_construct_function
template <typename Base, typename Derived>
void core_copy_construct_function(void const* src, void* dst) {
Derived const* derived = reinterpret_cast<Derived const*>(src);
new (reinterpret_cast<Base*>(dst)) Derived(*derived);
} // core_copy_construct_function
template <typename Derived>
void core_move_assign_function(void* src, void* dst) {
Derived* source = reinterpret_cast<Derived*>(src);
Derived* destination = reinterpret_cast<Derived*>(dst);
*destination = std::move(*source);
} // core_move_assign_function
template <typename Derived>
void core_copy_assign_function(void const* src, void* dst) {
Derived const* source = reinterpret_cast<Derived const*>(src);
Derived* destination = reinterpret_cast<Derived*>(dst);
*destination = *source;
} // core_copy_assign_function
template <typename Derived>
void core_destroy_function(void* dst) {
Derived* d = reinterpret_cast<Derived*>(dst);
d->~Derived();
} // core_destroy_function
template <typename Tag, typename Base, typename Derived>
typename std::enable_if<
std::is_same<Tag, MoveConstructibleTag>::value,
polymorphic_value_vtable::move_construct_type
>::type
build_move_construct_function()
{
return &core_move_construct_function<Base, Derived>;
} // build_move_construct_function
template <typename Tag, typename Base, typename Derived>
typename std::enable_if<
std::is_same<Tag, CopyConstructibleTag>::value,
polymorphic_value_vtable::copy_construct_type
>::type
build_copy_construct_function()
{
return &core_copy_construct_function<Base, Derived>;
} // build_copy_construct_function
template <typename Tag, typename Derived>
typename std::enable_if<
std::is_same<Tag, MoveAssignableTag>::value,
polymorphic_value_vtable::move_assign_type
>::type
build_move_assign_function()
{
return &core_move_assign_function<Derived>;
} // build_move_assign_function
template <typename Tag, typename Derived>
typename std::enable_if<
std::is_same<Tag, CopyAssignableTag>::value,
polymorphic_value_vtable::copy_construct_type
>::type
build_copy_assign_function()
{
return &core_copy_assign_function<Derived>;
} // build_copy_assign_function
template <typename Base, typename Derived,
typename MC, typename CC,
typename MA, typename CA>
polymorphic_value_vtable const& build_vtable() {
static polymorphic_value_vtable const V = {
build_move_construct_function<MC, Base, Derived>(),
build_copy_construct_function<CC, Base, Derived>(),
build_move_assign_function<MA, Derived>(),
build_copy_assign_function<CA, Derived>(),
&core_destroy_function<Derived>
};
return V;
} // build_vtable
The one trick I use here is to let the user configure whether the types he will use in this container can be move constructed, move assigned, ... via capability tags. A number of operations are keyed on these tags and will either be disabled or less efficient if the requested capability
You can create a stack allocator instance (with some max limit of course) and pass that as an argument to your pet_maker function. Then instead of regular new do a placement new on the address provided by the stack allocator.
You can probably also default to new on exceeding max_size of the stack allocator.
One way is to work out, in advance through analysis, how many of each type of object is needed by your program.
Then you can allocate arrays of an appropriate size in advance, as long as you have book-keeping to track the allocation.
For example;
#include <array>
// Ncats, Ndogs, etc are predefined constants specifying the number of cats and dogs
std::array<Cat, Ncats> cats;
std::array<Dog, Ndogs> dogs;
// bookkeeping - track the returned number of cats and dogs
std::size_t Rcats = 0, Rdogs = 0;
Pet *pet_maker()
{
// determine what needs to be returned
if (return_cat)
{
assert(Rcats < Ncats);
return &cats[Rcats++];
}
else if (return_dog)
{
assert(Rdogs < Ndogs);
return &dogs[Rdogs++];
}
else
{
// handle other case somehow
}
}
Of course, the big trade-off in is the requirement to explicitly determine the number of each type of animal in advance - and separately track each type.
However, if you wish to avoid dynamic memory allocation (operator new) then this way - as draconian as it might seem - provides an absolute guarantee. Using operator new explicitly allows the number of objects needed to be determined at run time. Conversely, to avoid using operator new but allow some function to safely access a number of objects it is necessary to predetermine the number of objects.
It depends on the exact use case you have, and what restrictions you are willing to tolerate. For example, if you are OK with re-using the same objects rather than having new copies every time, you could return references to static objects inside the function:
Pet& pet_maker()
{
static Dog dog;
static Cat cat;
//...
if(shouldReturnDog) {
//manipulate dog as necessary
//...
return dog;
}
else
{
//manipulate cat as necessary
//...
return cat;
}
}
This works if the client code accepts that it doesn't own the object returned and that the same physical instances are reused.
There are other tricks possible if this particular set of assumptions is unsuitable.
At some point somebody is going to have to allocate the memory and initialize the objects. If doing them on demand, using the heap via new is taking too long, then why no pre-allocate a number of then in a pool. Then you can initialize each individual object on an as needed basis. The downside is that you might have a bunch of extra objects laying around for a while.
If actually initializing the object is the problem, and not memory allocation, then you can consider keeping a pre-built object around and using the Pototype pattern for quicker initialization.
For best results, memory allocation is problem and initialization time, you can combine both strategies.
You may want to consider using a (Boost) variant. It will require an extra step by the caller, but it might suit your needs:
#include <boost/variant/variant.hpp>
#include <boost/variant/get.hpp>
#include <iostream>
using boost::variant;
using std::cout;
struct Pet {
virtual void print_type() const = 0;
};
struct Cat : Pet {
virtual void print_type() const { cout << "Cat\n"; }
};
struct Dog : Pet {
virtual void print_type() const { cout << "Dog\n"; }
};
using PetVariant = variant<Cat,Dog>;
enum class PetType { cat, dog };
PetVariant make_pet(PetType type)
{
switch (type) {
case PetType::cat: return Cat();
case PetType::dog: return Dog();
}
return {};
}
Pet& get_pet(PetVariant& pet_variant)
{
return apply_visitor([](Pet& pet) -> Pet& { return pet; },pet_variant);
}
int main()
{
PetVariant pet_variant_1 = make_pet(PetType::cat);
PetVariant pet_variant_2 = make_pet(PetType::dog);
Pet& pet1 = get_pet(pet_variant_1);
Pet& pet2 = get_pet(pet_variant_2);
pet1.print_type();
pet2.print_type();
}
Output:
Cat
Dog
For example I have some function pet_maker() that creates and returns a Cat or a Dog as a base Pet. I want to call this function many many times, and do something with the Pet returned.
If you are going to discard the pet immediately after you have done something with it, you can use the technique shown in the following example:
#include<iostream>
#include<utility>
struct Pet {
virtual ~Pet() = default;
virtual void foo() const = 0;
};
struct Cat: Pet {
void foo() const override {
std::cout << "cat" << std::endl;
}
};
struct Dog: Pet {
void foo() const override {
std::cout << "dog" << std::endl;
}
};
template<typename T, typename F>
void factory(F &&f) {
std::forward<F>(f)(T{});
}
int main() {
auto lambda = [](const Pet &pet) { pet.foo(); };
factory<Cat>(lambda);
factory<Dog>(lambda);
}
No allocation required at all. The basic idea is to revert the logic: the factory no longer returns an object. Instead it calls a function providing the right instance as a reference.
The problem with this approach arises if you want to copy and store the object somewhere.
For it is not clear from the question, it's worth to propose also this solution.

Is there an idiomatic way to return a pointer that optionally owns its value

I have a function that given a path name, does a look up and returns a pointer to the associated value. Sometimes the value lives in a static cache, sometimes it gets calculated and created on the fly.
So, sometimes the caller takes ownership and needs to delete the object after reading it, and sometimes not. I'm wondering, is there something I can wrap this pointer with so that it will automatically be freed as necessary by the caller?
I was thinking I might be able to use a unique_ptr, but isn't the deleter part of the type, so how could I return the same type that sometimes does and sometimes doesn't actually delete.
So indeed, one solution could be returning a normal std::shared_ptr for the value created inside the function, and another one with an empty deleter for the value that lives in the map.
Live example of this solution
You can see how both use cases don't require any actions from the calling code and are completely transparent.
You can use std::unique_ptr with a deleter that knows whether to free or not. While the deleter type is part of the unique_ptr type, different unique_ptr instances can have different deleter instances:
template <class T>
class delete_if_not_cached {
bool cached;
public:
delete_if_not_cached(bool c = false) : cached(c) {}
void operator()(T *obj) { if (!cached) delete obj; }
}
and you have your function return a std::unique_ptr<T, delete_if_not_cached<T>>. If you're returning a pointer into the cache, you create that pointer as:
return std::unique_ptr<T, delete_if_not_cached<T>>(raw_pointer, delete_if_not_cached<T>(true));
to return a non-cached object, use
return std::unique_ptr<T, delete_if_not_cached<T>>(new T(...))
One potential pitfall is that if you ever remove things from the cache, that might leave dangling unique_ptrs that you have previously returned. If that's an issue, it probably makes more sense to use shared_ptrs both to return and in the cache itself.
You could use a std::shared_ptr but that does not really describe your ownership model. Have you considered rolling your own wrapper that contains a std::unique_ptr and a raw pointer and uses the correct one depending on the circumstances? Something like:
#include <cassert>
#include <memory>
class MyClass { };
class Wrapper {
const MyClass* cached_;
std::unique_ptr<MyClass> owned_;
public:
Wrapper() : cached_(nullptr) {}
void setCached(const MyClass* cached) {cached_ = cached;}
void setOwned(std::unique_ptr<MyClass> owned) { owned_ = std::move(owned); }
const MyClass* get() const {return cached_ ? cached_ : owned_.get();}
};
Wrapper getWrapper(int i) {
static MyClass first;
static MyClass second;
Wrapper wrapper;
if (i == 0)
wrapper.setCached(&first);
else if (i == 1)
wrapper.setCached(&second);
else
wrapper.setOwned(std::unique_ptr<MyClass>(new MyClass()));
return wrapper;
}
int main() {
for (int i = 0; i != 4; ++i) {
Wrapper wrapper = getWrapper(i);
assert(wrapper.get() != nullptr);
}
}
The wrapper can either forward calls to the real class or provide access to a raw pointer to the real class.
Or the wrapper could work polymorphically, with an interface and two implementations. One with a raw pointer and one with a unique pointer:
#include <cassert>
#include <memory>
class MyClass {};
class Wrapper {
public:
virtual ~Wrapper() = 0;
virtual const MyClass* get() const = 0;
};
Wrapper::~Wrapper() {}
class OwnerWrapper : public Wrapper {
std::unique_ptr<MyClass> owned_;
public:
OwnerWrapper(std::unique_ptr<MyClass> in) : owned_(std::move(in)) {}
virtual const MyClass* get() const { return owned_.get(); }
};
class PtrWrapper : public Wrapper {
const MyClass* ptr_;
public:
PtrWrapper(const MyClass* ptr) : ptr_(ptr) {}
virtual const MyClass* get() const { return ptr_; }
};
std::unique_ptr<Wrapper> getWrapper(int i) {
static MyClass first;
static MyClass second;
if (i == 0)
return std::unique_ptr<Wrapper>(new PtrWrapper(&first));
else if (i == 1)
return std::unique_ptr<Wrapper>(new PtrWrapper(&second));
else {
std::unique_ptr<MyClass> myclass(new MyClass());
return std::unique_ptr<Wrapper>(new OwnerWrapper(std::move(myclass)));
}
}
int main() {
for (int i = 0; i != 4; ++i) {
auto wrapper = getWrapper(i);
assert(wrapper->get() != nullptr);
}
}