Simplified shared_ptr implementation - c++

I am attempting to implement a simplified shared_ptr that does not include weak_ptr functionality. It does support make_shared to perform only a single application (in fact, this is the only way to create a new shared_ptr in my implementation).
The problem is, something about my implementation causes my code to crash (hard fault on an ARM MCU).
/**
* #file shared_ptr.h
*
*/
#ifndef TW_SHARED_PTR_H_
#define TW_SHARED_PTR_H_
//==============================================================================
// INCLUDES
//==============================================================================
#include "_common.h"
/*
* If TW_CONFIG_USE_STD_SHARED_PTR == 1, simply define tw::shared_ptr (and other
* helper classes/methods) as wrappers around std::shared_ptr
*/
#if TW_CONFIG_USE_STD_SHARED_PTR == 1
#include <memory>
namespace tw {
template <typename T>
using shared_ptr = std::shared_ptr<T>;
template <typename T>
using enable_shared_from_this = std::enable_shared_from_this<T>;
template <typename T, typename ... ARGS>
inline shared_ptr<T> make_shared(ARGS&&... args) {
return std::make_shared<T>(std::forward<ARGS>(args)...);
}
} //namespace tw
#else //TW_CONFIG_USE_STD_SHARED_PTR == 0
#include <atomic>
#include <type_traits>
//==============================================================================
// DEFINES
//==============================================================================
#ifndef TW_SHARED_PTR_ASSERT
#include <cstdlib>
#define TW_SHARED_PTR_ASSERT(x_) if (!(x_)) abort()
#endif
namespace tw {
//==============================================================================
// CLASSES
//==============================================================================
template <typename T>
class shared_ptr;
class _enable_shared_from_this;
template <typename T>
class enable_shared_from_this;
//-----[ CLASS: _shared_ptr_base ]-------------------------------------------------
class _shared_ptr_base {
template <typename T_, typename ... ARGS_>
friend
shared_ptr<T_> make_shared(ARGS_&&...);
friend class _enable_shared_from_this;
protected:
using ref_count_type = std::atomic<unsigned int>;
struct control_block {
ref_count_type refCount;
struct {
const void* p;
void(*destroy)(const void*);
inline void operator()(){ destroy(p); }
inline operator bool() { return (p != nullptr) && (destroy != nullptr); }
} destructor;
};
};
//-----[ TEMPLATE CLASS: shared_ptr<T> ]-----------------------------------------
template <typename T>
class shared_ptr : public _shared_ptr_base {
template <typename T_>
friend
class shared_ptr;
template <typename T_>
friend
class enable_shared_from_this;
public:
using value_type = T;
public:
shared_ptr():
_block(nullptr),
_ptr(nullptr)
{
}
~shared_ptr() {
reset();
}
//Converting copy constructor
template <typename U_>
shared_ptr(const shared_ptr<U_>& other):
_block(other._block),
_ptr(other._ptr)
{
if (_block != nullptr) {
TW_SHARED_PTR_ASSERT(_ptr != nullptr);
//Increment ref count
++(_block->refCount);
}
}
//Converting move constructor
template <typename U_>
shared_ptr(shared_ptr<U_>&& other):
_block(other._block),
_ptr(other._ptr)
{
other._block = nullptr;
other._ptr = nullptr;
}
public:
//Converting copy assignment operator
template <typename U_>
shared_ptr& operator=(const shared_ptr<U_>& rhs) {
if (static_cast<void*>(this) != static_cast<void*>(&rhs)) {
reset();
_block = rhs._block;
_ptr = rhs._ptr;
if (_block != nullptr) {
TW_SHARED_PTR_ASSERT(_ptr != nullptr);
//Increment ref count
++(_block->refCount);
}
}
return *this;
}
//Converting move assignment operator
template <typename U_>
shared_ptr& operator=(shared_ptr<U_>&& rhs) {
if (static_cast<void*>(this) != static_cast<void*>(&rhs)) {
reset();
_block = rhs._block;
_ptr = rhs._ptr;
rhs._block = nullptr;
rhs._ptr = nullptr;
}
return *this;
}
inline T* operator->() const noexcept {
TW_SHARED_PTR_ASSERT(_ptr != nullptr);
return _ptr;
}
inline operator T*() const noexcept { return _ptr; }
inline T& operator*() const noexcept {
TW_SHARED_PTR_ASSERT(_ptr != nullptr);
return *_ptr;
}
inline operator bool() const noexcept { return (_ptr != nullptr); }
public:
inline T* get() const noexcept { return _ptr; }
void reset() {
if (_block != nullptr) {
TW_SHARED_PTR_ASSERT(_ptr != nullptr);
if (--(_block->refCount) == 0) {
TW_SHARED_PTR_ASSERT(_block->destructor);
//Free resources by calling deleter
_block->destructor();
}
}
_block = nullptr;
_ptr = nullptr;
}
inline ref_count_type::value_type use_count() const noexcept {
return (_block == nullptr) ? 0 : _block->refCount.load();
}
protected:
shared_ptr(control_block* block, value_type* ptr):
_block(block),
_ptr(ptr)
{
if (_block != nullptr) {
TW_SHARED_PTR_ASSERT(_ptr != nullptr);
//Increment ref count
++(_block->refCount);
}
}
private:
control_block* _block;
value_type* _ptr;
template <typename T_, typename ... ARGS_>
friend
shared_ptr<T_> make_shared(ARGS_&&...);
};
//-----[ CLASS: _enable_shared_from_this ]--------------------------------------
class _enable_shared_from_this {
protected:
_enable_shared_from_this():
_block(nullptr)
{
}
protected:
_shared_ptr_base::control_block* _block;
};
//-----[ TEMPLATE CLASS: enable_shared_from_this ]------------------------------
template <typename T>
class enable_shared_from_this : public _enable_shared_from_this {
template <typename T_, typename ... ARGS_>
friend
shared_ptr<T_> make_shared(ARGS_&&...);
public:
shared_ptr<T> shared_from_this() noexcept {
return shared_ptr<T>(_block, static_cast<T*>(this));
}
shared_ptr<const T> shared_from_this() const noexcept {
return shared_ptr<const T>(_block, static_cast<const T*>(this));
}
};
//==============================================================================
// FUNCTIONS
//==============================================================================
template <typename T, typename ... ARGS>
shared_ptr<T> make_shared(ARGS&&... args) {
struct FullBlock {
_shared_ptr_base::control_block block;
T value;
};
//Allocate block on heap
auto block = new FullBlock{
{0},
T{std::forward<ARGS>(args)...} //value
};
block->block.destructor.p = block;
block->block.destructor.destroy = [](const void* x){
delete static_cast<const FullBlock*>(x);
};
if constexpr (std::is_base_of_v<_enable_shared_from_this, T>) {
block->value._block = &block->block;
}
/*
* Up until this point, the make_shared function "owns" the pointer to
* 'block'. It now "transfers" ownership of this pointer to a shared_ptr
* instance.
*/
return shared_ptr<T>(&block->block, &block->value);
}
} //namespace tw
#endif
#endif /* TW_SHARED_PTR_H_ */
The define TW_CONFIG_USE_STD_SHARED_PTR allows me to swap between my shared_ptr implementation and the standard library's implementation. The standard library's implementation does NOT cause a hard fault in my application so I know that there must be something wrong with my implementation.
Can anyone spot any obvious problems in my implementation?

