Introduction

I want to offer to you not a bad (as I think) solution for a particular problem. Suppose you must give the user of your program the ability to dynamically assign tasks that the program will do. Or you want to write a test module that can be programmed - for example, you want the ability to define any sequence of performing tasks in any order. You can, of course, write several classes with the same interface: write an abstract base class, inherit several different children, and create a list of base pointers on their objects to call the same virtual interface functions. But you must inherit them, and that means – you must implement a lot of possible things. It will be time consuming at the least. Let's think of this another way. In real life, if you want to repair or make something, you take all the tools that you need one by one by your hand and do what you want to. Why not create such a tool as our hand (with brain of course) that can envelop any particular existingtool and use it? I propose two classes that will envelop any functionality, and another one that can store any sequence of these envelopes. And now to the details. (To use the code given with this article, you must know how to install boost on your computer or set it in the project property additional include directory.)

But any time boost::bind is called for a different source (function or functor), it returns a completely new type of functor. So to store an unknown type, the class ActionHolder and the member storage must be declared as template types:

4. Action m_Action;

As a result, ActionHolder can store any functor and itself be stored in the container.

Adding an action to the container of actions

Let's see what this instruction does:

dash.AddActionToSequence(boost::bind<int>(A<int>(), 2,3));

A<int>() creates an object of the A<int> class. According to the declaration of this class:

It has an overloaded operator () that receives two parameters. To call it, we must pass two params:

A<int> a;
A(2,3);

boost::bind<int>(A<int>(), 2,3) creates a functor object that is passed as a param to the dash.AddActionToSequence() call. So it will be stored as m_Action (line 4 above) inside the newly created ActionHolder object (see the following definition of the DynamicActionSequenceHolder class and particularly the declaration and body of the AddActionToSequence template member function). Finally, it will be called as m_Action(); - without passing any parameters at all.

Key moment: The interface function DoAction calls m_Action() without passing parameters, so we must adjust all the stored functionality means (functions, objects) to call them in this interface instruction without any parameters too.

This template function uses the template’s runtime type deduction from the received parameters, that cannot be done in the template class constructor – so it’s role is the following: deduce the type of the functor object returned by boost::bind, and create an ActionHolder on that particular type.

Main simply creates an object of DynamicActionSequenceHolder, adds some functionality to the container (including the global function and member function), and calls it afterwards.

It’s up to you to create the module that gives the user the ability to dynamically choose or set a sequence for your program functionality. I simply put in Main several possible simple variants. Of course, in the bigger program, all AddActionToSequencee calls must be enveloped in a try block to catch std::bad_alloc.

In the context of this part of the article (file DynamicActionSequence.cpp), I suppose that each of the called functional objects does all its job without receiving or returning any parameters. If you want a more elaborate version that can work with parameters, returned from called functors, let's see the next program – based on the previous version.

Version 2 - working with returned values

First of all, let's look at the problems that are raised and must be solved to make this functionality workable. Types, returned by functors, can be different, so the best way to store returned values – template storage. Potentially, ActionHolder can be a host for stored values, but these values may be required outside of the ActionHolder. So the main problems are:

How to store void?

DynamicActionSequenceHolder stores pointers on the base interface – how can it return different return types through the same virtual function?

Handler for the returned value may or may not be passed at all. (By the way, the requirement for the handler is - it must have the same interface to be universally called – it must be a functor that receives one parameter.)

I decided that this can be solved using template specialization. But instead of a specialized ActionHolder, here is a specialized auxiliary struct RetValueStorage. It is responsible for the solution for the first two problems. If we want to return different types by the same way – using the same function, it must be done using some sort of union and storing identification of the stored type to be deduced after (like CDBVariant in MFC). I think that template specialization of the storage struct RetValueStorage for these values will work much faster than any runtime identification of stored types. The third problem is solved using another specialization of another auxiliary class, HandlerStub.

And now, let's see who is who in this version:

class ActionInterface – the interface for calling stored functors through a virtual function, storing its children - ActionHolder objects in one container correctly deleting the children, and for storing and returning the same pointer on the template child of the struct Base_Value_Storage.

struct Base_Value_Storage – interface for correctly deleting children and for storing the returned type’s ID.

struct RetValueStorage – template struct that stores the returned value of any type (except void) plus sets and stores the returned type ID (this is made through specialization on any type that must be stored. As a result – a void specialization doesn’t have a void member; the RetTypeSpecificator macro is used to declare them quickly).

enum RetType – just for convenience when working with different stored types.

RetTypeSpecificator – macros that help to declare specalizations of RetValueStorage.

