Created event dispatcher and function binder (for input system)

actorid
Nicolay Korslund 15 years ago
parent d5228c6f5f
commit 3a26219cb8

@ -0,0 +1,73 @@
#ifndef _INPUT_DISPATCHMAP_H
#define _INPUT_DISPATCHMAP_H
#include <set>
#include <map>
#include <assert.h>
namespace Input {
/**
DispatchMap is a simple connection system that connects incomming
signals with outgoing signals.
The signals can be connected one-to-one, many-to-one, one-to-many
or many-to-many.
The dispatch map is completely system agnostic. It is a pure data
structure and all signals are just integer indices. It does not
delegate any actions, but used together with Dispatcher it can be
used to build an event system.
*/
struct DispatchMap
{
typedef std::set<int> OutList;
typedef std::map<int, OutList> InMap;
typedef OutList::iterator Oit;
typedef InMap::iterator Iit;
InMap map;
void bind(int in, int out)
{
map[in].insert(out);
}
void unbind(int in, int out)
{
Iit it = map.find(in);
if(it != map.end())
{
it->second.erase(out);
// If there are no more elements, then remove the entire list
if(it->second.empty())
map.erase(it);
}
}
/// Check if a given input is bound to anything
bool isBound(int in)
{
return map.find(in) != map.end();
}
/**
Get the list of outputs bound to the given input. Only call this
on inputs that you know are bound to something.
The returned set is only intended for immediate iteration. Do not
store references to it.
*/
const OutList &getList(int in)
{
assert(isBound(in));
const OutList &out = map[in];
assert(!out.empty());
return out;
}
};
}
#endif

@ -0,0 +1,78 @@
#ifndef _INPUT_EVENT_DISPATCHER_H
#define _INPUT_EVENT_DISPATCHER_H
#include "dispatch_map.hpp"
#include <boost/function.hpp>
namespace Input {
/**
The EventDispatcher translates an input event (as given by an
identifying index and an optional void pointer) into an output
event function call.
*/
class EventDispatcher
{
DispatchMap map;
/*
The event callback function that is called for all events. The
first parameter is the input event. The second parameter is the
resolved output event. The third is an optional user-defined
parameter passed to call().
*/
typedef boost::function<void(int,int,void*)> EventCallback;
EventCallback callback;
// Carpal-tunnel prevention
typedef DispatchMap::OutList _OL;
public:
/// Create an event binding connection
void bind(int in, int out)
{ map.bind(in,out); }
/// Dissolve an event binding connection
void unbind(int in, int out)
{ map.unbind(in, out); }
/// Check if a given input is bound to anything
bool isBound(int in)
{ return map.isBound(in); }
/// Register the callback you want to use to handle events.
void setCallback(EventCallback cb)
{ callback = cb; }
/**
Instigate an event.
This will look up the input event number (first parameter), and
call the event callback for each output number associated with
(bound to) that input.
The optional second paramter is also passed to the callback.
If no output is bound to the given event number, the callback
is never called.
*/
void call(int event, void *p = NULL)
{
// You have to set the callback before using call().
assert(!callback.empty());
// Not bound? Exit.
if(!isBound(event)) return;
// Dispatch to all events.
const _OL &list = map.getList(event);
for(_OL::const_iterator it = list.begin();
it != list.end(); it++)
callback(event, *it, p);
}
};
}
#endif

