clone_ptr implementation error - c++

I have the following implementation of a clone_ptr in an attempt to make safe copies of object pointers that need to be copied in a class, so instead of using the copy constructor, I was adviced to use smart pointers and create a clone pointer.
Clone_ptr implementation:
#include <algorithm>
#include <functional>
#include <xercesc/dom/DOM.hpp>
struct DOMImplementation_cloner
{
template <typename T>
T* operator()(T* pPtr) const
{
/* your clone code*/.
T = DOMImplementationRegistry::getDOMImplementation(X("Core"));
}
};
struct default_clone
{
template <typename T>
T* operator()(T* pPtr) const
{
return pPtr->clone();
}
};
template <typename T, typename Cloner = default_clone>
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() ? mCloner(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);
}
/////////////////////
// compare
template <typename T1, typename T2>
friend bool operator==(const clone_ptr<T1>& pFirst, const clone_ptr<T2>& pSecond)
{
return pFirst.get() == pSecond.get();
}
template <typename T1, typename T2>
friend bool operator!=(const clone_ptr<T1>& pFirst, const clone_ptr<T2>& pSecond)
{
return !(pFirst == pSecond);
}
template <typename T1, typename T2>
friend 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>
friend bool operator<=(const clone_ptr<T1>& pFirst, const clone_ptr<T2>& pSecond)
{
return !(pFirst > pSecond);
}
template <typename T1, typename T2>
friend bool operator>(const clone_ptr<T1>& pFirst, const clone_ptr<T2>& pSecond)
{
return pSecond < pFirst;
}
template <typename T1, typename T2>
friend bool operator>=(const clone_ptr<T1>& pFirst, const clone_ptr<T2>& pSecond)
{
return !(pFirst < pSecond);
}
template <typename T1>
friend bool operator!(const clone_ptr<T1>& pX)
{
return pX.get() == 0;
}
private:
pointer mPtr;
default_clone mCloner;
};
/// Use of a xerces pointer so it can be copied/cloned safely
private class member:
clone_ptr<DOMImplementation, DOMImplementation_cloner> impl;
//compiler error:
error C2039: 'clone' : is not a member of 'xercesc_3_1::DOMImplementation'
I don't understand why it is not using the DOMImplementation_cloner and tries to use the default_clone instead?
Can someone clarify as to what I'm doing wrong?

The type of the cloner is hard-coded to default_clone, instead of using the template parameter Cloner (see the last line of your class definition).
Edit Just to make sure you understand, your definition of mCloner should look like this:
Cloner mCloner;
This way the cloner will actually be of the type given by the template parameter.
One more thing. If you expect you clone_ptr to be used in a more general setting (e.g. by coworkers on other projects), you should make the type of the cloner a property of the type T. This can be done using type traits (this answer gives an example). This could look like this in your clone_ptr:
template< typename T, typename Cloner = cloner_traits< T >::cloner_type >
class clone_ptr {
// ...
};
This would have the same default behavior as your current implementation. The advantage is that classes that require a special cloner would just specialize the cloner_traits template and the user of the class does not have to worry about choosing the appropriate cloner. If you still want to override the cloner for any class, you can still pass the cloner manually.

I recommend using clone_ptr class in following link:
http://www.codeproject.com/KB/stl/clone_ptr.aspx
The above type of clone pointer does not need to have a clone function.
There are other advantages to this type of clone pointer class, over above type implementation.
See article for more details.

Related

Catch nondeterminism by error'ing on iterating pointer-keyed maps