Even though this question was a bit open ended without an straight forward answer, those who commented did help me out quite a bit. #WhozCraig pointed out that there was not copy or move constructors. I did not realize this was the case as I thought that my "converting" (i.e., templated) copy and move constructors would also cover the default case (same pointer). However, I guess this is NOT how the C++ compiler handles this.
So, for reference, here is a version of my pointer which works correctly:
/**
* #file shared_ptr.h
*
*/
#ifndef TW_SHARED_PTR_H_
#define TW_SHARED_PTR_H_
//==============================================================================
// INCLUDES
//==============================================================================
/*
* If TW_CONFIG_USE_STD_SHARED_PTR == 1, simply define tw::shared_ptr (and other
* helper classes/methods) as wrappers around std::shared_ptr
*/
#if TW_CONFIG_USE_STD_SHARED_PTR == 1
#include <memory>
namespace tw {
template <typename T>
using shared_ptr = std::shared_ptr<T>;
template <typename T>
using enable_shared_from_this = std::enable_shared_from_this<T>;
template <typename T, typename ... ARGS>
inline shared_ptr<T> make_shared(ARGS&&... args) {
return std::make_shared<T>(std::forward<ARGS>(args)...);
}
} //namespace tw
#else //TW_CONFIG_USE_STD_SHARED_PTR == 0
#include <atomic>
#include <type_traits>
//==============================================================================
// DEFINES
//==============================================================================
#ifndef TW_SHARED_PTR_ASSERT
#include <cstdlib>
#define TW_SHARED_PTR_ASSERT(x_) if (!(x_)) abort()
#endif
namespace tw {
//==============================================================================
// CLASSES
//==============================================================================
template <typename T>
class shared_ptr;
class _enable_shared_from_this;
template <typename T>
class enable_shared_from_this;
//-----[ CLASS: _shared_ptr_base ]-------------------------------------------------
class _shared_ptr_base {
template <typename T_, typename ... ARGS_>
friend
shared_ptr<T_> make_shared(ARGS_&&...);
friend class _enable_shared_from_this;
protected:
using ref_count_type = std::atomic<unsigned int>;
struct control_block {
ref_count_type refCount;
struct {
const void* p;
void(*destroy)(const void*);
inline void operator()(){ destroy(p); }
inline operator bool() { return (p != nullptr) && (destroy != nullptr); }
} destructor;
};
};
//-----[ TEMPLATE CLASS: shared_ptr<T> ]-----------------------------------------
template <typename T>
class shared_ptr : public _shared_ptr_base {
template <typename T_>
friend
class shared_ptr;
template <typename T_>
friend
class enable_shared_from_this;
public:
using value_type = T;
public:
/**
* #brief Default constructor
*
* By default, a shared_ptr is null (nullptr)
*/
shared_ptr():
_block(nullptr),
_ptr(nullptr)
{
}
/**
* #brief Destructor
*/
~shared_ptr() {
reset();
}
/**
* #brief Copy constructor
*/
shared_ptr(const shared_ptr& other):
_block(other._block),
_ptr(other._ptr)
{
_incr();
}
/**
* #brief Converting copy constructor
*/
template <typename U_>
shared_ptr(const shared_ptr<U_>& other):
_block(other._block),
_ptr(other._ptr)
{
_incr();
}
/**
* #brief Move constructor
*/
shared_ptr(shared_ptr&& other):
_block(other._block),
_ptr(other._ptr)
{
other._block = nullptr;
other._ptr = nullptr;
}
/**
* #brief Converting move constructor
*/
template <typename U_>
shared_ptr(shared_ptr<U_>&& other):
_block(other._block),
_ptr(other._ptr)
{
other._block = nullptr;
other._ptr = nullptr;
}
public:
/**
* #brief Copy assignment operator
*/
shared_ptr& operator=(const shared_ptr& rhs) {
if (static_cast<void*>(this) != static_cast<void*>(&rhs)) {
reset();
_block = rhs._block;
_ptr = rhs._ptr;
_incr();
}
return *this;
}
/**
* #brief Converting copy assignment operator
*/
template <typename U_>
shared_ptr& operator=(const shared_ptr<U_>& rhs) {
if (static_cast<void*>(this) != static_cast<void*>(&rhs)) {
reset();
_block = rhs._block;
_ptr = rhs._ptr;
_incr();
}
return *this;
}
/**
* #brief Move assignment operator
*/
shared_ptr& operator=(shared_ptr&& rhs) {
if (static_cast<void*>(this) != static_cast<void*>(&rhs)) {
reset();
_block = rhs._block;
_ptr = rhs._ptr;
rhs._block = nullptr;
rhs._ptr = nullptr;
}
return *this;
}
/**
* #brief Converting move assignment operator
*/
template <typename U_>
shared_ptr& operator=(shared_ptr<U_>&& rhs) {
if (static_cast<void*>(this) != static_cast<void*>(&rhs)) {
reset();
_block = rhs._block;
_ptr = rhs._ptr;
rhs._block = nullptr;
rhs._ptr = nullptr;
}
return *this;
}
inline T* operator->() const noexcept {
TW_SHARED_PTR_ASSERT(_ptr != nullptr);
return _ptr;
}
inline operator T*() const noexcept { return _ptr; }
inline T& operator*() const noexcept {
TW_SHARED_PTR_ASSERT(_ptr != nullptr);
return *_ptr;
}
inline operator bool() const noexcept { return (_ptr != nullptr); }
public:
inline T* get() const noexcept { return _ptr; }
void reset() {
if (_block != nullptr) {
TW_SHARED_PTR_ASSERT(_ptr != nullptr);
if (--(_block->refCount) == 0) {
TW_SHARED_PTR_ASSERT(_block->destructor);
//Free resources by calling deleter
_block->destructor();
}
}
_block = nullptr;
_ptr = nullptr;
}
inline ref_count_type::value_type use_count() const noexcept {
return (_block == nullptr) ? 0 : _block->refCount.load();
}
protected:
shared_ptr(control_block* block, value_type* ptr):
_block(block),
_ptr(ptr)
{
if (_block != nullptr) {
TW_SHARED_PTR_ASSERT(_ptr != nullptr);
//Increment ref count
++(_block->refCount);
}
}
private:
/**
* #brief Increment the reference count
*/
void _incr() {
if (_block != nullptr) {
TW_SHARED_PTR_ASSERT(_ptr != nullptr);
//Increment ref count
++(_block->refCount);
}
}
private:
control_block* _block;
value_type* _ptr;
template <typename T_, typename ... ARGS_>
friend
shared_ptr<T_> make_shared(ARGS_&&...);
};
//-----[ CLASS: _enable_shared_from_this ]--------------------------------------
class _enable_shared_from_this {
protected:
_enable_shared_from_this():
_block(nullptr)
{
}
protected:
_shared_ptr_base::control_block* _block;
};
//-----[ TEMPLATE CLASS: enable_shared_from_this ]------------------------------
template <typename T>
class enable_shared_from_this : public _enable_shared_from_this {
template <typename T_, typename ... ARGS_>
friend
shared_ptr<T_> make_shared(ARGS_&&...);
public:
shared_ptr<T> shared_from_this() noexcept {
return shared_ptr<T>(_block, static_cast<T*>(this));
}
shared_ptr<const T> shared_from_this() const noexcept {
return shared_ptr<const T>(_block, static_cast<const T*>(this));
}
};
//==============================================================================
// FUNCTIONS
//==============================================================================
template <typename T, typename ... ARGS>
shared_ptr<T> make_shared(ARGS&&... args) {
struct FullBlock {
_shared_ptr_base::control_block block;
T value;
};
//Allocate block on heap
auto block = new FullBlock{
{0},
T{std::forward<ARGS>(args)...} //value
};
block->block.destructor.p = block;
block->block.destructor.destroy = [](const void* x){
delete static_cast<const FullBlock*>(x);
};
if constexpr (std::is_base_of_v<_enable_shared_from_this, T>) {
block->value._block = &block->block;
}
/*
* Up until this point, the make_shared function "owns" the pointer to
* 'block'. It now "transfers" ownership of this pointer to a shared_ptr
* instance.
*/
return shared_ptr<T>(&block->block, &block->value);
}
} //namespace tw
#endif
#endif /* TW_SHARED_PTR_H_ */

