Metaprgramming has become more and more popular and is now an essential part of modern C++ programming. By utilizing type information, it is possible implement generic programming and perform some compile time optimizations and checks. In this article, I use template metaprogramming to perform type extraction on a function and show an application of this technique.

To demonstrate one of the possible practical applications, I've created an open-source app that functions like a basic command interpreter or REPL. A new command is registered by providing a function pointer. The command class then has enough information to evaluate an input string, parse input parameters (check their number and types, convert if needed), invoke a command, and provide a result.

Here is an excerpt from the code that registers a new command; in this case, an add function:

int f_add(int, int); dconsole::BaseCmd* pcmd = dconsole::createCmd("add", "an addition function", f_add);

(Other examples can be found in the linked files in src\cmd_test.cpp ) After entering the aforementioned code into the command line, if you try to invoke the next string: add 5 4 , the command interpreter automatically parses it, performs the addition, and outputs a result. In the event you entered invalid parameters, it will tell you what is wrong:

input: "add not a number"

output: "add: bad arguments, usage RV: INT add INT INT;"

Figure 1 a screenshot of my test app.



Figure 1: A REPL implementation showing argument type checking occurring in real time.

Implementation

In this implementation, I will use the concept of type lists introduced by Andrei Alexandrescu.

Here is the basic class to implement type lists:

template <class T, class U> struct TypeList { typedef T Head; typedef U Tail; };

To create a type list, I use this class this way:

typedef TypeList< int, TypeList<float, TypeList<char, NullType> > > MyTypeList;

The NullType is a special, so-called "terminator" type. It helps to find the end of a list and can be simply defined as class NullType {} .

Now, assuming that I can easily create a type list that enumerates all my function types (return type and input parameters), I could perform an automatic type check and conversion of data received as a string. So, if the parameters are passed as strings (as in the REPL), they need some method to check them automatically. What is the easiest way of doing this?

I suppose having something like this would be the easiest:

bool foo(int, int); Cmd* pcmd = createCmd(foo);

Now using this class, I can handle queries like this: pcmd->run("5 10");

The class itself will check the parameters and perform a call if the parameters are correct. Even though in this article I am not discussing the operation of parsing string and converting it to respective types, you can see an example of it in my sample program, which demonstrates an application of the described technique by implementing a debug console for the application.

Now, let's see how to implement createCmd to automatically create a type list. In order to implement this function, I will use templates. Here is the declaration of the function:

// first parameter - command name, second - function type; template <typename R, typename T> CmdBase* createCmd(const std::string& name, T );

Here, the name is passed as a first parameter followed by pointer to the function. Implementation of this function is straightforward: It is used as a wrapper for the actual class that will do all the work.

Here is the implementation for a single-parameter version ( T can also be void for functions with no parameters). I just pass all parameters to the cmdCreator class.

template <typename R, typename T> CmdBase* createCmd(const std::string& name, R (*fffp)(T)) { typedef void (*fffp_t)(T); return cmdCreator<fffp_t>::createCmd(name, fffp); }

Implementation for two or more partameters looks pretty similar.:

template <typename R, typename T1, typename T2> CmdBase* createCmd(const std::string& name, R (*fffp)(T1, T2)) { typedef R (*fffp_t)(T1, T2); return cmdCreator<fffp_t>::createCmd(name, fffp); }

Now let's proceed to the cmdCreator class.

Declaration looks very simple: template <typename T> struct cmdCreator;

Here is the implementation for the function with one or none ( void ) input parameters:

template <typename R, typename T> struct cmdCreator< R (*)(T) > { static CmdBase* createCmd(const std::string& name, R (*fffp)(T)) { return new TypeList< R, TypeList<T, NullType> > (name, fffp); } };

As you can see, the static function createCmd is the heart of our system. It creates type list in a correct way based on templates parameters. For demonstration purposes, here is the implementation for functions with two parameters:

template <typename R, typename T1, typename T2> struct cmdCreator< R (*)(T1, T2) > { static CmdBase* createCmd(const std::string& name, R (*fffp)(T1, T2)) { return new TypeList< R, TypeList<T1, TypeList<T2, NullType> > > (name, fffp); } };

Notice that I create class TypeList while returning pointer to CmdBase . As I will show later, I will derive the TypeList class from BaseCmd to benefit from polymorphism.

Now I have a way to create the correct TypeList , but how can I apply this?

Let's consider the earlier example and first extend TypeList to add automatic type checking.

For simplicity, let's assume that we have already parsed the input string and created a list of parameters, which are wrapped in a Variant class. The Variant class is a useful concept. For additional info you may check out Boost.Variant or any other implementation. Here is how I implement type checking:

template <class T, class U> struct TypeList: public CmdBase { //... enum { myIndex = NumEl< TypeList<Head, Tail> >::value }; bool check(const std::vector< Variant >& params) { if(params.size() < myIndex) return false; return checkParams(params, 0); } bool static checkParams(const std::vector< Variant >& params, int offset = 0) { if(!params[offset].is<Tail::Head>()) return false; return Tail::checkParams(params, offset+1); } };

I also have to define a specialization for TypeList< R, NullType> class, because we want the recursion in the above function to end.

template <class R> struct TypeList< R, NullType>: public CmdBase { enum { myIndex = NumEl< TypeList<Head, Tail> >::value }; //... void check(const std::vector< Variant >& params) { // same as above } bool static checkParams(const std::vector< Variant >& params, int offset = 0) { return true; } };

I introduced one more helper template class, NumEl , which keeps the counts for the type index in the type list. The declaration look like this:

template < class TypeList> struct NumEl;

Here is the implementation:

//Specialization for terminator class template <class T> struct NumEl<TypeList<T, NullType> > { enum { value = 0 }; };

As you can see, it uses recursion for its Tail type:

template <class Head, class Tail> struct NumEl<TypeList<Head, Tail> > { private: enum { temp = NumEl<Tail>::value }; public: enum { value = 1 + temp }; };

Now let's implement the run() method. I will also use the helper template class executor for which I will use same technique as for the cmdCreator class. But first, I'll provide a run() method. All that it does is call executor::run method, providing correct specialization.

virtual void run(const std::vector<Variant>& params) { if( check(params) ) executor<myfp, myIndex>::run(m_fptr_, params); }

The executor class is declared as: template <typename T, int N> struct executor;

The first template parameter T is a function pointer that is passed by the TypeList class. The second template parameter is the number of function types. As you see, I use the myIndex compile-time constant, which was calculated with the help of our NumEl helper class.

The implementation for two parameters is:

template <typename T> struct executor<T, 2> { static void run(T fp, const std::vector<Variant>& vec) { fp( vec[0], vec[1] ); } };

Implementation for any other number of parameters is pretty straightforward.