signals 2.0

This commit is contained in:
swaits committed 2010-10-02 22:08:50 -07:00
1 parent b849d8c7ce
commit ef1b465179
33 files changed
+537

No files matched your search

File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+56
View File
@@ -0,0 +1,56 @@
#ifndef __horrible_cast_h__
#define __horrible_cast_h__
// horrible_union is used by {safe|dangerous}_horrible_cast to copy one arbitrary type to another
template <typename t_out, typename t_in>
union horrible_union
{
t_in in;
t_out out;
};
// dangerous_horrible_cast uses horrible_union to blindly copy one arbitrary type to another
//
// the data is zeroed out (via memset) before copying, in the case that he input type is smaller
// than the output type
template <typename t_out, typename t_in>
inline t_out dangerous_horrible_cast(t_in in)
{
// instance the union
horrible_union<t_out,t_in> data;
// zero out the output part of the union (only if the input is actually smaller than the output)
if ( sizeof(t_in) < sizeof(t_out) )
{
memset(&data.out,0,sizeof(data.out));
}
// copy the input into the union
data.in = in;
// return the output part out of the union
return data.out;
}
// safe_horrible_cast is exactly like dangerous_horrible_cast, except it statically checks to make
// certain that the output type (t_out) is at least large enough (in size) to store an instance of
// the input type (t_in)
//
// note: if you use safe_horrible_cast to copy an instance of A to B, then you may safely use
// dangerous_horrible_cast to copy an instance of B back to A.
template <typename t_out, typename t_in>
inline t_out safe_horrible_cast(t_in in)
{
// statically (compile-time) test if t_out is at least as big as t_in
typedef int ERROR___horrible_union_is_too_small_to_store_input_type[(sizeof(t_in) <= sizeof(t_out)) ? 1 : -1];
// now just do the regular horrible_cast
return dangerous_horrible_cast<t_out,t_in>(in);
}
#endif // __horrible_cast_h__
+71
View File
@@ -0,0 +1,71 @@
#include <cstdio>
#include <cstring>
#include "slot.h"
#include "signal0.h"
class ParentFoo
{
public:
virtual bool Bar()
{
printf("ParentFoo::Bar!\n");
return false;
}
virtual ~ParentFoo()
{
// empty
}
};
class Foo: public ParentFoo
{
public:
bool Bar()
{
printf("Foo::Bar!\n");
return true;
}
virtual ~Foo()
{
// empty
}
};
Foo f;
bool somefunc()
{
printf("some func\n");
return true;
}
template <class T_class, typename T_method>
slot0_method<T_class,T_method>* make_slot(T_class* c, T_method m)
{
return new slot0_method<T_class,T_method>(c,m);
}
int main(int argc, char** argv)
{
slot0* s = make_slot(&f,&Foo::Bar);
slot0* t = make_slot(&f,&ParentFoo::Bar);
slot0_function<bool> x( somefunc );
slot0* u = &x;
if ( *s == *t )
(*s)();
(*u)();
signal0 sig;
sig.connect(&f,&Foo::Bar);
sig();
}
View File
Whitespace-only changes.
View File
Whitespace-only changes.
+36
View File
@@ -0,0 +1,36 @@
#ifndef __signal0_h__
#define __signal0_h__
#include "signal_base.h"
#include "slot.h"
class signal0: public signal_base
{
public:
/*
signal0();
virtual ~signal0();
*/
void operator() () const
{
// TODO: need a way to iterate the bound signal_base list ...
list_node* cur = head;
while(cur)
{
(cur->slot)();
cur = cur->next;
}
}
template <class T_object, typename T_member>
void connect(T_object* p_object, T_member p_member)
{
bind( slot0_method<T_object,T_member>(p_object,p_member) );
}
};
#include "signal0.inl"
#endif // __signal0_h__
View File
Whitespace-only changes.
+151
View File
@@ -0,0 +1,151 @@
#include <cassert>
#include "signal_base.h"
signal_base::signal_base() : head(0), tail(0)
{
// empty
}
signal_base::~signal_base()
{
// clear the list
clear_list();
}
bool signal_base::bind(const slot_base& slot)
{
// add it to our signal list
return add_to_list(slot);
}
bool signal_base::unbind(const slot_base& slot)
{
// look for corresponding list_node in list
list_node* n = find_in_list(slot);
// see if we found a match
if ( !n )
{
return false; // no match found
}
// take it out of the linked list
remove_from_list(n);
// free it
delete n;
// found a match, success
return true;
}
void signal_base::clear_list()
{
// start at head
list_node* cur = head;
// iterate
while ( cur )
{
// save off next so we can safely delete
list_node* next = cur->next;
// free
delete cur;
// advance
cur = next;
}
// reset head & tail
head = tail = 0;
}
signal_base::list_node* signal_base::find_in_list(const slot_base& slot) const
{
// start search at head of list
list_node* cur = head;
// search list for slot
while ( cur )
{
// see if this is a match
if ( cur->slot == slot )
{
// we have a match, return it
return cur;
}
// advance to next in list
cur = cur->next;
}
// no match
return 0;
}
bool signal_base::add_to_list(const slot_base& slot)
{
// a sanity check
assert((head && tail) || (!head && !tail));
// make a new list_node to hold this slot in the list
list_node* n = new list_node;
if ( !n )
{
return false;
}
// initialize new list_node
n->slot = slot; // copy slot into list_node
n->prev = tail; // prev is tail because we'll put this list_node behind it (i.e. the new tail)
n->next = 0; // next is 0 because we'll put this list_node on the tail
// add to tail
if ( tail )
{
// tail exists, just append
tail->next = n; // insert item after current tail
tail = n; // now have a new tail
}
else
{
// list is currently empty, add this new list_node
head = tail = n; // now have a single list_node, which is also both the head and tail
}
return true;
}
void signal_base::remove_from_list(list_node* n)
{
// do a little sanity checking
assert((head && tail) || (!head && !tail));
assert(n->prev == 0 || n->prev->next == n);
assert(n->next == 0 || n->next->prev == n);
// if this is the head, set the new head
if ( n == head )
{
head = n->next;
}
// if this is the tail, set the new tail
if ( n == tail )
{
tail = n->prev;
}
// set list to skip over this list_node
if ( n->prev )
{
n->prev->next = n->next;
}
if ( n->next )
{
n->next->prev = n->prev;
}
}
+38
View File
@@ -0,0 +1,38 @@
#ifndef __signal_base_h__
#define __signal_base_h__
#include "slot_base.h"
class signal_base
{
public:
signal_base();
virtual ~signal_base();
protected:
bool bind(const slot_base& slot);
bool unbind(const slot_base& slot);
// TEMP HACK private:
// linked list data structure
struct list_node
{
slot_base slot;
list_node* prev;
list_node* next;
};
list_node* head;
list_node* tail;
// linked list manipulation
void clear_list();
list_node* find_in_list(const slot_base& slot) const;
bool add_to_list(const slot_base& slot);
void remove_from_list(list_node* n);
};
#endif // __signal_base_h__
+86
View File
@@ -0,0 +1,86 @@
#ifndef __slot_h__
#define __slot_h__
#include "slot_base.h"
#include "horrible_cast.h"
// TODO: do return types really matter???
// if we get rid of them, slot0 untemplates, making signal completely untemplate as well!
// maybe signal::Emit() should simply return the number of signals it emits?
//
// abstract base class for a slot taking 0 arguments
//
// this is basically just slot_base with the pure virtual "operator(...)" added
//
// each derivative of this should have NO storage of its own. Instead it should
// store data in the slot_base (parent class) members 'data[]'. Use horrible_cast<>()
// to subvert C++'s type system.
//
class slot0: public slot_base
{
public:
virtual void operator() () const = 0;
};
//
// a concrete slot class for a normal function
//
template <typename T_return>
class slot0_function: public slot0
{
public:
typedef T_return (*FUNCTION_POINTER)();
slot0_function(const FUNCTION_POINTER func)
{
// convert and store function pointer in slot_base
this->data[0] = safe_horrible_cast<data_container>(func);
}
void operator() () const
{
// retrieve data from slot_base and convert back to a function pointer
FUNCTION_POINTER func = dangerous_horrible_cast<FUNCTION_POINTER>(this->data[0]);
// call function
func();
}
};
//
// a concrete slot class for an object pointer and method (usually 'this' and 'Class::Method')
//
template <class T_object, typename T_member>
class slot0_method: public slot0
{
public:
slot0_method(const T_object* p_object, const T_member p_member)
{
// convert and store object pointer and member function pointer in slot_base
this->data[0] = safe_horrible_cast<data_container>(p_object);
this->data[1] = safe_horrible_cast<data_container>(p_member);
}
void operator() () const
{
// retrieve data from slot_base and convert back to an object pointer and member function pointer
T_object* p_object = dangerous_horrible_cast<T_object*>(this->data[0]);
T_member p_member = dangerous_horrible_cast<T_member> (this->data[1]);
// call member function
(p_object->*p_member)();
}
};
#endif // __slot_h__
+56
View File
@@ -0,0 +1,56 @@
#include "slot_base.h"
bool slot_base::data_container::operator== (const slot_base::data_container& other) const
{
// TODO: should use memcmp or equivalent
// do a bytewise comparison
for (int i=0;i<SIZE_IN_WORDS;++i)
{
if ( this->data[i] != other.data[i] )
{
return false;
}
}
// must be a perfect match
return true;
}
void slot_base::data_container::zero()
{
// do a bytewise zeroing
for (int i=0;i<SIZE_IN_WORDS;++i)
{
this->data[i] = 0;
}
}
slot_base::slot_base ()
{
for (int i=0;i<NUM_DATA_ITEMS;++i)
{
data[i].zero();
}
}
slot_base::~slot_base ()
{
// empty
}
bool slot_base::operator== (const slot_base& other) const
{
// check each data item for inequality
for (int i=0;i<NUM_DATA_ITEMS;++i)
{
if ( !(this->data[i] == other.data[i]) )
return false;
}
// all data matched
return true;
}
+43
View File
@@ -0,0 +1,43 @@
#ifndef __slot_base_h__
#define __slot_base_h__
class slot_base
{
public:
slot_base (); // constructor
virtual ~slot_base (); // destructor
/* Use compiler-generated copy constructor, assignment operator */
bool operator== (const slot_base& other) const;
protected:
// a generic type to hold a pointer, virtual method function pointer, etc.
struct data_container
{
// should be at least as large as the largest type you want
// to store in one of these
enum { SIZE_IN_WORDS = 4 };
enum { SIZE_IN_BYTES = SIZE_IN_WORDS*sizeof(unsigned int) };
// the actual storage space
unsigned int data[SIZE_IN_WORDS];
// an equality operator
bool operator== (const data_container& other) const;
// a method to set to empty (zero)
void zero();
};
// number of pointers, etc. we'll track in this class
enum { NUM_DATA_ITEMS = 2 };
// storage for function pointers, etc. hoisted out of derivative slots
data_container data[NUM_DATA_ITEMS];
};
#endif // __slot_base_h__