Related

How do I correctly pass converting constructor from an std::queue through to the underlying std::deque?

I have created a custom memory allocator. To use it with STL Containers, I've also created a wrapper so that it adheres to the std::allocator_traits requirements.
#include <cstdint>
#include <memory>
#include <deque>
#include <queue>
class CustomAlloc
{
public:
CustomAlloc(const std::size_t& maxMem) noexcept
:
m_maxMem(maxMem),
m_usedMem(0)
{
}
CustomAlloc(CustomAlloc&& other) noexcept
:
m_maxMem(other.m_maxMem),
m_usedMem(other.m_usedMem)
{
}
CustomAlloc& operator=(CustomAlloc&& other) noexcept
{
m_maxMem = other.m_maxMem;
m_usedMem = other.m_usedMem;
return *this;
}
CustomAlloc(const CustomAlloc&) = delete;
CustomAlloc& operator=(CustomAlloc&) = delete;
[[nodiscard]] void* Allocate(const std::size_t& size)
{
if(m_usedMem + size > m_maxMem)
{
throw std::bad_alloc();
}
void* ptr = std::malloc(size);
if(ptr == nullptr)
{
throw std::bad_alloc();
}
m_usedMem += size;
return ptr;
}
void Free(void* const ptr) const
{
return std::free(ptr);
}
const std::size_t& GetMaxMem() const noexcept
{
return m_maxMem;
}
private:
std::size_t m_maxMem;
std::size_t m_usedMem;
};
template<typename T, typename Alloc>
class STLAdaptor
{
public:
typedef T value_type;
STLAdaptor(Alloc& allocator) noexcept
:
m_allocator(allocator)
{
}
template<typename U>
STLAdaptor(const STLAdaptor<U, Alloc>& other) noexcept
:
m_allocator(other.m_allocator)
{}
[[nodiscard]] constexpr T* allocate(std::size_t n)
{
return reinterpret_cast<T*>
(m_allocator.Allocate(n * sizeof(T)));
}
constexpr void deallocate(T* p, [[maybe_unused]] std::size_t n)
{
m_allocator.Free(p);
}
std::size_t MaxAllocationSize() const
{
return m_allocator.GetMaxMem();
}
bool operator==(const STLAdaptor<T,Alloc>& rhs)
{
return m_allocator.GetStart() == rhs.m_allocator.GetStart();
}
bool operator!=(const STLAdaptor<T,Alloc>& rhs)
{
return !(*this == rhs);
}
Alloc& m_allocator;
};
template<typename T, typename Alloc>
using CustomDeque = std::deque<T, STLAdaptor<T,Alloc>>;
template<typename T, typename Alloc>
using CustomQueue = std::queue<T, CustomDeque<T, Alloc>>;
The issue I'm facing:
int main()
{
CustomAlloc customAlloc(3000000);
CustomDeque<int, CustomAlloc> customDeque(customAlloc);
CustomQueue<int, CustomAlloc> customQueue(STLAdaptor<int, CustomAlloc>{customAlloc});
return 0;
}
This works perfectly fine, but it's code-gore to have to pass in the STLAdaptor<int, CustomAlloc>{customAlloc} where all other STL Containers can rely on the converting constructor for the STLAdaptor.
What I'd like to do is:
int main()
{
CustomAlloc customAlloc(3000000);
CustomDeque<int, CustomAlloc> customDeque(customAlloc);
CustomQueue<int, CustomAlloc> customQueue(customAlloc);
return 0;
}
Where the customAlloc is somehow passed to the underlying std::deque.
If you can't change CustomAlloc code as Ted Lyngmo suggested, the alternative is to define CustomQueue as a subclass of std::queue:
template<typename T, typename Alloc>
struct CustomQueue : std::queue<T, CustomDeque<T, Alloc>>
{
CustomQueue(Alloc& alloc) :std::queue<T, CustomDeque<T, Alloc>>(STLAdaptor<T, Alloc>(alloc)) {}
};
Now you can construct it by passing merely the allocator:
int main()
{
CustomAlloc customAlloc(3000000);
CustomDeque<int, CustomAlloc> customDeque(customAlloc);
CustomQueue<int, CustomAlloc> customQueue(customAlloc);
customQueue.push(5);
return 0;
}
This code was tested on VS 2019.

