signals 2.0
This commit is contained in:
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.
@@ -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__
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
|
||||
Whitespace-only changes.
@@ -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__
|
||||
|
||||
Whitespace-only changes.
+151
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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__
|
||||
|
||||
@@ -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__
|
||||
|
||||
@@ -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
@@ -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__
|
||||
|
||||
Reference in new issue
Block a user