@ -0,0 +1,99 @@
#ifndef _INPUT_FUNCBINDER_H
#define _INPUT_FUNCBINDER_H
#include <string>
#include <vector>
#include <boost/function.hpp>
#include <assert.h>
namespace Input {
/**
An Action defines the user defined action corresponding to a
binding.
The first parameter is the action index that invoked this call. You
can assign the same function to multiple actions, and this can help
you keep track of which action was invoked.
The second parameter is an optional user-defined parameter,
represented by a void pointer. In many cases it is practical to
point this to temporaries (stack values), so make sure not to store
permanent references to it unless you've planning for this on the
calling side as well.
*/
typedef boost::function<void(int,void*)> Action;
/**
The FuncBinder is a simple struct that binds user-defined indices
to functions. It is useful for binding eg. keyboard events to
specific actions in your program, but can potentially have many
other uses as well.
*/
class FuncBinder
{
struct FuncBinding
{
std::string name;
Action action;
};
std::vector<FuncBinding> bindings;
public:
/**
Constructor. Initialize the struct by telling it how many action
indices you intend to bind.
The indices you use should be 0 <= i < number.
*/
FuncBinder(int number) : bindings(number) {}
/**
Bind an action to an index.
*/
void bind(int index, Action action, const std::string &name="")
{
assert(index >= 0 && index < bindings.size());
FuncBinding &fb = bindings[index];
fb.action = action;
fb.name = name;
}
/**
Unbind an index, reverting a previous bind().
*/
void unbind(int index)
{
assert(index >= 0 && index < bindings.size());
bindings[index] = FuncBinding();
}
/**
Call a specific action. Takes an optional parameter that is
passed to the action.
*/
void call(int index, void *p=NULL)
{
assert(index >= 0 && index < bindings.size());
FuncBinding &fb = bindings[index];
if(fb.action) fb.action(index, p);
}
/// Check if a given index is bound to anything
bool isBound(int index)
{
assert(index >= 0 && index < bindings.size());
return !bindings[index].action.empty();
}
/// Return the name associated with an action (empty if not bound)
const std::string &getName(int index)
{
assert(index >= 0 && index < bindings.size());
return bindings[index].name;
}
};
}
#endif

@ -7,8 +7,6 @@
#include <OgreFrameListener.h>
#include <OgreRenderWindow.h>
//#include <iostream>
namespace Input
{
struct ExitListener : Ogre::FrameListener,

@ -1,9 +1,15 @@
GCC=g++
all: funcbind_test
all: funcbind_test dispatch_map_test event_dispatcher_test
funcbind_test: funcbind_test.cpp
$(GCC) $^ -o $@
funcbind_test: funcbind_test.cpp ../func_binder.hpp
$(GCC) $< -o $@
dispatch_map_test: dispatch_map_test.cpp ../dispatch_map.hpp
$(GCC) $< -o $@
event_dispatcher_test: event_dispatcher_test.cpp ../event_dispatcher.hpp
$(GCC) $< -o $@
clean:
rm *_test

@ -0,0 +1,54 @@
#include <iostream>
using namespace std;
#include "../dispatch_map.hpp"
using namespace Input;
typedef DispatchMap::OutList OutList;
typedef OutList::const_iterator Cit;
void showList(const DispatchMap::OutList &out)
{
for(Cit it = out.begin();
it != out.end(); it++)
{
cout << " " << *it << endl;
}
}
void showAll(DispatchMap &map)
{
cout << "\nPrinting everything:\n";
for(DispatchMap::Iit it = map.map.begin();
it != map.map.end(); it++)
{
cout << it->first << ":\n";
showList(map.getList(it->first));
}
}
int main()
{
cout << "Testing the dispatch map\n";
DispatchMap dsp;
dsp.bind(1,9);
dsp.bind(2,-5);
dsp.bind(2,9);
dsp.bind(3,10);
dsp.bind(3,12);
dsp.bind(3,10);
showAll(dsp);
dsp.unbind(1,9);
dsp.unbind(5,8);
dsp.unbind(3,11);
dsp.unbind(3,12);
dsp.unbind(3,12);
showAll(dsp);
return 0;
}

@ -0,0 +1,54 @@
#include <iostream>
using namespace std;
#include "../event_dispatcher.hpp"
using namespace Input;
void callback(int in, int out, void *p)
{
cout << " Got event: in=" << in << " out=" << out << endl;
}
EventDispatcher dsp;
void callAll()
{
cout << "\nDuty calls:\n";
for(int i=1; i<5; i++)
{
cout << " Calling event " << i << ":\n";
dsp.call(i);
}
}
int main()
{
cout << "Testing the event dispatcher\n";
dsp.setCallback(&callback);
callAll();
dsp.bind(2,1);
dsp.bind(1,10);
dsp.bind(14,-12);
dsp.bind(2,-137);
callAll();
dsp.unbind(1,8);
dsp.unbind(1,10);
dsp.unbind(2,-137);
dsp.unbind(2,1);
callAll();
dsp.bind(3, 19);
dsp.bind(4, 18);
dsp.bind(4, 18);
callAll();
return 0;
}

@ -1,84 +1,47 @@
#include <iostream>
using namespace std;
#include <string>
#include <vector>
#include <boost/function.hpp>
#include <assert.h>
#include "../func_binder.hpp"
typedef boost::function<void()> Action;
struct FuncBind
{
std::string name;
Action action;
};
class Binder
void f1(int i, void *p)
{
std::vector<FuncBind> bindings;
public:
/**
Initialize the struct by telling it how many functions you have
to bind. The largest index you intend to use should be number-1.
*/
Binder(int number) : bindings(number) {}
void bind(int index, Action action, const std::string &name="")
{
assert(index >= 0 && index < bindings.size());
FuncBind &fb = bindings[index];
fb.action = action;
fb.name = name;
}
void unbind(int index)
{
assert(index >= 0 && index < bindings.size());
FuncBind &fb = bindings[index];
fb = FuncBind();
}
cout << " F1 i=" << i << endl;
void call(int index)
{
assert(index >= 0 && index < bindings.size());
FuncBind &fb = bindings[index];
if(fb.action)
{
cout << "Calling '" << fb.name << "'\n";
fb.action();
}
else
cout << "No function\n";
}
};
void f1()
{
cout << "In f1()\n";
if(p)
cout << " Got a nice gift: "
<< *((float*)p) << endl;
}
void f2()
void f2(int i, void *p)
{
cout << "In f2()\n";
cout << " F2 i=" << i << endl;
}
using namespace Input;
int main()
{
cout << "This will test the function binding system\n";
Binder bnd(5);
FuncBinder bnd(5);
bnd.bind(0, &f1, "This is action 1");
bnd.bind(1, &f2);
bnd.bind(2, &f1, "This is action 3");
bnd.bind(3, &f2, "This is action 4");
bnd.unbind(2);
for(int i=0; i<5; i++)
{
cout << "\nCalling " << i << endl;
cout << "Calling " << i << ": '" << bnd.getName(i) << "'\n";
bnd.call(i);
if(!bnd.isBound(i)) cout << " (not bound)\n";
}
cout << "\nCalling with parameter:\n";
float f = 3.1415;
bnd.call(0, &f);
return 0;
}

@ -0,0 +1,18 @@
Testing the dispatch map
Printing everything:
1:
9
2:
-5
9
3:
10
12
Printing everything:
2:
-5
9
3:
10

@ -0,0 +1,30 @@
Testing the event dispatcher
Duty calls:
Calling event 1:
Calling event 2:
Calling event 3:
Calling event 4:
Duty calls:
Calling event 1:
Got event: in=1 out=10
Calling event 2:
Got event: in=2 out=-137
Got event: in=2 out=1
Calling event 3:
Calling event 4:
Duty calls:
Calling event 1:
Calling event 2:
Calling event 3:
Calling event 4:
Duty calls:
Calling event 1:
Calling event 2:
Calling event 3:
Got event: in=3 out=19
Calling event 4:
Got event: in=4 out=18

@ -1,19 +1,15 @@
This will test the function binding system
Calling 0
Calling 'This is action 1'
In f1()
Calling 1
Calling ''
In f2()
Calling 2
Calling 'This is action 3'
In f1()
Calling 3
No function
Calling 4
No function
Calling 0: 'This is action 1'
F1 i=0
Calling 1: ''
F2 i=1
Calling 2: ''
(not bound)
Calling 3: 'This is action 4'
F2 i=3
Calling 4: ''
(not bound)
Calling with parameter:
F1 i=0
Got a nice gift: 3.1415

Loading…
Cancel
Save