Can't use CRTP for inner class

I'm trying to use CRTP for common functionality of several iterators types. As stated here
Using inner class with CRTP it's impossible to use CRTP for inner class, so I move the base iterator class out of the container class and inherit it inside the container for the iterator. But I still got
error: invalid use of incomplete type 'class ConcreteIteratorBase<Node<int>, std::iterator_traits<Node<int>*>, std::iterator<std::forward_iterator_tag, Node<int>, long int, Node<int>*, Node<int>&> >'
error.
Here is the minimal reproducible example:
#include <algorithm>
#include <functional>
#include <iostream>
#include <iterator>
#include <type_traits>
#include <utility>
template <typename T>
class NotEqFromEqMixin : public T
{
public:
bool operator!=(T const & other) const
{
return !(static_cast<T const &>(*this) == other);
}
};
template <typename Iterator, typename Traits, typename StdBase>
class IteratorHelperMixin : public NotEqFromEqMixin<Iterator>, public StdBase
{
public:
using pointer = typename Traits::pointer;
using reference = typename Traits::reference;
pointer operator->()
{
return &**this;
}
};
template <typename NodeT, typename TraitsT, typename StdBaseT>
class ConcreteIteratorBase
: public IteratorHelperMixin<ConcreteIteratorBase<NodeT, TraitsT, StdBaseT>, TraitsT, StdBaseT>
{
public:
using Super = IteratorHelperMixin<ConcreteIteratorBase<NodeT, TraitsT, StdBaseT>, TraitsT, StdBaseT>;
using typename Super::pointer;
using typename Super::reference;
ConcreteIteratorBase(pointer node) : node(node) {}
reference operator*() noexcept { return *node; }
bool operator==(ConcreteIteratorBase const & other) const noexcept { return node == other.node; }
protected:
pointer node;
};
template <typename Value>
class Node
{
public:
~Node() { delete next; }
Node* next = nullptr;
Value value;
};
template <typename Value>
class List
{
public:
using NodeType = Node<Value>;
void push(Value value)
{
NodeType* newNode = new NodeType();
newNode->value = value;
newNode->next = head;
head = newNode;
}
class iterator :
public ConcreteIteratorBase<
Node<Value>,
std::iterator_traits<Node<Value>*>,
std::iterator<std::forward_iterator_tag, Node<Value>>
>
{
using Super = ConcreteIteratorBase<
Node<Value>,
std::iterator_traits<Node<Value>*>,
std::iterator<std::forward_iterator_tag, Node<Value>>
>;
public:
iterator(NodeType* ptr = nullptr) : Super(ptr){}
iterator& operator++()
{
if (this->node)
{
this->node = this->node->next;
}
return *this;
}
Value& operator*()
{
return this->node->value;
}
bool operator==(const iterator& other) const
{
return this->node == other.node;
}
};
iterator begin()
{
return iterator{head};
}
iterator end()
{
return iterator{};
}
NodeType* head = nullptr;
};
int main(int , char**)
{
List<int> list;
for (int i = 0; i < 10; ++i)
{
list.push(i);
}
for(auto & val : list)
{
std::cout << val << " ";
}
std::cout << std::endl;
return 0;
}
Your issue is that you misuse CRTP as you do a circular inheritance:
ConcreteIteratorBase
-> IteratorHelperMixin<ConcreteIteratorBase, ..> -> NotEqFromEqMixin<ConcreteIteratorBase>
-> ConcreteIteratorBase
You should drop the inheritance here
template <typename T>
class NotEqFromEqMixin //: public T
// ^^^^^^^^^^^^
{
public:
bool operator!=(T const & other) const
{
return !(static_cast<T const &>(*this) == other);
}
};
Demo