class ActionHolder – stores the action, returned value, and handler for that value. The program has only one iteration of the handler call. The functor returned value and handler is called upon it.

CallFunctor() – member template function inside ActionHolder. Together withHandlerStub (or the real handler), they solve the following problems: for convenience, the user may or may not put a pointer on the existing handle in the ActionHolder class. So if the user does not provide a real handler for the returned value - in the ActionHolder constructor, we passed in the dynamically created HandlerStub object. A handler exists in every ActionHolder object, even if the returned type of the functor is void. The handler is a template member and can receive in its functor a parameter of any returned type except void. So the default HandlerStub class is specialized. The same problem happens with invoking m_Action();. This functor must be called according to the type returned from the stored action. This call may have a returned value or not (if void is returned). As a result, inside the virtual function, we call the specialized CallFunctor<>() template member function. In its CallFunctor<void>() specification, it can simply call m_Action(). In all other instances of CallFunctor, the corresponding storage receives the value returned from the m_Action() call.

TypeDeductor – auxiliary class used to check the similarity between the stored type and the buffer type (the variable that is passed to the ExtractValue function as a reference) when extracting data. If the types are the same – overloaded operator == returns true.

ExtractValue – global template function used to extract data that is stored inside the ActionHolder object in the RetValueStorage object.

HandlerStub – real stub that is passed when the user doesn’t want to use any handler at all.

It must be specialized because it must call its functor with the parameter, which cannot be a void object! So, an instance specialized on void does not have any functor at all.

DynamicActionSequenceHolder – manager that handles all the work with a particular action sequence. Its AddActionToSequence member function has two overloaded versions – one for receiving the real passed handler, and one for actions passed without any handler at all. This is for the user's convenience.

Include in RetTypeSpecificator.hpp the file with the declaration of the newly stored type.

Add to the RetTypeenum a new type (if it is not there already).

Use the macro RetTypeSpecificatorwith the new type.

Add to the ExtractValue function a new Case for your type.

If needed, define the handler for your type.

Add your action and handler to the DynamicActionSequenceHolder object.

This description is doubled using a //:ref_1 token (use it in the Find in files dialog). I use this trick in all my projects. It really saves a lot of time when adding new functionality or when looking for a bug.

Comments and Discussions

It was a pleasure to read a first part of your article and see how templates help to produce an elegant code.
The 'version two' is less satisfying.

Some friendly comments:

I see the first part of the article as a template implementation of the Object Factory design pattern. Indeed, you developed an hierarchy of classes, registered them in std::vector (std::map might be better), and later are accessing the registered objects.
You actually do not need to use boost::bind; inside a virtual DoAction() you can do anything. So, generally, a call to DoAction() is enough. Sure, a global function should be converted to the functor first.

As I understand, you want to use return values of Action functors. It is right that you have placed return type ID in base class and use them. But all other is overcomplicated. It is enough to change the signature of DoAction() to DoAction(void**) or to void* DoAction(). Inside DoAction() you may allocate an appropriate storage for the pointer because you know the type of return value, set value (*pointer = value) and return void pointer.
You will need some global function or structure with 'switch' statement inside to cast the returned pointer, but that is it.

If the memory size is not a problem and/or your classes have a small footprint, you may return a tuple from DoAction(). Something like typedef std::tuple MY_TUPLE. DoAction() will fill appropriate element of this tuple. Use the return type ID to access the value. It is not so elegant, but it is simple.

By a way, instead of enum RET_TYPE I would use TypeList. It is much simpler.
An last: please port your code to VC++ 2010. VC++ 2010 Express is free, works just fine, and is standard compliant.

Thanks for your comments!
I already thought about returning void * from DoAction() while worked on my article. But I decided to implement all functionality using template specifications. By the way, I must use boost::bind inside DoAction(), because the main idea is following - store and use any functionality as human hand can use any tools. And those tools are coming into DoAction() from the outside world.

Anyway, thanks!!!
I already downloaded your article and with pleasure will read it in my free time (and will compare your version - using void * with mine considering convenience and performance).

Thanks.
I forgot to say that there are STL templates mem_fun, mem_fun_ref to convert a member function to function object and ptr_fun to convert a pointer to function to function object. They are in header of Standard Library (http://www.cplusplus.com/reference/std/functional/.) The similar header is in Boost.
The best on TypeList is in Andrei Alexandresku "Modern C++ Design..."

Thanks.
I will read the links you send me, and STL templates mentioned. Completely agreed with your opinion about Andrei Alexandresku book - it is "must have", and with a need for me to switch from VS-2005 to VS-2010. My company is used VS-2005, but for me the time is come to make the next step.
best regards,
Andrew Padalka