diff --git a/callbacktest.sln b/deprec/callbacktest.sln similarity index 100% rename from callbacktest.sln rename to deprec/callbacktest.sln diff --git a/callbacktest.vcproj b/deprec/callbacktest.vcproj similarity index 100% rename from callbacktest.vcproj rename to deprec/callbacktest.vcproj diff --git a/source/Signal.h b/deprec/source/Signal.h similarity index 100% rename from source/Signal.h rename to deprec/source/Signal.h diff --git a/source/main.cpp b/deprec/source/main.cpp similarity index 100% rename from source/main.cpp rename to deprec/source/main.cpp diff --git a/source/signal/signal.pl b/deprec/source/signal/signal.pl similarity index 100% rename from source/signal/signal.pl rename to deprec/source/signal/signal.pl diff --git a/source/signal/signal0.h b/deprec/source/signal/signal0.h similarity index 100% rename from source/signal/signal0.h rename to deprec/source/signal/signal0.h diff --git a/source/signal/signal1.h b/deprec/source/signal/signal1.h similarity index 100% rename from source/signal/signal1.h rename to deprec/source/signal/signal1.h diff --git a/source/signal/signal10.h b/deprec/source/signal/signal10.h similarity index 100% rename from source/signal/signal10.h rename to deprec/source/signal/signal10.h diff --git a/source/signal/signal11.h b/deprec/source/signal/signal11.h similarity index 100% rename from source/signal/signal11.h rename to deprec/source/signal/signal11.h diff --git a/source/signal/signal12.h b/deprec/source/signal/signal12.h similarity index 100% rename from source/signal/signal12.h rename to deprec/source/signal/signal12.h diff --git a/source/signal/signal13.h b/deprec/source/signal/signal13.h similarity index 100% rename from source/signal/signal13.h rename to deprec/source/signal/signal13.h diff --git a/source/signal/signal14.h b/deprec/source/signal/signal14.h similarity index 100% rename from source/signal/signal14.h rename to deprec/source/signal/signal14.h diff --git a/source/signal/signal15.h b/deprec/source/signal/signal15.h similarity index 100% rename from source/signal/signal15.h rename to deprec/source/signal/signal15.h diff --git a/source/signal/signal16.h b/deprec/source/signal/signal16.h similarity index 100% rename from source/signal/signal16.h rename to deprec/source/signal/signal16.h diff --git a/source/signal/signal2.h b/deprec/source/signal/signal2.h similarity index 100% rename from source/signal/signal2.h rename to deprec/source/signal/signal2.h diff --git a/source/signal/signal3.h b/deprec/source/signal/signal3.h similarity index 100% rename from source/signal/signal3.h rename to deprec/source/signal/signal3.h diff --git a/source/signal/signal4.h b/deprec/source/signal/signal4.h similarity index 100% rename from source/signal/signal4.h rename to deprec/source/signal/signal4.h diff --git a/source/signal/signal5.h b/deprec/source/signal/signal5.h similarity index 100% rename from source/signal/signal5.h rename to deprec/source/signal/signal5.h diff --git a/source/signal/signal6.h b/deprec/source/signal/signal6.h similarity index 100% rename from source/signal/signal6.h rename to deprec/source/signal/signal6.h diff --git a/source/signal/signal7.h b/deprec/source/signal/signal7.h similarity index 100% rename from source/signal/signal7.h rename to deprec/source/signal/signal7.h diff --git a/source/signal/signal8.h b/deprec/source/signal/signal8.h similarity index 100% rename from source/signal/signal8.h rename to deprec/source/signal/signal8.h diff --git a/source/signal/signal9.h b/deprec/source/signal/signal9.h similarity index 100% rename from source/signal/signal9.h rename to deprec/source/signal/signal9.h diff --git a/horrible_cast.h b/horrible_cast.h new file mode 100644 index 0000000..7f44922 --- /dev/null +++ b/horrible_cast.h @@ -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 +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 +inline t_out dangerous_horrible_cast(t_in in) +{ + // instance the union + horrible_union 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 +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(in); +} + + +#endif // __horrible_cast_h__ + diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..aa8a653 --- /dev/null +++ b/main.cpp @@ -0,0 +1,71 @@ +#include +#include + + +#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 +slot0_method* make_slot(T_class* c, T_method m) +{ + return new slot0_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 x( somefunc ); + slot0* u = &x; + + if ( *s == *t ) + (*s)(); + + (*u)(); + + signal0 sig; + sig.connect(&f,&Foo::Bar); + sig(); +} + + + diff --git a/signal.cpp b/signal.cpp new file mode 100644 index 0000000..e69de29 diff --git a/signal.h b/signal.h new file mode 100644 index 0000000..e69de29 diff --git a/signal0.h b/signal0.h new file mode 100644 index 0000000..ca15ee5 --- /dev/null +++ b/signal0.h @@ -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 + void connect(T_object* p_object, T_member p_member) + { + bind( slot0_method(p_object,p_member) ); + } +}; + +#include "signal0.inl" + +#endif // __signal0_h__ + diff --git a/signal0.inl b/signal0.inl new file mode 100644 index 0000000..e69de29 diff --git a/signal_base.cpp b/signal_base.cpp new file mode 100644 index 0000000..38a0a76 --- /dev/null +++ b/signal_base.cpp @@ -0,0 +1,151 @@ +#include +#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; + } +} + + + diff --git a/signal_base.h b/signal_base.h new file mode 100644 index 0000000..d032bad --- /dev/null +++ b/signal_base.h @@ -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__ + diff --git a/slot.h b/slot.h new file mode 100644 index 0000000..d1d49c4 --- /dev/null +++ b/slot.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 +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(func); + } + + void operator() () const + { + // retrieve data from slot_base and convert back to a function pointer + FUNCTION_POINTER func = dangerous_horrible_cast(this->data[0]); + + // call function + func(); + } +}; + + +// +// a concrete slot class for an object pointer and method (usually 'this' and 'Class::Method') +// +template +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(p_object); + this->data[1] = safe_horrible_cast(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(this->data[0]); + T_member p_member = dangerous_horrible_cast (this->data[1]); + + // call member function + (p_object->*p_member)(); + } +}; + + +#endif // __slot_h__ + diff --git a/slot_base.cpp b/slot_base.cpp new file mode 100644 index 0000000..f6ab378 --- /dev/null +++ b/slot_base.cpp @@ -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;idata[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;idata[i] = 0; + } +} + + +slot_base::slot_base () +{ + for (int i=0;idata[i] == other.data[i]) ) + return false; + } + + // all data matched + return true; +} + diff --git a/slot_base.h b/slot_base.h new file mode 100644 index 0000000..93f6409 --- /dev/null +++ b/slot_base.h @@ -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__ +