How to use rebind with custom allocator and custom list

I implemented my custom allocator and custom list container, which supports this allocator. They are defined like so:
template <typename T, size_t num_of_blocks = 16>
class MyAllocator {
public:
template <typename U>
struct rebind {
using other = MyAllocator<U, num_of_blocks>;
}
...dozens of standard methods
}
template <typename T, typename MyAlloc = std::allocator<Node<T>>>
class MyList {
private:
MyAlloc allocator;
...different methods which use allocator member
}
It all works good. So, in my client code, I can do it like:
auto my_list = MyList<int, MyAllocator<Node<int>,10>>{};
It works without errors and I see, that my custom allocator is used. But I do not like the way I use my custom allocator. In fact, I want my client code look like:
auto my_list = MyList<int, MyAllocator<int,10>>{};
My first attempt was this:
template <typename T, typename MyAlloc = std::allocator<T>>
class MyList {
private:
//MyAlloc allocator; // remove this member and rebind allocator to another one
typedef typename MyAlloc::template rebind<Node<T>>::other node_alloc_type;
node_alloc_type allocator; // I expect that my allocator now is of type MyAllocator<Node<T>, num_of_blocks>
... all the rest left unchanged
}
When however I run my new client code:
auto my_list = MyList<int, MyAllocator<int,10>>{};
I get these error messages:
can not convert 'int*' to 'Node*'in assignment
I'm not sure what I'm doing wrong and what I'm missing. So, how can I fix it? And what is the right way to use rebind in custom container?
EDIT
That is how my custom container looks like now:
//MyList.h
#include <memory>
template<typename T>
struct Node
{
Node(): m_next(nullptr){}
Node(T const &t):
m_value(t),
m_next(nullptr)
{}
T m_value;
Node* m_next;
};
template<typename T, typename MyAllocator = std::allocator<Node<T>>>
class MyList
{
private:
Node<T>* m_head = nullptr;
Node<T>* m_tail = nullptr;
MyAllocator my_allocator;
public:
class Iterator
{
private:
Node<T>* m_Node;
public:
Iterator(Node<T>* Node): m_Node(Node) {};
bool operator==(const Iterator& other)
{
return this == &other || m_Node == other.m_Node;
}
bool operator!=(const Iterator& other)
{
return !operator==(other);
}
T operator*()
{
if (m_Node)
{
return m_Node->m_value;
}
return T();
}
Iterator operator++()
{
Iterator i = *this;
if (m_Node)
{
m_Node = m_Node->m_next;
}
return i;
}
};
template<typename... Args>
void emplace(T v)
{
auto new_Node = my_allocator.allocate(1);
my_allocator.construct(new_Node, v);
if (m_head)
{
m_tail->m_next = new_Node;
} else {
m_head = new_Node;
new_Node->m_next = nullptr;
}
m_tail = new_Node;
}
Iterator begin() const
{
return Iterator(m_head);
}
Iterator end() const
{
return Iterator(nullptr);
}
};
At this moment there is no rebinding and I have to define it like
template<typename T, typename MyAllocator = std::allocator<Node<T>>>
class MyList
What I want is to define it like so:
template<typename T, typename MyAllocator = std::allocator<T>>
class MyList
EDIT
Here is a client code with standard allocator:
//main.cpp
#include "MyList.h"
int main()
{
MyList<int, std::allocator<Node<int>>> my_list;
//auto my_list = MyList<int, std::allocator<int>>; // will not work
for (int i = 0; i < 10; ++i)
{
my_list.emplace(i);
}
return 0;
}
These are the requirements for an allocator: http://en.cppreference.com/w/cpp/concept/Allocator
Notice that template rebind is optional.
Here is a list of what a container must have in order to qualify for the concept. http://en.cppreference.com/w/cpp/concept/AllocatorAwareContainer
Yes, gasp. I have searched in vain for a simple, or at least minimalist example. If all you need is a linked list, and you can use C++11 or later, use std::forward_list.
The following works in the example given.
template<typename T, typename MyAllocator = std::allocator<T>>
class MyList
{
private:
using node_alloc_t = typename std::allocator_traits<MyAllocator>::
template rebind_alloc<Node<T>>;
// create an object of type node allocator
node_alloc_t node_alloc;
// etc ....
public:
template<typename T>
void emplace(T v)
{
Node<T>* new_Node = node_alloc.allocate(1);
// Etc...
}
// etc...
};
All together now...
#include <memory>
#include <iostream>
template<typename T>
struct Node
{
Node() : m_next(nullptr) {}
Node(T const &t) :
m_value(t),
m_next(nullptr)
{}
T m_value;
Node<T>* m_next;
};
template<typename T, typename MyAllocator = std::allocator<T>>
class MyList
{
private:
using node_alloc_t = typename std::allocator_traits<MyAllocator>::
template rebind_alloc<Node<T>>;
// create an object of type node allocator
node_alloc_t node_alloc;
public:
class Iterator
{
private:
Node<T>* m_Node;
public:
Iterator(Node<T>* Node) : m_Node(Node) {};
bool operator==(const Iterator& other)
{
return this == &other || m_Node == other.m_Node;
}
bool operator!=(const Iterator& other)
{
return !operator==(other);
}
T operator*()
{
if (m_Node)
{
return m_Node->m_value;
}
return T();
}
Iterator operator++()
{
Iterator i = *this;
if (m_Node)
{
m_Node = m_Node->m_next;
}
return i;
}
};
template<typename T>
void emplace(T v)
{
Node<T>* new_Node = node_alloc.allocate(1);
node_alloc.construct(new_Node, v);
if (m_head)
{
m_tail->m_next = new_Node;
}
else {
m_head = new_Node;
new_Node->m_next = nullptr;
}
m_tail = new_Node;
}
Iterator begin() const
{
return Iterator(m_head);
}
Iterator end() const
{
return Iterator(nullptr);
}
};
int main()
{
MyList<int> my_list;
for (int i = 0; i < 10; ++i)
{
my_list.emplace(i);
}
for (auto i : my_list) {
std::cout << i << std::endl;
}
return 0;
}

