425 lines
9.2 KiB
C++
425 lines
9.2 KiB
C++
#include <rcp.h>
|
|
#include <cassert>
|
|
#include <map>
|
|
#include <unordered_map>
|
|
#include <vector>
|
|
|
|
static int mybase_construct;
|
|
static int mybase_destruct;
|
|
static int myderived_construct;
|
|
static int myderived_destruct;
|
|
|
|
class MyBase
|
|
{
|
|
rcp_managed_root(MyBase);
|
|
|
|
protected:
|
|
MyBase(int x, int y)
|
|
{
|
|
this->x = x;
|
|
this->y = y;
|
|
mybase_construct++;
|
|
}
|
|
|
|
virtual ~MyBase()
|
|
{
|
|
mybase_destruct++;
|
|
}
|
|
|
|
public:
|
|
int x;
|
|
int y;
|
|
};
|
|
typedef rcp<MyBase> MyB;
|
|
|
|
class MyDerived : public MyBase
|
|
{
|
|
rcp_managed(MyDerived);
|
|
|
|
protected:
|
|
MyDerived(double v) : MyBase(1, 2)
|
|
{
|
|
this->v = v;
|
|
myderived_construct++;
|
|
}
|
|
|
|
virtual ~MyDerived()
|
|
{
|
|
myderived_destruct++;
|
|
}
|
|
|
|
public:
|
|
double v;
|
|
};
|
|
|
|
void test_class_hierarchy()
|
|
{
|
|
{
|
|
rcp<MyBase> mybase = MyBase::create(4, 5);
|
|
rcp<MyDerived> myderived = MyDerived::create(42.5);
|
|
}
|
|
assert(mybase_construct == 2);
|
|
assert(mybase_destruct == 2);
|
|
assert(myderived_construct == 1);
|
|
assert(myderived_destruct == 1);
|
|
}
|
|
|
|
void test_dereference()
|
|
{
|
|
rcp<MyBase> mybase = MyBase::create(2, 3);
|
|
assert(mybase->x == 2);
|
|
assert(mybase->y == 3);
|
|
assert((*mybase).x == 2);
|
|
assert((*mybase).y == 3);
|
|
rcp<MyBase> mybase2 = mybase;
|
|
assert(mybase == mybase2);
|
|
assert(!(mybase != mybase2));
|
|
}
|
|
|
|
void test_booleans()
|
|
{
|
|
rcp<MyBase> mybase = MyBase::create(2, 3);
|
|
assert(mybase);
|
|
assert(mybase != nullptr);
|
|
rcp<MyDerived> myderived;
|
|
assert(!myderived);
|
|
assert(myderived == nullptr);
|
|
}
|
|
|
|
void test_create()
|
|
{
|
|
MyB myb = MyB::create(8, 9);
|
|
assert(myb->x == 8);
|
|
}
|
|
|
|
struct MyObj
|
|
{
|
|
rcp_managed_root(MyObj);
|
|
public:
|
|
int v = {42};
|
|
template <typename R>
|
|
void add_to(R & receiver)
|
|
{
|
|
receiver.add(this);
|
|
}
|
|
};
|
|
struct Receiver
|
|
{
|
|
std::vector<rcp<MyObj>> objects;
|
|
void add(MyObj * o)
|
|
{
|
|
objects.push_back(rcp<MyObj>(o));
|
|
}
|
|
};
|
|
|
|
void test_multi_construct_from_raw_pointers()
|
|
{
|
|
Receiver r;
|
|
auto myo = MyObj::create();
|
|
for (int i = 0; i < 5; i++)
|
|
{
|
|
myo->add_to(r);
|
|
}
|
|
for (int i = 0; i < 5; i++)
|
|
{
|
|
assert(r.objects[i]->v == 42);
|
|
}
|
|
}
|
|
|
|
struct EventListener;
|
|
static std::vector<rcp<EventListener>> g_listeners;
|
|
|
|
struct EventListener
|
|
{
|
|
rcp_managed_root(EventListener);
|
|
public:
|
|
int events_received = 0;
|
|
void attach() { g_listeners.push_back(this); }
|
|
};
|
|
|
|
void test_listener_self_registration()
|
|
{
|
|
g_listeners.clear();
|
|
auto a = EventListener::create();
|
|
auto b = EventListener::create();
|
|
a->attach();
|
|
b->attach();
|
|
for (auto & l : g_listeners) l->events_received++;
|
|
assert(a->events_received == 1);
|
|
assert(b->events_received == 1);
|
|
g_listeners.clear();
|
|
}
|
|
|
|
void test_copy_assignment_decrements_previous_reference()
|
|
{
|
|
MyB myb = MyB::create(12, 13);
|
|
MyB myb2 = MyB::create(14, 15);
|
|
myb = myb2;
|
|
assert(myb->x == 14);
|
|
}
|
|
|
|
void test_move_constructor()
|
|
{
|
|
int constructed_before = mybase_construct;
|
|
int destructed_before = mybase_destruct;
|
|
{
|
|
MyB a = MyB::create(1, 2);
|
|
MyB b = std::move(a);
|
|
assert(!a);
|
|
assert(b);
|
|
assert(b->x == 1);
|
|
assert(mybase_destruct == destructed_before);
|
|
}
|
|
assert(mybase_construct == constructed_before + 1);
|
|
assert(mybase_destruct == destructed_before + 1);
|
|
}
|
|
|
|
void test_move_assignment()
|
|
{
|
|
int constructed_before = mybase_construct;
|
|
int destructed_before = mybase_destruct;
|
|
{
|
|
MyB a = MyB::create(1, 2);
|
|
MyB b;
|
|
b = std::move(a);
|
|
assert(!a);
|
|
assert(b);
|
|
assert(b->x == 1);
|
|
assert(mybase_destruct == destructed_before);
|
|
}
|
|
assert(mybase_construct == constructed_before + 1);
|
|
assert(mybase_destruct == destructed_before + 1);
|
|
}
|
|
|
|
void test_move_assignment_same_object()
|
|
{
|
|
MyB a = MyB::create(1, 2);
|
|
MyB b = a;
|
|
assert(a.use_count() == 2);
|
|
b = std::move(a);
|
|
assert(!a);
|
|
assert(b);
|
|
assert(b->x == 1);
|
|
assert(b.use_count() == 1);
|
|
}
|
|
|
|
void test_move_assignment_releases_existing()
|
|
{
|
|
int constructed_before = mybase_construct;
|
|
int destructed_before = mybase_destruct;
|
|
{
|
|
MyB a = MyB::create(1, 2);
|
|
MyB b = MyB::create(3, 4);
|
|
b = std::move(a);
|
|
assert(!a);
|
|
assert(b->x == 1);
|
|
assert(mybase_destruct == destructed_before + 1);
|
|
}
|
|
assert(mybase_construct == constructed_before + 2);
|
|
assert(mybase_destruct == destructed_before + 2);
|
|
}
|
|
|
|
void test_cross_type_comparison()
|
|
{
|
|
rcp<MyDerived> derived = MyDerived::create(1.5);
|
|
rcp<MyBase> base = derived;
|
|
assert(base == derived);
|
|
assert(!(base != derived));
|
|
rcp<MyBase> other = MyBase::create(1, 2);
|
|
assert(base != other);
|
|
assert(!(base == other));
|
|
}
|
|
|
|
void test_reset()
|
|
{
|
|
int destructed_before = mybase_destruct;
|
|
MyB p = MyB::create(1, 2);
|
|
p.reset();
|
|
assert(!p);
|
|
assert(mybase_destruct == destructed_before + 1);
|
|
}
|
|
|
|
void test_upcast()
|
|
{
|
|
rcp<MyDerived> derived = MyDerived::create(1.5);
|
|
rcp<MyBase> base = derived;
|
|
assert(base);
|
|
assert(base->x == 1);
|
|
}
|
|
|
|
void test_move_upcast()
|
|
{
|
|
int constructed_before = mybase_construct;
|
|
int destructed_before = mybase_destruct;
|
|
{
|
|
rcp<MyDerived> derived = MyDerived::create(1.5);
|
|
rcp<MyBase> base = std::move(derived);
|
|
assert(!derived);
|
|
assert(base);
|
|
assert(base->x == 1);
|
|
assert(mybase_destruct == destructed_before);
|
|
}
|
|
assert(mybase_construct == constructed_before + 1);
|
|
assert(mybase_destruct == destructed_before + 1);
|
|
}
|
|
|
|
void test_dynamic_cast_success()
|
|
{
|
|
rcp<MyDerived> derived = MyDerived::create(1.5);
|
|
rcp<MyBase> base = derived;
|
|
rcp<MyDerived> back = rcp_dynamic_cast<MyDerived>(base);
|
|
assert(back);
|
|
assert(back->v == 1.5);
|
|
}
|
|
|
|
void test_dynamic_cast_move_success()
|
|
{
|
|
int constructed_before = mybase_construct;
|
|
int destructed_before = mybase_destruct;
|
|
{
|
|
rcp<MyBase> base = MyDerived::create(1.5);
|
|
rcp<MyDerived> derived = rcp_dynamic_cast<MyDerived>(std::move(base));
|
|
assert(!base);
|
|
assert(derived);
|
|
assert(derived->v == 1.5);
|
|
assert(mybase_destruct == destructed_before);
|
|
}
|
|
assert(mybase_construct == constructed_before + 1);
|
|
assert(mybase_destruct == destructed_before + 1);
|
|
}
|
|
|
|
void test_dynamic_cast_move_failure()
|
|
{
|
|
rcp<MyBase> base = MyBase::create(1, 2);
|
|
rcp<MyDerived> derived = rcp_dynamic_cast<MyDerived>(std::move(base));
|
|
assert(!derived);
|
|
assert(base);
|
|
}
|
|
|
|
void test_dynamic_cast_failure()
|
|
{
|
|
rcp<MyBase> base = MyBase::create(1, 2);
|
|
rcp<MyDerived> derived = rcp_dynamic_cast<MyDerived>(base);
|
|
assert(!derived);
|
|
}
|
|
|
|
/* Simulate a external class that cannot be modified */
|
|
namespace external
|
|
{
|
|
class Counter
|
|
{
|
|
public:
|
|
int start;
|
|
int count;
|
|
Counter(int start) : start(start), count(start) {}
|
|
virtual ~Counter() {}
|
|
};
|
|
}
|
|
|
|
static int external_destruct;
|
|
|
|
/* Create a managed wrapper for the external class. */
|
|
class Counter : public external::Counter
|
|
{
|
|
rcp_managed_root(Counter);
|
|
protected:
|
|
Counter(int start) : external::Counter(start) {}
|
|
~Counter() { external_destruct++; }
|
|
};
|
|
|
|
void test_ordering()
|
|
{
|
|
MyB a = MyB::create(1, 2);
|
|
MyB b = MyB::create(3, 4);
|
|
MyB a2 = a;
|
|
assert(!(a < a2));
|
|
assert(a <= a2);
|
|
assert(!(a > a2));
|
|
assert(a >= a2);
|
|
assert((a < b) != (b < a));
|
|
assert((a < b) == (b > a));
|
|
assert((a <= b) == (b >= a));
|
|
std::map<MyB, int> m;
|
|
m[a] = 1;
|
|
m[b] = 2;
|
|
assert(m[a2] == 1);
|
|
assert(m[b] == 2);
|
|
}
|
|
|
|
void test_use_count()
|
|
{
|
|
MyB a = MyB::create(1, 2);
|
|
assert(a.use_count() == 1);
|
|
{
|
|
MyB b = a;
|
|
assert(a.use_count() == 2);
|
|
assert(b.use_count() == 2);
|
|
}
|
|
assert(a.use_count() == 1);
|
|
MyB empty;
|
|
assert(empty.use_count() == 0);
|
|
}
|
|
|
|
void test_swap()
|
|
{
|
|
MyB a = MyB::create(1, 2);
|
|
MyB b = MyB::create(3, 4);
|
|
a.swap(b);
|
|
assert(a->x == 3);
|
|
assert(b->x == 1);
|
|
}
|
|
|
|
void test_hash()
|
|
{
|
|
MyB a = MyB::create(1, 2);
|
|
MyB b = a;
|
|
std::unordered_map<MyB, int> map;
|
|
map[a] = 42;
|
|
assert(map[b] == 42);
|
|
MyB c = MyB::create(3, 4);
|
|
assert(map.find(c) == map.end());
|
|
}
|
|
|
|
void test_external_class()
|
|
{
|
|
int before = external_destruct;
|
|
{
|
|
auto a = Counter::create(10);
|
|
auto b = Counter::create(20);
|
|
assert(a->count == 10);
|
|
assert(b->count == 20);
|
|
a->count++;
|
|
assert(a->count == 11);
|
|
}
|
|
assert(external_destruct == before + 2);
|
|
}
|
|
|
|
int main(int argc, char * argv[])
|
|
{
|
|
test_class_hierarchy();
|
|
test_dereference();
|
|
test_booleans();
|
|
test_create();
|
|
test_multi_construct_from_raw_pointers();
|
|
test_listener_self_registration();
|
|
test_copy_assignment_decrements_previous_reference();
|
|
test_cross_type_comparison();
|
|
test_reset();
|
|
test_move_constructor();
|
|
test_move_assignment();
|
|
test_move_assignment_same_object();
|
|
test_move_assignment_releases_existing();
|
|
test_upcast();
|
|
test_move_upcast();
|
|
test_dynamic_cast_success();
|
|
test_dynamic_cast_failure();
|
|
test_dynamic_cast_move_success();
|
|
test_dynamic_cast_move_failure();
|
|
test_ordering();
|
|
test_use_count();
|
|
test_swap();
|
|
test_hash();
|
|
test_external_class();
|
|
return 0;
|
|
}
|