I am currently working on a piece of code in which lots of pointers are used without proper ownership control. In some cases, it becomes a huge contraint because everything has to be initialized properly and can't be changed afterwards. So far I have been using dummy wrappers
// MyObject.h
struct MyObject
{
virtual int myMethod(int i){ return i; }
};
struct MyObjectWrapper : MyObject
{
MyObject *obj = nullptr;
int myMethod(int i){ if(obj) return obj->myMethod(i); }
void setObject(MyObject *obj){ this->obj = obj; }
}
// MyObjectFactory.h
MyObject *createMyObject(){
MyObject *object = new MyObject();
MyObjectWrapper *wrapper = new MyObjectWrapper();
wrapper->setObject(object);
return wrapper;
}
I am wondering if there is a way to do pointer "spoofing" by returning an object that is not actually a pointer but has the type of one, so that I can return a smart pointer instead of the regular pointer without changing the rest of the code. Is there a way of achieving that?
Additionnally, this led me to think about boost::optional. How is it implemented? Is boost using a lot of preprocessing for reflection? I cannot understand how it is possible to "intercept" a method call on a variable without neither knowing the name of the method nor using heavy preprocessing.
In case the answers are simple NOs, are you aware of any design pattern that can be used to work around my issues?
Ya know. It's time for the "Don't Defend Against Macchiavelli¹" talk. You'll always be able to know the address of an object if you have access to the object.
If you don't trust your users for that, don't give them access.
This is why the e.g. the OS kernel gives out opaque handles (aside from technical reasons).
In case your question was "Can I automatically create a proxy object" the answer is NO:
not without code generation tools
not without overloadable operator. (which doesn't currently exist outside of language proposals)
¹ e.g. http://www.gotw.ca/gotw/076.htm
You want a type that can store a dumb pointer or a smart pointer relatively transparently? Well, this isn't a good plan, but it can be done:
namespace details {
template<template<class...>class Z, class, class...Ts>
struct can_apply:std::false_type {};
template<template<class...>class Z, class...Ts>
struct can_apply<Z, std::void_t<Z<Ts...>>, Ts...>:std::true_type{};
}
template<template<class...>class Z, class...Ts>
using can_apply = details::can_apply<Z,void,Ts...>;
template<class T>
using dot_get_r = decltype( std::declval<T>().get() );
template<class T>
using can_get = can_apply< dot_get_r, T >;
template<class T>
struct ptr_like {
T* get()const{
if (!pvtable && !pimpl) return nullptr;
return pvtable->get(pimpl.get());
}
explicit operator bool() const{ return !!get(); }
// wall of == <= etc:
friend bool operator==( ptr_like const& ptr, std::nullptr_t ) { return !(bool)ptr; }
friend bool operator==( std::nullptr_t, ptr_like const& ptr ) { return !(bool)ptr; }
friend bool operator!=( ptr_like const& ptr, std::nullptr_t ) { return (bool)ptr; }
friend bool operator!=( std::nullptr_t, ptr_like const& ptr ) { return (bool)ptr; }
friend bool operator==( ptr_like const& lhs, ptr_like const& rhs ) {return lhs.get()==rhs.get();}
friend bool operator!=( ptr_like const& lhs, ptr_like const& rhs ) {return lhs.get()!=rhs.get();}
friend bool operator<=( ptr_like const& lhs, ptr_like const& rhs ) {return lhs.get()<=rhs.get();}
friend bool operator>=( ptr_like const& lhs, ptr_like const& rhs ) {return lhs.get()>=rhs.get();}
friend bool operator<( ptr_like const& lhs, ptr_like const& rhs ) {return lhs.get()<rhs.get();}
friend bool operator>( ptr_like const& lhs, ptr_like const& rhs ) {return lhs.get()>rhs.get();}
template<class U,
std::enable_if_t< std::is_convertible<U*, T*>{}, int > =0
>
friend bool operator==( ptr_like const& lhs, ptr_like<U> const& rhs ) { return lhs.get()==rhs.get(); }
template<class U,
std::enable_if_t< std::is_convertible<U*, T*>{}, int > =0
>
friend bool operator==( ptr_like<U> const& lhs, ptr_like const& rhs ) { return lhs.get()==rhs.get(); }
template<class U,
std::enable_if_t< std::is_convertible<U*, T*>{}, int > =0
>
friend bool operator!=( ptr_like const& lhs, ptr_like<U> const& rhs ) { return lhs.get()!=rhs.get(); }
template<class U,
std::enable_if_t< std::is_convertible<U*, T*>{}, int > =0
>
friend bool operator!=( ptr_like<U> const& lhs, ptr_like const& rhs ) { return lhs.get()!=rhs.get(); }
template<class U,
std::enable_if_t< std::is_convertible<U*, T*>{}, int > =0
>
friend bool operator<=( ptr_like const& lhs, ptr_like<U> const& rhs ) { return lhs.get()<=rhs.get(); }
template<class U,
std::enable_if_t< std::is_convertible<U*, T*>{}, int > =0
>
friend bool operator<=( ptr_like<U> const& lhs, ptr_like const& rhs ) { return lhs.get()<=rhs.get(); }
template<class U,
std::enable_if_t< std::is_convertible<U*, T*>{}, int > =0
>
friend bool operator>=( ptr_like const& lhs, ptr_like<U> const& rhs ) { return lhs.get()>=rhs.get(); }
template<class U,
std::enable_if_t< std::is_convertible<U*, T*>{}, int > =0
>
friend bool operator>=( ptr_like<U> const& lhs, ptr_like const& rhs ) { return lhs.get()>=rhs.get(); }
template<class U,
std::enable_if_t< std::is_convertible<U*, T*>{}, int > =0
>
friend bool operator<( ptr_like const& lhs, ptr_like<U> const& rhs ) { return lhs.get()<rhs.get(); }
template<class U,
std::enable_if_t< std::is_convertible<U*, T*>{}, int > =0
>
friend bool operator<( ptr_like<U> const& lhs, ptr_like const& rhs ) { return lhs.get()<rhs.get(); }
template<class U,
std::enable_if_t< std::is_convertible<U*, T*>{}, int > =0
>
friend bool operator>( ptr_like const& lhs, ptr_like<U> const& rhs ) { return lhs.get()>rhs.get(); }
template<class U,
std::enable_if_t< std::is_convertible<U*, T*>{}, int > =0
>
friend bool operator>( ptr_like<U> const& lhs, ptr_like const& rhs ) { return lhs.get()>rhs.get(); }
// -> and unary * support:
T* operator->()const{ return get(); }
T& operator*()const{ return *get(); }
// move:
ptr_like(ptr_like&&)=default;
ptr_like& operator=(ptr_like&&)=default;
// copy:
ptr_like(ptr_like const& o):
pvtable(o.pvtable),
pimpl(o.clone())
{}
ptr_like& operator=(ptr_like const& o) {
auto tmp = o;
swap(*this, tmp);
return *this;
}
// swap:
friend void swap( ptr_like& lhs, ptr_like& rhs ) {
using std::swap;
swap(lhs.vtable, rhs.vtable);
swap(lhs.pimpl, rhs.pimpl);
}
// Construct from smart pointer:
template<class Smart,
class dSmart = std::decay_t<Smart>,
std::enable_if_t< can_get<dSmart const&>{} && !std::is_same<dSmart, ptr_like>{}, int > =0
>
ptr_like( Smart&& smart ):
pvtable( smart_vtable<dSmart>() ),
pimpl( unique_ptr_with_deleter(std::forward<Smart>(smart)) )
{}
// construct from dumb pointer:
template<class Dumb>
ptr_like( Dumb* dumb ):
pvtable( pointer_vtable<Dumb>() ),
pimpl( unique_ptr_with_deleter(dumb) )
{}
private:
using state = std::unique_ptr<void, void(*)(void*)>;
struct vtable {
T* (*get)(void*) = 0;
state (*clone)(state const&) = 0;
};
vtable const* pvtable = nullptr;
state pimpl;
state clone() const {
if (!pimpl||!pvtable) return {nullptr, [](void*){}};
return pvtable->clone( pimpl );
}
template<class U,
class dU = std::decay_t<U>
>
static state unique_ptr_with_deleter(U&& u) {
std::unique_ptr<void, void(*)(void*)> r
= {
new dU(std::forward<U>(u)),
[](void* ptr){
delete static_cast<dU*>(ptr);
}
};
return r;
}
template<class U>
static vtable const* pointer_vtable() {
static vtable const table = make_ptr_vtable<U>();
return &table;
}
template<class O>
static vtable const* smart_vtable() {
static vtable const table = make_smart_vtable<O>();
return &table;
}
template<class U>
static vtable make_ptr_vtable() {
return {
[](void* ptr)->T*{
return *static_cast<U**>(ptr);
},
[](state const& ptr)->state{
return {ptr.get(), ptr.get_deleter()};
}
};
}
template<class O>
static vtable make_smart_vtable() {
return {
[](void* ptr)->T*{
O* o = static_cast<O*>(ptr);
return o?o->get():nullptr;
},
[](state const& ptr)->state{
O* o = static_cast<O*>(ptr.get());
if (!o) return {nullptr, ptr.get_deleter()};
return {new O(*o), ptr.get_deleter()};
}
};
}
};
note that unique_ptr does not qualify, as it cannot be copied while dumb pointers can. Custom smart pointers (that can be copied) can be used. We could extend this to having it throw/terminate if you pass it a non-copyable smart pointer and that value is copied instead of moved, if you where even more insane than this answer.
Live example. Code is C++1z, and no I'm not making it C++11 or 14, it is a dumb idea to start with.
Code is incomplete -- nullptr < p doesn't work, for example. Code is less than perfectly efficient (I could store dumb pointers more efficiently, and use SBO even for smart pointers). Needs std::less specialization or have < use it.
But it is a proof of concept.
int foo( ptr_like<int> ptr ) {
if (!ptr) return -1;
return *ptr+1;
}
will compile. The ptr_like<int> could be an int* or a std::shared_ptr<int> it won't care. You can persist the ptr_like<int> and copy it around. Copies of shared_ptr<int>s will maintain reference counts, copies of int* won't.
If you use this, you'll end up with a code base that is harder to maintain, not easier, as object lifetime will get more confusing, not less.
Related
We have a huge legacy code base which is multithreaded and uses vectors extensively. To cut down the time spent in dynamic memory allocation, we are moving to a pools. The plan is to use Boost small vector with a custom allocator. The custom allocator will create a thread local pool per each container type. I have implemented a custom allocator based on the above idea and tested it. For some reason, the code falls in an infinite pattern inside find_prev method in Boost simple segregated storage. There are lots of places where there is a nesting of containers, like vector>> etc. Is this the right way of defining allocator ??
template<typename T, typename allocatorType>
class customAllocator
{
public:
static thread_local allocatorType *_allocator;
typedef T value_type;
typedef allocatorType allocator_Type;
template <class X> struct rebind
{
typedef customAllocator<X, allocatorType> other;
};
customAllocator()
{
_allocator = new allocatorType;
assert(_allocator);
return;
}
~customAllocator()
{
delete _allocator;
_allocator = nullptr;
return;
}
template<class X, class Y> customAllocator(const customAllocator<X, Y>& other)
{
_allocator = other._allocator;
return;
}
template<class X, class Y> customAllocator(customAllocator<X, Y>&& other)
{
_allocator = other._allocator;
other._allocator = nullptr;
return;
}
template<class X, class Y> customAllocator& operator=(const customAllocator<X, Y>& other)
{
_allocator = other._allocator;
return *this;
}
template<class X, class Y> customAllocator& operator=(customAllocator<X, Y>&& other)
{
_allocator = other._allocator;
other._allocator = nullptr;
return *this;
}
T* allocate(size_t n)
{
return _allocator->allocate(n * sizeof(T));
}
void deallocate(T* ptr, size_t n)
{
_allocator->deallocate(ptr, n);
return;
}
template<class X, class Y> bool operator==(const customAllocator<X, Y>& other) const noexcept
{ return (*this._allocator == other.allocator); }
template<class X, class Y> bool operator!=(const customAllocator<X, Y>& other) const noexcept
{ return !(*this._allocator == other._allocator); }
};
template <typename T1, typename T2>
thread_local T2 *customAllocator<T1, T2>::_allocator = nullptr;
using smallVector = boost::container::small_vector<
T,
DEFAULT_SMALL_VECTOR_LENGTH,
customAllocator<T,
boost::pool_allocator<
T,
boost::default_user_allocator_new_delete,
boost::details::pool::null_mutex,
2,
4
>>>;
i'm new to lambda functions in c++ and am trying to make a simple one but have some problems. i've tried to make a heterogeneous container which include stacks, queues, and lists and one of the exercise is to make a lambda function which check if an element answers a specific condition defined as:
using Condition = bool (*)(T const&);
so here is a piece of my heterogeneous container:
For example for stacks:
template <typename T>
using Condition1 = bool (*)(T const&);
template <typename T>
class LinkedStack {
private:
StackElement<T>* top;
public:
LinkedStack();
LinkedStack(LinkedStack const&);
LinkedStack& operator=(LinkedStack const&);
bool empty() const;
bool member(T const& x);
T peek() const;
void push(T const&);
T pop();
~LinkedStack();
};
template<typename T,typename Condition1>
bool q_filter(Condition1 func,LinkedStack<T>& s){
LinkedStack<T> tmp;
tmp = s;
if((tmp).empty())
return false;
while(!tmp.empty()){
if (func(tmp.peek()))
return true;
else
tmp.pop();
}
return false;
}
the stack-object(which is need to perform object in h-container):
template <typename T>
class Object {
public:
using Condition = bool (*)(T const&);
virtual bool insert(T const&) = 0;
virtual bool remove(T&) = 0;
virtual bool member(T const&) = 0;
virtual int l_size() = 0;
virtual void sort() = 0;
virtual bool special_condition(Condition);
virtual void print(ostream& os) const = 0;
virtual ~Object() {}
};
template <typename T>
class StackObject : public Object<T>, private LinkedStack<T> {
public:
using Condition = bool (*)(T const&);
// включване
bool insert(T const& x) {
LinkedStack<T>::push(x);
return true;
}
// изключване
bool remove(T& x) {
if (LinkedStack<T>::empty())
return false;
x = LinkedStack<T>::pop();
return true;
}
// проверка
bool member(T const& x){
return LinkedStack<T>::member(x);
}
int l_size() {
return my_size(*this);
}
// извеждане
void print(ostream& os) const {
os << *this;
}
void sort(){
s_sort(*this);
}
bool special_condition(Condition c){
return q_filter(c,*this);
}
};
and main-function:
int main(){
QueueStackList qsl;
qsl.read_from_file();
(*(qsl.begin()))->special_condition([](int x) -> bool { return x%2 != 0; });
return 0;
}
QueueStackList is implemented like a linked-list and qsl.begin() returns
an iterator for the first element in the heterogeneous list;
when i compile it returns this kind of errors:
invalid user-defined conversion from 'main()::<lambda(int)>' to 'Object<int>::Condition {aka bool (*)(const int&)}' [-fpermissive]|
candidate is: main()::<lambda(int)>::operator bool (*)(int)() const <near match>|
no known conversion for implicit 'this' parameter from 'bool (*)(int)' to 'Object<int>::Condition {aka bool (*)(const int&)}'|
which i really don't know what mean ?
I would like to have compile-time selection of function pointers.
Something like functionListAutomatic in the following
int funk( int a, int b ) { return a * b / 2; }
template< typename T0, typename T1 >
int null_func( T0 a, T1 b ) { return 0; }
tuple< int( *)(int, int), int( *)(int, float) > functionList {
funk,
null_func<int, float>
};
// Pseudo code.
tuple< int( *)(int, int), int( *)(int, float) > functionListAutomatic {
condition( funk_exist( funk( int, int ) ) , funk, null_func<int, int> ),
condition( funk_exist( funk( int, string ) ), funk, null_func<int, string> ),
};
void main() {
int res0 = get<0>( functionList )(1, 2);
int res1 = get<1>( functionList )(1, 2);
}
I cannot figure out how to do this.
I do know how to make funk_exist so that it evaluates at compile time (I use a variant of this: https://en.wikibooks.org/wiki/More_C++_Idioms/Member_Detector). But the two parameters funk and null_func causes problem. The compiler tries to find a funk( int, string ) function and fails before it evaluates funk_exist(). I need an expression that evaluates funk_exist() and then does not evaluate funk( int, string ) if funk_exist() evaluates to false.
Appreciate your help.
namespace details {
template<class...>struct voider{using type=void;};
template<class...Ts>using void_t=typename voider<Ts...>::type;
template<template<class...>class Z, class, class...Ts>
struct can_apply:std::false_type{};
template<template<class...>class Z, class...Ts>
struct can_apply<Z,void_t<Z<Ts...>>,Ts...>:std::true_type{};
}
template<template<class...>class Z, class...Ts>
using can_apply=details::can_apply<Z,void,Ts...>;
boilerplate injected!1
Adding funk:
template<class...Ts>
funk_r = decltype(funk(std::declval<Ts>()...));
template<class...Ts>
can_funk = can_apply<funk_r, Ts...>;
now we know if we can funk.
but who will funk? The funk_miester:
template<class lhs, class rhs, class=void>
struct funk_miester {
decltype(null_func<lhs,rhs>) operator()()const{
return null_func<lhs, rhs>;
}
};
template<class lhs, class rhs>
struct funk_miester<lhs, rhs, std::enable_if_t< can_funk<lhs,rhs>{} >> {
funk_r<lhs,rhs>(*)(lhs, rhs) operator()()const{
return [](lhs l, rhs r)->funk_r<lhs,rhs> {
return funk(l,r);
};
}
};
and get down below this line to see the result:
tuple< int( *)(int, int), int( *)(int, float) > functionListAutomatic (
funk_miester<int,int>{}(),
funk_miester<int,string>{}()
);
and you are funkey.
Note that I check if funk can be called in can_funk, but you can replace that with whatever trait you want, so long as it generates a compile-time bool.
In my case, the lambda acts as an adapter, so if the signatures don't match it will still generate a function pointer.
1 This just gives me a trait to detect if I can call funk on some arguments. You have your own, so you don't have to use it.
I would like to thank Yakk for his answer. With a few modifications I got that to work, and I have posted the complete program below.
However there were a few snafus.
The use of class template for Z in details pushes the evaluation out of details and out to funk_r. This means that the compiler tries to evaluate a funk( int, string ) which then gives a compile error. Apparently it is no possible to use SFINAE when making aliases with using so I did not find a solution for that.
#pragma once
#include <string>
#include <tuple>
using namespace std;
int funk( int a, int b ) { return a * b; }
template< typename T0, typename T1 >
int null_funk( T0 a, T1 b ) { return 0; }
template< typename... Ts >
struct can_apply {
using Yes = char[2];
using No = char[1];
// SFINAE will take 'Yes' if funk( Us... ) matches. Otherwise it will take 'No'
template< typename... Us >
static Yes & test( decltype(funk( Us()... ))* ); // *1
template< typename U0, typename... Us >
static No & test( U0* );
static constexpr bool value = sizeof( test< Ts... >( nullptr ) ) == sizeof( Yes );
};
template< typename... Ts >
using funk_r = decltype(funk( declval<Ts>()... ));
template< typename... Ts >
using can_funk = can_apply< Ts... >;
template< typename lhs, typename rhs, typename = void >
struct funk_meister {
typedef typename decltype(null_funk<lhs, rhs>( lhs(), rhs() ))(*TFunk)(lhs, rhs);
TFunk operator()() const {
return null_funk<lhs, rhs>;
}
};
template<typename lhs, typename rhs>
struct funk_meister<lhs, rhs, enable_if_t< can_funk<lhs, rhs>::value > > {
typedef typename funk_r<lhs, rhs>( *TFunk )(lhs, rhs);
TFunk operator()() const {
return []( lhs l, rhs r ) -> funk_r<lhs, rhs> {
return funk( l, r );
};
}
};
tuple< int( *)(int, int), int( *)(int, string), int( *)(int, float)> functionList {
funk_meister<int,int>{}(), funk_meister<int,string>{}(), funk_meister<int,float>{}()
};
void test() {
int res0 = get<0>( functionList )(1, 2);
int res1 = get<1>( functionList )(1, "2");
int res2 = get<2>( functionList )(1, 2.5f);
}
*1. It is not possible to use class template here because then the SFINAE evaluation gets pushed out to funk_r<...>.
In std::map, this ends up causing an error when the first object is constructed. I've checked the debugger, and I see that free_list::init() creates the consecutive memory addresses correctly. I'm aware this allocator cannot be used in vector or other related containers, but it's only meant to work with the nodular containers.
I get a run-time error from this in xutility (in VC12), at line 158:
_Container_proxy *_Parent_proxy = _Parent->_Myproxy;
Checking the debugger, it appears that _Parent was never initialized, bringing about the 0xC0000005 run-time error. Why or how it didn't get initialized and why this occurred when the first object was being constructed (after std::map did 3 separate allocations), I do not know.
I would like to have this work with std::map and std::list and the other nodular containers and am not worried about whether it can perform in std::vector, etc.
#include <algorithm>
class free_list {
public:
free_list() {}
free_list(free_list&& other)
: m_next(other.m_next) {
other.m_next = nullptr;
}
free_list(void* data, std::size_t num_elements, std::size_t element_size) {
init(data, num_elements, element_size);
}
free_list& operator=(free_list&& other) {
m_next = other.m_next;
other.m_next = nullptr;
}
void init(void* data, std::size_t num_elements, std::size_t element_size) {
union building {
void* as_void;
char* as_char;
free_list* as_self;
};
building b;
b.as_void = data;
m_next = b.as_self;
b.as_char += element_size;
free_list* runner = m_next;
for (std::size_t s = 1; s < num_elements; ++s) {
runner->m_next = b.as_self;
runner = runner->m_next;
b.as_char += element_size;
}
runner->m_next = nullptr;
}
free_list* obtain() {
if (m_next == nullptr) {
return nullptr;
}
free_list* head = m_next;
m_next = head->m_next;
return head;
}
void give_back(free_list* ptr) {
ptr->m_next = m_next;
m_next = ptr;
}
free_list* m_next;
};
template<class T>
class pool_alloc {
typedef pool_alloc<T> myt;
public:
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
typedef T value_type;
typedef T& reference;
typedef const T& const_reference;
typedef T* pointer;
typedef const T* const_pointer;
typedef std::false_type propagate_on_container_copy_assignment;
typedef std::true_type propagate_on_container_move_assignment;
typedef std::true_type propagate_on_container_swap;
template<class U> struct rebind {
typedef pool_alloc<U> other;
};
~pool_alloc() {
destroy();
}
pool_alloc() : data(nullptr), fl(), capacity(4096) {
}
pool_alloc(size_type capacity) : data(nullptr), fl(), capacity(capacity) {}
pool_alloc(const myt& other)
: data(nullptr), fl(), capacity(other.capacity) {}
pool_alloc(myt&& other)
: data(other.data), fl(std::move(other.fl)), capacity(other.capacity) {
other.data = nullptr;
}
template<class U>
pool_alloc(const pool_alloc<U>& other)
: data(nullptr), fl(), capacity(other.max_size()) {}
myt& operator=(const myt& other) {
destroy();
capacity = other.capacity;
}
myt& operator=(myt&& other) {
destroy();
data = other.data;
other.data = nullptr;
capacity = other.capacity;
fl = std::move(other.fl);
}
static pointer address(reference ref) {
return &ref;
}
static const_pointer address(const_reference ref) {
return &ref;
}
size_type max_size() const {
return capacity;
}
pointer allocate(size_type) {
if (data == nullptr) create();
return reinterpret_cast<pointer>(fl.obtain());
}
void deallocate(pointer ptr, size_type) {
fl.give_back(reinterpret_cast<free_list*>(ptr));
}
template<class... Args>
static void construct(pointer ptr, Args&&... args) {
::new (ptr) T(std::forward<Args>(args)...);
}
static void destroy(pointer ptr) {
ptr->~T();
}
bool operator==(const myt& other) const {
return reinterpret_cast<char*>(data) ==
reinterpret_cast<char*>(other.data);
}
bool operator!=(const myt& other) const {
return !operator==(other);
}
private:
void create() {
data = ::operator new(capacity * sizeof(value_type));
fl.init(data, capacity, sizeof(value_type));
}
void destroy() {
::operator delete(data);
data = nullptr;
}
void* data;
free_list fl;
size_type capacity;
};
template<>
class pool_alloc < void > {
public:
template <class U> struct rebind { typedef pool_alloc<U> other; };
typedef void* pointer;
typedef const void* const_pointer;
typedef void value_type;
};
The problem comes when std::pair is being constructed (in MSVC12 utility at line 214):
template<class _Other1,
class _Other2,
class = typename enable_if<is_convertible<_Other1, _Ty1>::value
&& is_convertible<_Other2, _Ty2>::value,
void>::type>
pair(_Other1&& _Val1, _Other2&& _Val2)
_NOEXCEPT_OP((is_nothrow_constructible<_Ty1, _Other1&&>::value
&& is_nothrow_constructible<_Ty2, _Other2&&>::value))
: first(_STD forward<_Other1>(_Val1)),
second(_STD forward<_Other2>(_Val2))
{ // construct from moved values
}
Even after stepping in, the run-time error occurs, the same as described above with _Parent not being initialized.
I was able to answer my own question through extensive debugging. Apparently, VC12's std::map implementation at least at times will cast an _Alnod (permanent allocator that stays in scope for the life of the map, which is used to allocate and deallocate the nodes in the map, what I'd expect to be what actually calls allocate() and deallocate()) as an _Alproxy, a temporary allocator which creates some sort of object called _Mproxy (or something like that) using allocate(). The problem, though, is that VC12's implementation then lets _Alproxy go out of scope while still expecting the pointer to the allocated object to remain valid, so it is clear then that I would have to use ::operator new and ::operator delete on an object like _Mproxy: using a memory pool that then goes out of scope while a pointer to a location in it remains is what causes the crash.
I came up with what I suppose could be called a dirty trick, a test that is performed when copy-constructing or copy-assigning an allocator to another allocator type:
template<class U>
pool_alloc(const pool_alloc<U>& other)
: data(nullptr), fl(), capacity(other.max_size()), use_data(true) {
if (sizeof(T) < sizeof(U)) use_data = false;
}
I added the bool member use_data to the allocator class, which if true means to use the memory pool and which if false means to use ::operator new and ::operator delete. By default, it is true. The question of its value arises when the allocator gets cast as another allocator type whose template parameter's size is smaller than that of the source allocator; in that case, use_data is set to false. Because this _Mproxy object or whatever it's called is rather small, this fix seems to work, even when using std::set with char as the element type.
I've tested this using std::set with type char in both VC12 and GCC 4.8.1 in 32-bit and have found that in both cases it works. When allocating and deallocating the nodes in both cases, the memory pool is used.
Here is the full source code:
#include <algorithm>
class free_list {
public:
free_list() {}
free_list(free_list&& other)
: m_next(other.m_next) {
other.m_next = nullptr;
}
free_list(void* data, std::size_t num_elements, std::size_t element_size) {
init(data, num_elements, element_size);
}
free_list& operator=(free_list&& other) {
if (this != &other) {
m_next = other.m_next;
other.m_next = nullptr;
}
return *this;
}
void init(void* data, std::size_t num_elements, std::size_t element_size) {
union building {
void* as_void;
char* as_char;
free_list* as_self;
};
building b;
b.as_void = data;
m_next = b.as_self;
b.as_char += element_size;
free_list* runner = m_next;
for (std::size_t s = 1; s < num_elements; ++s) {
runner->m_next = b.as_self;
runner = runner->m_next;
b.as_char += element_size;
}
runner->m_next = nullptr;
}
free_list* obtain() {
if (m_next == nullptr) {
return nullptr;
}
free_list* head = m_next;
m_next = head->m_next;
return head;
}
void give_back(free_list* ptr) {
ptr->m_next = m_next;
m_next = ptr;
}
free_list* m_next;
};
template<class T>
class pool_alloc {
typedef pool_alloc<T> myt;
public:
typedef std::size_t size_type;
typedef std::ptrdiff_t difference_type;
typedef T value_type;
typedef T& reference;
typedef const T& const_reference;
typedef T* pointer;
typedef const T* const_pointer;
typedef std::false_type propagate_on_container_copy_assignment;
typedef std::true_type propagate_on_container_move_assignment;
typedef std::true_type propagate_on_container_swap;
myt select_on_container_copy_construction() const {
return *this;
}
template<class U> struct rebind {
typedef pool_alloc<U> other;
};
~pool_alloc() {
clear();
}
pool_alloc() : data(nullptr), fl(), capacity(4096), use_data(true) {}
pool_alloc(size_type capacity) : data(nullptr), fl(),
capacity(capacity), use_data(true) {}
pool_alloc(const myt& other)
: data(nullptr), fl(), capacity(other.capacity),
use_data(other.use_data) {}
pool_alloc(myt&& other)
: data(other.data), fl(std::move(other.fl)), capacity(other.capacity),
use_data(other.use_data) {
other.data = nullptr;
}
template<class U>
pool_alloc(const pool_alloc<U>& other)
: data(nullptr), fl(), capacity(other.max_size()), use_data(true) {
if (sizeof(T) < sizeof(U)) use_data = false;
}
myt& operator=(const myt& other) {
if (*this != other) {
clear();
capacity = other.capacity;
use_data = other.use_data;
}
}
myt& operator=(myt&& other) {
if (*this != other) {
clear();
data = other.data;
other.data = nullptr;
capacity = other.capacity;
use_data = other.use_data;
fl = std::move(other.fl);
}
return *this;
}
template<class U>
myt& operator=(const pool_alloc<U>& other) {
if (this != reinterpret_cast<myt*>(&other)) {
capacity = other.max_size();
if (sizeof(T) < sizeof(U))
use_data = false;
else
use_data = true;
}
return *this;
}
static pointer address(reference ref) {
return &ref;
}
static const_pointer address(const_reference ref) {
return &ref;
}
size_type max_size() const {
return capacity;
}
pointer allocate(size_type) {
if (use_data) {
if (data == nullptr) create();
return reinterpret_cast<pointer>(fl.obtain());
} else {
return reinterpret_cast<pointer>(::operator new(sizeof(T)));
}
}
void deallocate(pointer ptr, size_type) {
if (use_data) {
fl.give_back(reinterpret_cast<free_list*>(ptr));
} else {
::operator delete(reinterpret_cast<void*>(ptr));
}
}
template<class... Args>
static void construct(pointer ptr, Args&&... args) {
::new ((void*)ptr) value_type(std::forward<Args>(args)...);
}
static void destroy(pointer ptr) {
ptr->~value_type();
}
bool operator==(const myt& other) const {
return reinterpret_cast<char*>(data) ==
reinterpret_cast<char*>(other.data);
}
bool operator!=(const myt& other) const {
return !operator==(other);
}
private:
void create() {
size_type size = sizeof(value_type) < sizeof(free_list*) ?
sizeof(free_list*) : sizeof(value_type);
data = ::operator new(capacity * size);
fl.init(data, capacity, size);
}
void clear() {
::operator delete(data);
data = nullptr;
}
void* data;
free_list fl;
size_type capacity;
bool use_data;
};
template<>
class pool_alloc < void > {
public:
template <class U> struct rebind { typedef pool_alloc<U> other; };
typedef void* pointer;
typedef const void* const_pointer;
typedef void value_type;
};
template<class Container, class Alloc>
void change_capacity(Container& c, typename Alloc::size_type new_capacity) {
Container temp(c, Alloc(new_capacity));
c = std::move(temp);
}
Since the allocator is not automatic-growing (don't know how to make such a thing), I have added the change_capacity() function.
The find method of boost::splay_set that require only the key accepts an argument of type KeyValueCompare to compare objects with the key. To be able to use this, we need to supply two methods of the form:
struct KeyValCompare {
inline bool operator() (const std::int64_t key, const MyType& val) const {
//TODO:
}
inline bool operator() (const MyType& val, const std::int64_t key) const {
//TODO:
}
};
However there is no mention in the documentation about how to implement these. Any pointers?
Found a solution here:
http://boost.cowic.de/rc/pdf/intrusive.pdf
they should return true if key (or key from the value) of lhs is less than the key (or key from the value) of rhs.
I don't see why the comparator would be so complicated. The set just stores elements of MyType, so you need to define a strict weak total ordering on them:
struct Comparator {
bool operator()(MyType const& a, MyType const& b) const;
};
Indeed, the default comparer is std::less<MyType>
E.g. to sort
class MyType : public splay_set_base_hook<>
{
int int_;
public:
MyType(int i) : int_(i) {}
int getValue() const { return int_; }
};
By the value, after reversing the digits (e.g. "431" before "322" because 134<223):
struct CompareReversed {
bool operator()(MyType const& a, MyType const& b) const {
return reversed(a.getValue()) < reversed(b.getValue());
}
private:
static int reversed(int i)
{
auto s = std::to_string(i);
std::reverse(s.begin(), s.end());
return boost::lexical_cast<int>(s);
}
};
See it Live On Coliru:
#include <boost/intrusive/splay_set.hpp>
#include <boost/lexical_cast.hpp>
#include <vector>
#include <algorithm>
using namespace boost::intrusive;
class MyType : public splay_set_base_hook<>
{
int int_;
public:
MyType(int i) : int_(i)
{}
// default ordering
friend bool operator< (const MyType &a, const MyType &b) { return a.int_ < b.int_; }
friend bool operator> (const MyType &a, const MyType &b) { return a.int_ > b.int_; }
friend bool operator== (const MyType &a, const MyType &b) { return a.int_ == b.int_; }
int getValue() const { return int_; }
};
struct CompareReversed {
bool operator()(MyType const& a, MyType const& b) const {
return reversed(a.getValue()) < reversed(b.getValue());
}
private:
static int reversed(int i)
{
auto s = std::to_string(i);
std::reverse(s.begin(), s.end());
return boost::lexical_cast<int>(s);
}
};
#include <iostream>
int main()
{
//typedef splay_set<MyType, compare<std::less<MyType> > > Set;
typedef splay_set<MyType, compare<CompareReversed> > Set;
std::vector<MyType> v { 24, 42, 123, 321 };
Set set;
set.insert(v[0]);
set.insert(v[1]);
set.insert(v[2]);
set.insert(v[3]);
for (auto& el : set)
{
std::cout << el.getValue() << "\n";
}
std::cout << set.count(24) << "\n"; // 1
std::cout << set.count(25) << "\n"; // 0
std::cout << set.count(42) << "\n"; // 1
}
If you want to suppor mixed type comparisons, just supply the overloads, obviously:
struct CompareReversed {
bool operator()(MyType const& a, MyType const& b) const {
return reversed(a.getValue()) < reversed(b.getValue());
}
bool operator()(MyType const& a, int b) const {
return reversed(a.getValue()) < reversed(b);
}
bool operator()(int a, MyType const& b) const {
return reversed(a) < reversed(b.getValue());
}
// ...
};
Thanks sehe for the support.
That is exactly what I am doing there. But please have a look at following sample code which fails.
#include <boost/intrusive/splay_set.hpp>
#include <algorithm>
using namespace boost::intrusive;
class MyClass {
public:
MyClass(const std::int64_t& k)
: key(k) {
}
std::int64_t key;
splay_set_member_hook<> member_hook_;
friend bool operator <(const MyClass& lhs, const MyClass& rhs) {
return lhs.key < rhs.key;
}
friend bool operator >(const MyClass& lhs, const MyClass& rhs) {
return lhs.key > rhs.key;
}
friend bool operator ==(const MyClass& lhs, const MyClass& rhs) {
return lhs.key == rhs.key;
}
};
struct KeyValCompare {
inline bool operator()(const std::int64_t key, const MyClass& val) const {
return key < val.key;
}
inline bool operator()(const MyClass& val, const std::int64_t key) const {
return val.key < key;
}
};
typedef member_hook<MyClass, splay_set_member_hook<>, &MyClass::member_hook_> MemberOption;
typedef splay_set<MyClass, MemberOption, compare<std::greater<MyClass> > > MyClassObjectsType;
TEST(MyClass, test) {
MyClassObjectsType set;
set.insert(*new MyClass(10));
set.insert(*new MyClass(20));
set.insert(*new MyClass(100));
auto ite = set.find(100, KeyValCompare());
ASSERT_TRUE(ite != set.end()); // Fails here
}
If I use std::less instead of std::greater, it passes.
Figured it out:
The greater than operator must be change from:
friend bool operator >(const MyClass& lhs, const MyClass& rhs) {
return lhs.key > rhs.key;
}
to this:
friend bool operator >(const MyClass& lhs, const MyClass& rhs) {
return lhs.key < rhs.key;
}