It's been a few times we've found nondeterministic issues in the codebase I'm working on, and so far it's almost been root caused to the use of std::[unordered_]map/set<T*,U>, where the key is a pointer, combined with iteration on the map, usually in the form of a range-based for loop (since pointer values may change between executions, iteration order is nondeterministic).
I was wondering if there was some black template magic one could use to inject a static_assert when begin() is called on such a container. I think begin() is the best place to do this, or maybe iterator::operator++, since constructing iterators otherwise, such as a result of find(), is okay.
I thought I could overload std::begin, but the rules for range-based for loops state that .begin() is used if it exists. So, I'm out of ideas. Is there a clever trick to do this?
Further clarification: No custom comparator is involved, the direct value of the pointer (aka the address of the target object) is the key. This is fine for insertion and lookup, and only becomes a problem when iterating over the container since the order is based on unpredictable pointer values. I'm trying to find existing cases like this in a large existing codebase.
You can almost achieve the desired behavior with partial specializations:
20.5.4.2.1 The behavior of a C++ program is undefined if it adds declarations or definitions to namespace std or to a namespace within namespace std unless otherwise specified. A program may add a template specialization for any standard library template to namespace std only if the declaration depends on a user-defined type and the specialization meets the standard library requirements for the original template and is not explicitly prohibited.
Therefore, a simple specialization for std::map can be used to detect attempts to instantiate the template with a pointer key type:
#include <map>
namespace internal
{
// User-defined type trait
template<class Key, class T>
class DefaultAllocator
{
public:
using type = std::allocator<std::pair<const Key, T>>;
};
// Effectively the same as std::allocator, but a different type
template<class T>
class Allocator2 : public std::allocator<T> {};
}
namespace std
{
// Specialization for std::map with a pointer key type and the default allocator.
// The class inherits most of the implementation from
// std::map<Key*, T, Compare, ::internal::Allocator2<std::pair<Key*, T>>>
// to mimic the standard implementation.
template<class Key, class T, class Compare>
class map<Key*, T, Compare, typename ::internal::DefaultAllocator<Key*, T>::type> :
public map<Key*, T, Compare, ::internal::Allocator2<std::pair<Key*, T>>>
{
using base = map<Key*, T, Compare, ::internal::Allocator2<std::pair<Key*, T>>>;
using base::iterator;
using base::const_iterator;
public:
// Overload begin() and cbegin()
iterator begin() noexcept
{
static_assert(false, "OH NOES, A POINTER");
}
const_iterator begin() const noexcept
{
static_assert(false, "OH NOES, A POINTER");
}
const_iterator cbegin() const noexcept
{
static_assert(false, "OH NOES, A POINTER");
}
};
}
int main()
{
std::map<int, int> m1;
std::map<int*, int> m2;
// OK, not a specialization
m1[0] = 42;
for (auto& keyval : m1)
{
(void)keyval;
}
m2[nullptr] = 42; // Insertion is OK
for (auto& keyval : m2) // static_assert failure
{
(void)keyval;
}
}
However,
I haven't figured out a way to extend this for custom allocators: the declaration of the specialization has to depend on some user-defined type.
This is a terrible kludge, so I would only use it to find existing cases (rather than keeping as a static checker).
One approach to achieve a compile time failure for designated pointer types is to delete std::less, std::greater, std::hash, etc specializations for the specific pointer types that are susceptible to non-deterministic behavior (i.e. returned by interfaces). There are many options to provide "safe" functionality for pointer collections.
The following is a comprehensive example:
#include <cassert>
#include <memory>
#include <set>
#include <map>
#include <unordered_set>
#include <unordered_map>
#define DISABLE_NUMERIC_POINTER_SPECIALIZATIONS(T) \
namespace std { \
template <> struct hash<const T*> { std::size_t operator()(const T* obj) const = delete; }; \
template <> struct hash<T*> { std::size_t operator()(T* obj) const = delete; }; \
template <> struct less<const T*> { bool operator()(const T* lhs, const T* rhs) const = delete; }; \
template <> struct less<T*> { bool operator()(T* lhs, T* rhs) const = delete; }; \
template <> struct greater<const T*> { bool operator()(const T* lhs, const T* rhs) const = delete; }; \
template <> struct greater<T*> { bool operator()(T* lhs, T* rhs) const = delete; }; \
template <> struct less_equal<const T*> { bool operator()(const T* lhs, const T* rhs) const = delete; }; \
template <> struct less_equal<T*> { bool operator()(T* lhs, T* rhs) const = delete; }; \
template <> struct greater_equal<const T*> { bool operator()(const T* lhs, const T* rhs) const = delete; }; \
template <> struct greater_equal<T*> { bool operator()(T* lhs, T* rhs) const = delete; }; \
}
namespace NS {
class C {
public:
explicit C(int id) : m_id{id} {}
int id() const { return m_id; }
private:
int m_id;
};
inline bool operator ==(const C& lhs, const C& rhs) { return lhs.id() == rhs.id(); }
inline bool operator <(const C& lhs, const C& rhs) { return lhs.id() < rhs.id(); }
} // namespace NS
namespace std {
template <> struct hash<NS::C> { std::size_t operator()(const NS::C& obj) const { return obj.id(); } };
}
DISABLE_NUMERIC_POINTER_SPECIALIZATIONS(NS::C)
struct IndirectEqual {
template <typename T>
bool operator()(const T* lhs, const T* rhs) const {
return (lhs && rhs) ? *lhs == *rhs : lhs == rhs;
}
};
struct IndirectLess {
template <typename T>
bool operator()(const T* lhs, const T* rhs) const {
return (lhs && rhs) ? *lhs < *rhs : lhs < rhs;
}
};
struct IndirectGreater {
template <typename T>
bool operator()(const T* lhs, const T* rhs) const {
return (lhs && rhs) ? *lhs > *rhs : lhs > rhs;
}
};
struct IndirectHash {
template <typename T>
std::size_t operator()(const T* ptr) const {
return ptr ? std::hash<T>{}(*ptr) : std::numeric_limits<std::size_t>::max();
}
};
struct BuiltinLess {
template <typename T>
bool operator()(const T& lhs, const T& rhs) const { return lhs < rhs; }
};
struct SPLess {
template <typename T>
bool operator()(const std::shared_ptr<T>& lhs, const std::shared_ptr<T>& rhs) const { return lhs.get() < rhs.get(); }
};
struct BuiltinGreater {
template <typename T>
bool operator()(const T& lhs, const T& rhs) const { return lhs < rhs; };
};
struct PtrHash {
template <typename T>
std::size_t operator()(const T* ptr) const { return static_cast<std::size_t>(ptr); };
};
template <typename T>
class BasicSet : private std::set<T, BuiltinLess> {
public:
using std::set<T, BuiltinLess>::set;
using std::set<T, BuiltinLess>::find;
using std::set<T, BuiltinLess>::insert;
using std::set<T, BuiltinLess>::emplace;
using std::set<T, BuiltinLess>::end;
};
template <typename T>
class BasicSet<std::shared_ptr<T>> : private std::set<std::shared_ptr<T>, SPLess> {
public:
using std::set<std::shared_ptr<T>, SPLess>::set;
using std::set<std::shared_ptr<T>, SPLess>::find;
using std::set<std::shared_ptr<T>, SPLess>::insert;
using std::set<std::shared_ptr<T>, SPLess>::emplace;
using std::set<std::shared_ptr<T>, SPLess>::end;
};
int main()
{
// All of these decls result in a compiler error
// std::set<NS::C*> unsafe_s{new NS::C{1}, new NS::C{2}};
// std::map<NS::C*, int> unsafe_m{ {new NS::C{1}, 100} };
// std::unordered_set<NS::C*> unsafe_us{new NS::C{1}, new NS::C{2}};
// std::unordered_map<NS::C*, int> unsafe_um{ {new NS::C{1}, 123} };
std::set<NS::C*, IndirectLess> s{ new NS::C{1} };
std::unordered_set<NS::C*, IndirectHash> us1{ new NS::C{1} };
std::unordered_set<NS::C*, IndirectHash, IndirectEqual> us2{ new NS::C{1} };
auto c = new NS::C{1};
assert (s.find(c) != s.end());
assert (us1.find(c) == us1.end()); // pointers aren't equal
assert (us2.find(c) != us2.end()); // objects are equal
BasicSet<NS::C*> bs{ new NS::C{1} };
assert (bs.find(c) == bs.end()); // pointers aren't equal
auto sp1 = std::make_shared<NS::C>(10);
auto sp2 = std::make_shared<NS::C>(20);
BasicSet<std::shared_ptr<NS::C>> spset{sp1, sp2};
assert(spset.find(sp1) != spset.end());
return 0;
}
Note: This isn't perfect. E.G., one would need to disable 'volatile T*' and 'const volatile T*' variations. I'm sure there are other issues.

