Skip to content

Instantly share code, notes, and snippets.

@shadowmint
Created April 11, 2015 15:51
Show Gist options
  • Save shadowmint/d49650668e9a74c324a1 to your computer and use it in GitHub Desktop.
Save shadowmint/d49650668e9a74c324a1 to your computer and use it in GitHub Desktop.

Other various files are obviously not included here, because they're not relevant.

#include "option.h"
#include "iterator.h"
using namespace std;
namespace npp {
/// An intrusive linked list for collecting resource instances
template<typename TId, typename TValue>
class Resource {
private:
/// Next item in the chain
unique_ptr<Resource<TId, TValue>> data;
protected:
/// The id this value
TId id;
/// The value on this value
TValue value;
public:
/// Create a new instance and assign the values to it
Resource(TId id, TValue value) {
this->id = id;
this->value = value;
}
/// Destructor
virtual ~Resource() {
}
/// Safely clone a new instance of this
virtual Resource<TId, TValue> *Clone();
/// Overload * to get value
TValue operator*() {
TValue rtn = this->value;
return rtn;
}
/// Check if the id matches
bool Is(TId id) {
return this->id == id;
}
/// Return the next item in the chain
Option<Resource<TId, TValue>*> Next() {
if (data) {
return Some<Resource<TId, TValue>*>(data.get());
}
return None<Resource<TId, TValue>*>();
}
/// Find the final link in this chain of values
Resource<TId, TValue> *Last() {
Resource<TId, TValue> *rtn = this;
while (rtn->Next()) {
rtn = *rtn->Next();
}
return rtn;
}
/// Push a resource onto the chain
void Push(Resource<TId, TValue>* value) {
if (data) {
auto last = Last();
last->Push(value);
}
else {
data = unique_ptr<Resource<TId, TValue>>(value);
}
}
/// Return an iterator to the contents
template<typename TOut>
Iterator<Resource<TId, TValue>*, TOut*> Iter() {
return iter<Resource<TId, TValue>, TOut*>(this, [] (Resource<TId, TValue>* root, std::function< bool(TOut *value) > handler) {
auto tmp = root;
while(tmp) {
if (!handler((TOut *) tmp))
break;
tmp = tmp->Next().Or(NULL);
}
});
}
/// Return an iterator to the contents, by id value
template<typename TOut>
Iterator<Resource<TId, TValue>*, TOut*> Iter(TId query) {
return iter<Resource<TId, TValue>, TOut*>(this, [query] (Resource<TId, TValue>* root, std::function< bool(TOut *value) > handler) {
auto tmp = root;
while(tmp) {
if (tmp->Is(query)) {
if (!handler((TOut *) tmp))
break;
}
tmp = tmp->Next().Or(NULL);
}
});
}
/// Return an iterator to the contents, by custom filter
/// ...oddly, this is actually useful.
template<typename TOut>
Iterator<Resource<TId, TValue>*, TOut*> Iter(std::function< bool(Resource<TId, TValue>*) > filter) {
return iter<Resource<TId, TValue>, TOut*>(this, [filter] (Resource<TId, TValue>* root, std::function< bool(TOut *value) > handler) {
auto tmp = root;
while(tmp) {
if (filter(tmp)) {
if (!handler((TOut *) tmp))
break;
}
tmp = tmp->Next().Or(NULL);
}
});
}
/// Create a new resource chain with a custom filter
Option<Resource<TId, TValue>*> Filter(std::function< bool(Resource<TId, TValue>*) > filter) {
auto iter = this->Iter<Resource<TId, TValue>>(filter);
if (iter.Any()) {
auto index = 0;
auto root = (*iter.First())->Clone();
iter.Each([&] (Resource<TId, TValue>* r) {
if (index != 0) {
root->Push(r->Clone());
}
++index;
});
return Some<Resource<TId, TValue>*>(root);
}
return None<Resource<TId, TValue>*>();
}
};
}
#include <npp/test/test_suite.h>
#include <npp/types.h>
#include <npp/resource.h>
using namespace npp;
using namespace npp::test;
int alloc_counter = 0;
int dealloc_counter = 0;
enum RType {
ThingOne = 1,
ThingTwo = 2
};
class RValue : public Resource<RType, i32> {
public:
RValue(int value) : Resource(ThingOne, value) {
alloc_counter += 1;
}
~RValue() {
dealloc_counter += 1;
}
Resource<RType, i32>* Clone() {
return new RValue(this->value);
}
};
class LValue : public Resource<RType, i32> {
public:
LValue(int value) : Resource(ThingTwo, value) {
alloc_counter += 1;
}
~LValue() {
dealloc_counter += 1;
}
Resource<RType, i32>* Clone() {
return new LValue(this->value);
}
};
void test_alloc_dealloc() {
auto foo = new RValue(100);
delete foo;
ASSERT(alloc_counter == dealloc_counter);
}
void test_push() {
auto counter = alloc_counter;
auto foo = new RValue(100);
foo->Push(new RValue(200));
delete foo;
ASSERT(alloc_counter == counter + 2);
ASSERT(alloc_counter == dealloc_counter);
}
void test_iter() {
auto count = 0;
auto acount = alloc_counter;
auto foo = new RValue(100);
foo->Push(new RValue(101));
foo->Push(new LValue(200));
foo->Push(new LValue(201));
foo->Push(new LValue(202));
foo->Iter<Resource<RType, i32>>().Each([&] (Resource<RType, i32> *v) {
count += 1;
});
delete foo;
ASSERT(count == 5);
ASSERT(alloc_counter == (acount + 5));
ASSERT(dealloc_counter == (acount + 5));
}
void test_iter_id() {
auto l_count = 0;
auto r_count = 0;
auto foo = new RValue(100);
foo->Push(new RValue(101));
foo->Push(new LValue(200));
foo->Push(new LValue(201));
foo->Push(new LValue(202));
foo->Iter<LValue>(ThingTwo).Each([&] (LValue *l) {
l_count += 1;
});
foo->Iter<RValue>(ThingOne).Each([&] (RValue *l) {
r_count += 1;
});
delete foo;
ASSERT(l_count == 3);
ASSERT(r_count == 2);
}
void test_filter_by_function() {
auto foo = new RValue(100);
foo->Push(new RValue(101));
foo->Push(new LValue(100));
foo->Push(new LValue(201));
foo->Push(new LValue(202));
auto filtered = foo->Filter([] (Resource<RType, i32>* value) {
return **value == 100;
});
/*ASSERT(filtered.Some());
ASSERT(*filtered.Count() == 2);*/
delete foo;
}
class Tests: public TestSuite {
public:
Tests() {
TEST(test_alloc_dealloc)
TEST(test_push)
TEST(test_iter)
TEST(test_iter_id)
TEST(test_filter_by_function);
}
};
int main(int argc, char **argv) {
return (new Tests())->Report();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment