#ifndef __TUTORIAL_CALLBACK_H
#define __TUTORIAL_CALLBACK_H
/**
How to read the tutorial
Tutorial 1 -- get start using callback (slot in Qt, boost::signals, and libsigc++)
Tutorial 2 -- get start using callback list (signal in Qt, boost::signals, and libsigc++)
Tutorial 3 -- auto disconnection
Tutorial 4 -- access the internal list and make advanced change to callback list
How to read the tutorial:
Each tutorial example is a class which name starts with Tutorial_.
Each class' constructor is the entry function of the tutorial.
All other functions in the class are accessorial.
*/
#include "gcallbacklist.h"
/**
The namespace is cpgf.
*/
using namespace cpgf; // for tutorial only, usually don't do this in head file.
#include <string>
#include <iostream>
using namespace std; // for tutorial only again
// A global function to test using global function as callback
static int globalFunction(const string & s) {
cout << "globalFunction: " << s << endl;
return 5;
}
// A functor class to test using functor as callback
class SampleFunctor {
public:
int operator () (const string & s) const {
cout << "SampleFunctor: " << s << endl;
return 0;
}
/* Define the == operator to always return true to make the functor comparable.
Otherwise, GCallback<int, const string &>(SampleFunctor()) will be not equal to another GCallback<int, const string &>(SampleFunctor())
*/
bool operator == (const SampleFunctor &) const {
return true;
}
};
// A normal class to test using member function as callback
class TestObject
{
public:
// virtual or non-virtual doesn't matter for callback
virtual int __stdcall objectFunction(const string & s) {
cout << "objectFunction: " << s << endl;
return 1;
}
static int staticFunction(const string & s) {
cout << "staticFunction: " << s << endl;
return 2;
}
};
/**
Get start using callback (slot).
A single callback can be used where C style raw function pointer is used.
You can pass a callback, store it, assign new function to it, and invoke it.
*/
class Tutorial_GetStart_Callback
{
public:
Tutorial_GetStart_Callback() {
TestObject test;
GCallback<int (const string &)> cb;
cb = SampleFunctor();
cout << cb("aaa") << endl;
cb = GCallback<int (const string &)>(&test, &TestObject::objectFunction);
// same as above line but simpler
cb = makeCallback(&test, &TestObject::objectFunction);
cout << cb("bbb") << endl;
cb = &TestObject::staticFunction;
cout << cb("ccc") << endl;
cb = &globalFunction;
cout << cb("ddd") << endl;
}
};
/**
Get start using callback list (signal).
*/
class Tutorial_GetStart_CallbackList
{
public:
Tutorial_GetStart_CallbackList() {
TestObject test;
GCallbackList<int (const string &)> cbList;
cbList.add(SampleFunctor()); // the functor will be converted to callback implicitly
cbList.add(&test, &TestObject::objectFunction);
cbList.add(&TestObject::staticFunction); // the function will be converted to callback implicitly
cbList.add(&globalFunction); // the function will be converted to callback implicitly
cbList.dispatch("dispatch"); // call all callbacks
// This will only succeed if an "always return true" == operator is defined for the SampleFunctor.
// If no the == operator, to remove the functor, we need to either retain the reference to the functor object or add a special tag to it.
cbList.remove(SampleFunctor());
// Below will always work
cbList.remove(&test, &TestObject::objectFunction);
cbList.remove(&TestObject::staticFunction);
cbList.remove(&globalFunction);
// Now the list should be empty.
cbList.dispatch("dispatch after remove. Should not happen."); // do nothing and can't see the message
}
};
class SampleTrackable
{
public:
void callback1() const {
cout << "SampleTrackable callback1" << std::endl;
}
public:
GCallbackTrackable trackable;
};
/**
Auto disconnection.
Learn how to make a callback auto disconnect from callback list when the host object is destroyed.
*/
class Tutorial_AutoDisconnection
{
public:
Tutorial_AutoDisconnection() {
GCallbackList<void ()> cbList;
// Scoped context
{
SampleTrackable obj;
// The last parameter is the key for auto disconnection
cbList.add(&obj, &SampleTrackable::callback1, &obj.trackable);
cbList();
}
// Now obj is freed out of scope and the callback is auto removed from cbList
cbList(); // does nothing
}
};
/**
Access the internal list and make advanced change to callback list.
This is very advanced topic and usually you don't need to read this.
*/
class Tutorial_InternalList
{
public:
Tutorial_InternalList() {
// Define a type for callback list so we don't need to input the template parameters every time.
// Good practice for callback list and callback.
// To use the tag and auto remove feature in connection, GCallbackExtendedConnection must be specified as the second parameter of callback list.
typedef GCallbackList<int (const string &), GCallbackExtendedConnection> CallbackListType;
TestObject test;
CallbackListType cbList;
cbList.add(SampleFunctor());
cbList.add(&test, &TestObject::objectFunction);
cbList.add(&TestObject::staticFunction);
cbList.add(&globalFunction);
// Get the internal list. Don't copy it, just reference to it.
CallbackListType::DirectListType & internalList = cbList.getDirectList();
{
CallbackListType::ConnectionType & connection = *internalList.begin();
connection.autoRemoveAfterFirstCall();
}
// After the dispath, the first callback, SampleFunctor, will be auto removed due to autoRemoveAfterFirstCall.
cbList("first dispatch");
// This dispatch will not invoke SampleFunctor
cbList("second dispatch");
// Invoke each callback manually and output the return values.
for(CallbackListType::IteratorType it = internalList.begin(); it != internalList.end(); ++it) {
cout << it->getCallback().invoke("internal invoke") << endl;
}
// Reverse the callbacks order, fun but not very useful
internalList.reverse();
cbList("reversed");
// Control the connections
for(CallbackListType::IteratorType it = internalList.begin(); it != internalList.end(); ++it) {
CallbackListType::ConnectionType & connection = *it;
connection.setTag(10);
}
// Remove all callbacks one by one
for(CallbackListType::IteratorType it = internalList.begin(); it != internalList.end(); ++it) {
CallbackListType::ConnectionType & connection = *it;
// Warning: Don't remove the iterator from internal list directly, otherwise the auto disconnection will take no effect and cause memory leak.
// We should always remove callbacks from the callback list.
cbList.remove(connection);
}
// We have removed all connections.
// Below invoke will do nothing.
cbList("should not happen");
}
};
#define RUN_TUTORIAL(t) \
cout << endl << # t << endl; \
t();
// call this function to execute all tutorials
static void runTutorials()
{
RUN_TUTORIAL(Tutorial_GetStart_Callback);
RUN_TUTORIAL(Tutorial_GetStart_CallbackList);
RUN_TUTORIAL(Tutorial_AutoDisconnection);
RUN_TUTORIAL(Tutorial_InternalList);
}
#endif
Write a comment
- Required fields are marked with *
- Security Code is case sensitive, 'A' is different with 'a'.