Error with template operator overloading

I wrote a class and I wanted to implement an iterator for it ( as shown in the following code ). I needed to overload a variety of operators and I faced the error mentioned below:
class BaseClass
{
virtual ~BaseClass() {}
};
template<class T>
class AbstractBaseOrgan: public BaseClass
{
public:
typedef T value;
template<class TT>
class AbstractBaseIterator:
public std::iterator<std::random_access_iterator_tag,
typename std::iterator_traits<TT>::value_type>
{
protected:
TT _M_current;
const TT&
base() const
{ return this->_M_current; }
};
protected:
value te;
};
template<typename Iter>
inline bool
operator<(const typename AbstractBaseOrgan<typename
std::iterator_traits<Iter>::value_type>::template
AbstractBaseIterator<Iter>& lhs,
const typename AbstractBaseOrgan<typename
std::iterator_traits<Iter>::value_type>::template
AbstractBaseIterator<Iter>& rhs)
{ return lhs.base() < rhs.base(); }
int main()
{
AbstractBaseOrgan<int>::AbstractBaseIterator<int*> temp;
AbstractBaseOrgan<int>::AbstractBaseIterator<int*> temp2;
int ttemp;
if(operator< (temp,temp2))
ttemp = 0;
return 0;
}
Compiling it gives me the following error:
error: no matching function for call to ‘operator<(AbstractBaseOrgan<int>::AbstractBaseIterator<int*>&, AbstractBaseOrgan<int>::AbstractBaseIterator<int*>&)’
Any idea what might cause this?
4 In most cases, the types, templates, and non-type values that are
used to compose P participate in template argument deduction. That is,
they may be used to determine the value of a template argument, and
the value so determined must be consistent with the values determined
elsewhere. In certain contexts, however, the value does not
participate in type deduction, but instead uses the values of template
arguments that were either deduced elsewhere or explicitly specified.
If a template parameter is used only in non-deduced contexts and is
not explicitly specified, template argument deduction fails.
The non-deduced contexts are:
— The nested-name-specifier of a type that was specified using a qualified-id.
You can avoid this by few ways. First way - make operator < friend for class AbstractIteratorBase, or its member.
template<class TT>
class AbstractBaseIterator:
public std::iterator<std::random_access_iterator_tag,
typename std::iterator_traits<TT>::value_type>
{
public:
template<typename Iter>
friend bool operator < (const AbstractBaseIterator<Iter>& lhs, const AbstractBaseIterator<Iter>& rhs)
{
return lhs.base() < rhs.base();
}
protected:
TT _M_current;
const TT&
base() const
{ return this->_M_current; }
};
Second variant is define AbstractBaseIterator class not in template class. And then typedef AbstractBaseIterator<T> iterator; in AbstractBaseOrgan. If you can use C++11 you can use something like this.
class BaseClass
{
virtual ~BaseClass() {}
};
template<class TT>
class AbstractBaseIterator:
public std::iterator<std::random_access_iterator_tag,
typename std::iterator_traits<TT>::value_type>
{
protected:
TT _M_current;
const TT&
base() const
{ return this->_M_current; }
};
template<typename Iter>
bool operator < (const AbstractBaseIterator<Iter>& lhs, const AbstractBaseIterator<Iter>& rhs)
{
return lhs.base() < rhs.base();
}
template<class T>
class AbstractBaseOrgan: public BaseClass
{
public:
typedef T value;
template<typename TT>
using iterator = AbstractBaseIterator<TT>;
protected:
value te;
};
int main()
{
AbstractBaseOrgan<int>::iterator<int*> temp;
AbstractBaseOrgan<int>::iterator<int*> temp2;
int ttemp;
if(operator< (temp,temp2))
ttemp = 0;
return 0;
}

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++.