C++ -- why we have to define this friend template class

template<class T>
class auto_ptr2 {
public:
explicit auto_ptr2(T *p = 0): pointee(p) {}
template<class U>
auto_ptr2(auto_ptr2<U>& rhs): pointee(rhs.release()) {}
~auto_ptr2() { delete pointee; }
template<class U>
auto_ptr2<T>& operator=(auto_ptr2<U>& rhs)
{
if (this != &rhs) reset(rhs.release());
return *this;
}
T& operator*() const { return *pointee; }
T* operator->() const { return pointee; }
T* get() const { return pointee; }
T* release()
{
T *oldPointee = pointee;
pointee = 0;
return oldPointee;
}
void reset(T *p = 0)
{
if (pointee != p) {
delete pointee;
pointee = p;
}
}
private:
T *pointee;
//template<class U> friend class auto_ptr2<U>;
// Question 1> Why we have to define this friend class
// Question 2> I cannot compile this code with above line with VS2010.
// Error 1 error C3772: 'auto_ptr2<T>' : invalid friend template declaration
};
thank you
Why we have to define this friend class
I'm fairly sure you don't; as far as I can see, nothing is referencing the private member of a different template instantiation. You would need it if the copy constructor or assignment operator manipulated rhs.pointee directly, rather than just calling rhs.release().
I cannot compile this code with above line with VS2010.
The declaration should be:
template<class U> friend class auto_ptr2;

