Callback functions are a very popular software design feature used for decoupling software. While many programmers make use of callback functions in libraries, quite often these programmers introduce significant coupling in their own programs by not creating and using callback handlers in their own designs. This coupling can be reduced by designing and providing a callback function manager. In this tutorial a simple class for managing callback functions in C++ is provided.
In this tutorial we shall use the scenario of performing a set of test actions on starting up a car, from checking the oil level to checking the tyre pressures. The designers may wish to conduct a test on engine startup which may be different from one model to the next. With this in mind, the development team may have a number of test functions defined in different components. A callback manager class, CallbackMgr, is therefore defined to register the callback functions and provide a function to run all the registered functions when the Run function is called. A list is used in this implementation to store the registered functions. The use of the list in this implementation and the use of the push back function imply that the order that the samples will be run will be the order in which they were registered with the CallbackMgr. In practice this function may be created as a Singleton if we wish all components to access the same calback manager in our example for engine startup tests.
class CallbackMgr { std::list<std::function<void()>> m_list; public: void Register(std::function<void()> f){ m_list.push_back(f);} void Run(){ for (auto f : m_list){ f(); } } };
A simple test callback function is defined in test1.
void test1() { std::cout << "Check oil" << std::endl; }
The class Test2 registers a number of functions for callback as part of its constructor.
class Test2 { public: Test2(CallbackMgr &mgr) { mgr.Register(std::bind( &Test2::tyres, this)); mgr.Register(std::bind( &Test2::water, this)); } private: void tyres() { std::cout << "Check tyre pressure" << std::endl;} void water() { std::cout << "Check water level" << std::endl;} };
The Test2 class shows how access to the test functions tyres and water can be restricted, but the tests are still run when the callback manager Run function is called. The following code snippet shows the usage of the callback manager.
CallbackMgr mgr; mgr.Register(test1); Test2 t(mgr); mgr.Run();
The test1 function is registered by calling the manager register function while the reference to the manager is used within the Test2 class to register any functions that the Test2 class needs to register. Calling the Run function in the manager causes all registered functions to run.
While a simple callback function manager has been presented here, the presented callback manager class can be used as the basis for much more sophisticated designs.
Use the following link to obtain the complete source code used.
ABOUT THE AUTHOR(S)
Nicholas St. Hill
Nicholas St. Hill is an engineer with over 20 years research and development experience who runs KEIKY, a company specializing in the areas of instrument and sensor modelling and design. He is a Senior Member of the Institute of Electrical and Electronic Engineers (2009) and a Fellow of the Institution of Mechanical Engineers (2010).
Published: 2017-12-08