Using STL algorithms (specifically std::sort) from within a templated class

I've declared a template class MyContainer as bellow, then created an instance of it of type DataType1. The DataType1 class provides a friend function "DataSpecificComparison" which is used by std::sort to compare DataType1 objects. The program compiled and sorted correctly.
I then defined a class called DataType2, gave it a friend implementation of "DataSpecificComparison" and used it to create another instance of MyContainer.
I am now unable to compile the program as a "C2914: 'std::sort' : cannot deduce template argument as function argument is ambiguous" compile time error is reported.
How can a developer specify that the DataSpecificComparison binary predicate is to take arguments of template type T*? Or is there another way around this issue?
template <class T>
class MyContainer
{
private:
vector<T*> m_vMyContainerObjects;
....
public:
....
void SortMyContainerObjects()
{
std::sort(m_vMyContainerObjects.begin(), m_vMyContainerObjects.end(), DataSpecificComparison)
}
}
class DataType1
{
....
friend bool DataSpecificComparison(const DataType1 * lhs, const DataType1 * rhs)
}
class DataType2
{
....
friend bool DataSpecificComparison(const DataType2* lhs, const DataType2* rhs)
}
You can use a temporary local function pointer variable of the required type to select the correct overload of DataSpecificComparison:
void SortMyContainerObjects()
{
typedef bool (*comparer_t)(const T*, const T*);
comparer_t cmp = &DataSpecificComparison;
std::sort(m_vMyContainerObjects.begin(), m_vMyContainerObjects.end(), cmp);
}
Here the compiler can deduce that you want to use the DataSpecificComparison overload that matches the comparer_t type, which resolves the ambiguity.
sth already gave a correct answer, but there's also a direct alternative based on the same principle:
void SortMyContainerObjects()
{
std::sort(m_vMyContainerObjects.begin(), m_vMyContainerObjects.end(),
static_cast<bool (*comparer_t)(const T*, const T*)>(&DataSpecificComparison));
}
This uses essentially the same mechanism. The cast forces overload resolution to happen before the Template Argument Deduction for std::sort.
template<typename T>
struct DataSpecificComp : public binary_function<T, T, bool>
{
public:
bool operator()(const T* lhs, const T* rhs)
{
return *lhs < *rhs;
}
};
call the sort function as shown below:
sort(vi.begin(), vi.end(), DataSpecificComp<int>());
I'd prefer something along the following lines: by default it compares objects with less_than (so you wouldn't have to remember to provide a function with a funny name), and there's an overload that allows giving your own comparison functor (again, value-based):
#include <vector>
#include <algorithm>
#include <functional>
template <class T, class Func>
struct indirect_binary_call_type: public std::binary_function<const T*, const T*, bool>
{
Func f;
indirect_binary_call_type(Func f): f(f) {}
bool operator()(const T* a, const T* b) const
{
return f(*a, *b);
}
};
template <class T, class Func>
indirect_binary_call_type<T, Func> indirect_binary_call(Func f)
{
return indirect_binary_call_type<T, Func>(f);
}
template <class T>
class MyContainer
{
private:
std::vector<T*> m_vMyContainerObjects;
public:
void Sort()
{
Sort(std::less<T>());
}
template <class Func>
void Sort(Func f )
{
std::sort(m_vMyContainerObjects.begin(), m_vMyContainerObjects.end(), indirect_binary_call<T>(f));
}
};
int main()
{
MyContainer<int> m;
m.Sort();
m.Sort(std::greater<int>());
}
Did you try defining DataSpecificComparison as template with bunch of specializations and giving it the type?
template<T>
bool DataSpecificComparison(const T* t1, const T* t2)
{
// something non compilable here
}
template<> bool DataSpecificComparison<Data1>(const Data1* t1, const Data1* t2)
{
// return *t1 < *t2;
}
....
void SortMyContainerObjects()
{
std::sort(m_vMyContainerObjects.begin(), m_vMyContainerObjects.end(), DataSpecificComparison<T>)
}
....
Templating DataSpecificComparison should work. You can also specifically call the proper std::sort template, but it's a bit cumbersome:
template <class T>
class MyContainer
{
private:
vector<T*> m_vMyContainerObjects;
typedef bool (*compsT)(T, T);
public:
....
void SortMyContainerObjects()
{
std::sort<std::vector<T*>::iterator, compsT>(m_vMyContainerObjects.begin(), m_vMyContainerObjects.end(), DataSpecificComparison);
}
}