clone_ptr problem, I need to create a copy object using a function of the library instead of new

I am a bit new to templates in C++ so forgive me if this question is confusing or stupid, I just have a problem where I want to implement a clone smart pointer so I don't have to create copy constructors for each and every class that uses my underlying XML library that only seems to use object pointers and not smart pointers. The problem is that my traits need to create the new objects using functions from the underlying library and I do not know how I would go about doing that in a template/traits class. I have posted all the code with some comments below, if anybody could advice, I'd appreciate it.
If something is unclear, please ask and I will try to clarify.
#ifndef CLONE_PTR_H
#define CLONE_PTR_H
#include <algorithm>
#include <functional>
#include <xercesc/dom/DOM.hpp>
#include <xercesc/dom/DOMDocument.hpp>
struct DOMObject_cloner
{
static DOMDocument* clone(DOMDocument* pPtr)
{
DOMImplementation* impl = DOMImplementationRegistry::getDOMImplementation(X("Core")); // this looks wrong, depends on DOMIMplementation_cloner being done really... how do I do this properly
return pPtr ? : impl->createDocument(...) //I need this function for a DOMDocument* to be created!!!
}
};
struct DOMImplementation_cloner
{
static DOMImplementation* clone(DOMImplementation* pPtr)
{
return pPtr ? DOMImplementationRegistry::getDOMImplementation(X("Core")) : 0;
}
};
template<typename T>
struct default_clone
{
static T* clone(T* pPtr)
{
return pPtr ? pPtr->clone() : 0;
}
};
template <typename T, typename Cloner = default_clone<T> >
class clone_ptr
{
public:
// types
typedef T element_type;
typedef element_type value_type;
typedef const element_type const_value_type;
typedef value_type* pointer;
typedef const_value_type* const_pointer;
typedef value_type& reference;
typedef const_value_type& const_reference;
// creation
clone_ptr() :
mPtr(0)
{}
explicit clone_ptr(pointer pPtr) :
mPtr(pPtr)
{}
clone_ptr(const clone_ptr& pOther) :
mPtr(pOther.get() ? Cloner()(pOther.get()) : 0)
{}
/*clone_ptr(const clone_ptr& pOther) :
mPtr(pOther.get() ? pOther->clone() : 0),
{}*/
clone_ptr& operator=(clone_ptr pOther)
{
swap(*this, pOther);
return *this;
}
~clone_ptr()
{
delete get();
}
// observers
pointer get() const
{
return mPtr;
}
pointer operator->() const
{
return get();
}
reference operator*() const
{
assert(get() != 0);
return *get();
}
// modifiers
pointer release()
{
pointer result = mPtr;
mPtr = 0;
return result;
}
void reset(pointer pPtr = 0)
{
*this = clone_ptr(pPtr);
}
// utility
friend void swap(clone_ptr& pFirst, clone_ptr& pSecond)
{
std::swap(pFirst.mPtr, pSecond.mPtr);
}
private:
pointer mPtr;
//default_clone Cloner;
};
template <typename T1>
bool operator!(const clone_ptr<T1>& pX)
{
return pX.get() == 0;
};
template <typename T1, typename T2>
bool operator>=(const clone_ptr<T1>& pFirst, const clone_ptr<T2>& pSecond)
{
return !(pFirst < pSecond);
};
// compare
template <typename T1, typename T2>
bool operator==(const clone_ptr<T1>& pFirst, const clone_ptr<T2>& pSecond)
{
return pFirst.get() == pSecond.get();
};
template <typename T1, typename T2>
bool operator!=(const clone_ptr<T1>& pFirst, const clone_ptr<T2>& pSecond)
{
return !(pFirst == pSecond);
};
template <typename T1, typename T2>
bool operator<(const clone_ptr<T1>& pFirst, const clone_ptr<T2>& pSecond)
{
return std::less<void*>()(pFirst.get(), pSecond.get());
};
template <typename T1, typename T2>
bool operator<=(const clone_ptr<T1>& pFirst, const clone_ptr<T2>& pSecond)
{
return !(pFirst > pSecond);
};
template <typename T1, typename T2>
bool operator>(const clone_ptr<T1>& pFirst, const clone_ptr<T2>& pSecond)
{
return pSecond < pFirst;
};
#endif
I am not really sure if I understand your question, but I see one thing wrong with your code. DOMObject_cloner and DOMImplementation_cloner should be specializations of default_clone, like this:
template<>
struct default_clone<DOMDocument> {
static DOMDocument* clone(DOMDocument* pPtr)
{
DOMImplementation* impl = DOMImplementationRegistry::getDOMImplementation(X("Core"));
return pPtr ? : impl->createDocument(...);
}
};
Template specialization is the whole point of traits in C++.