Why doesn't this C++ STL allocator allocate?

I'm trying to write a custom STL allocator that is derived from std::allocator, but somehow all calls to allocate() go to the base class. I have narrowed it down to this code:
template <typename T> class a : public std::allocator<T> {
public:
T* allocate(size_t n, const void* hint = 0) const {
cout << "yo!";
return 0;
}
};
int main()
{
vector<int, a<int>> v(1000, 42);
return 0;
}
I expect "Yo!" to get printed, followed by some horrible error because I don't actually allocate anything. Instead, the program runs fine and prints nothing. What am I doing wrong?
I get the same results in gcc and VS2008.
You will need to provide a rebind member template and the other stuff that is listed in the allocator requirements in the C++ Standard. For example, you need a template copy constructor which accepts not only allocator<T> but also allocator<U>. For example, one code might do, which a std::list for example is likely to do
template<typename Allocator>
void alloc1chunk(Allocator const& alloc) {
typename Allocator::template rebind<
wrapper<typename Allocator::value_type>
>::other ot(alloc);
// ...
}
The code will fail if there either exist no correct rebind template, or there exist no corresponding copy constructor. You will get nowhere useful with guessing what the requirements are. Sooner or later you will have to do with code that relies on one part of those allocator requirements, and the code will fail because your allocator violates them. I recommend you take a look at them in some working draft your your copy of the Standard in 20.1.5.
In this case, the problem is that I didn't override the rebind member of the allocator. This version works (in VS2008):
template <typename T> class a : public std::allocator<T> {
public:
T* allocate(size_t n, const void* hint = 0) const {
cout << "yo!";
return 0;
}
template <typename U> struct rebind
{
typedef a<U> other;
};
};
int main() {
vector<int, a<int>> v(1000, 42);
return 0;
}
I found this by debugging through the STL headers.
Whether this works or not will be completely dependent on the STL implementation though, so I think that ultimately, Klaim is right in that this shouldn't be done this way.
I have two templates for creating customized allocators; the first works automagically if it is used on a custom type:
template<>
class std::allocator<MY_TYPE>
{
public:
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef MY_TYPE* pointer;
typedef const MY_TYPE* const_pointer;
typedef MY_TYPE& reference;
typedef const MY_TYPE& const_reference;
typedef MY_TYPE value_type;
template <class U>
struct rebind
{
typedef std::allocator<U> other;
};
pointer allocate(size_type n, std::allocator<void>::const_pointer hint = 0)
{
return reinterpret_cast<pointer>(ALLOC_FUNC(n * sizeof(T)));
}
void construct(pointer p, const_reference val)
{
::new(p) T(val);
}
void destroy(pointer p)
{
p->~T();
}
void deallocate(pointer p, size_type n)
{
FREE_FUNC(p);
}
size_type max_size() const throw()
{
// return ~size_type(0); -- Error, fixed according to Constantin's comment
return std::numeric_limits<size_t>::max()/sizeof(MY_TYPE);
}
};
The second is used when we want to have our own allocator for a predefined type with a standard allocator, for instance char, wchar_t, std::string, etc.:
namespace MY_NAMESPACE
{
template <class T> class allocator;
// specialize for void:
template <>
class allocator<void>
{
public:
typedef void* pointer;
typedef const void* const_pointer;
// reference to void members are impossible.
typedef void value_type;
template <class U>
struct rebind
{
typedef allocator<U> other;
};
};
template <class T>
class allocator
{
public:
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef T* pointer;
typedef const T* const_pointer;
typedef T& reference;
typedef const T& const_reference;
typedef T value_type;
template <class U>
struct rebind
{
typedef allocator<U> other;
};
allocator() throw()
{
}
template <class U>
allocator(const allocator<U>& u) throw()
{
}
~allocator() throw()
{
}
pointer address(reference r) const
{
return &r;
}
const_pointer address(const_reference r) const
{
return &r;
}
size_type max_size() const throw()
{
// return ~size_type(0); -- Error, fixed according to Constantin's comment
return std::numeric_limits<size_t>::max()/sizeof(T);
}
pointer allocate(size_type n, allocator<void>::const_pointer hint = 0)
{
return reinterpret_cast<pointer>(ALLOC_FUNC(n * sizeof(T)));
}
void deallocate(pointer p, size_type n)
{
FREE_FUNC(p);
}
void construct(pointer p, const_reference val)
{
::new(p) T(val);
}
void destroy(pointer p)
{
p->~T();
}
};
template <class T1, class T2>
inline
bool operator==(const allocator<T1>& a1, const allocator<T2>& a2) throw()
{
return true;
}
template <class T1, class T2>
inline
bool operator!=(const allocator<T1>& a1, const allocator<T2>& a2) throw()
{
return false;
}
}
The first template above, for your own defined type, does not require any further handling but is used automatically by the standard container classes. The second template requires further work when used on a standard type. For std::string, for example, one have to use the following construct when declaring variables of that type (it is simplest with a typedef):
std::basic_string<char>, std::char_traits<char>, MY_NAMESPACE::allocator<char> >
The following code prints "yo" as expected - what you were seeing was our old friend "undefined behaviour".
#include <iostream>
#include <vector>
using namespace std;
template <typename T> class a : public std::allocator<T> {
public:
T* allocate(size_t n, const void* hint = 0) const {
cout << "yo!";
return new T[10000];
}
};
int main()
{
vector<int, a<int> > v(1000, 42);
return 0;
}
Edit: I just checked out the C++ Standard regarding the default allocator. There is no prohibition on inheriting from it. In fact, as far as I'm aware, there is no such prohibition in any part of the Standard.