I sometimes have meta-programs written using C++ templates that have to build a function which performs various tasks. One example would be to create a serialization-deserialization function pair for an object that’s identified as an aggregate of values. Or once could be describing an operation as a series of tags or traits, and the metaprogram must explore those traits and generate the function.
I propose here a small one-file template metaprogramming library that allows the compile-time creation of functions from component sub-functions. First, an example of how it can be used:
#include <iostream> #include "cacoethes/action.hpp" struct outer { static void before(int &i) { std::cout << "outer::before(" << i << ")n"; ++i; } static void after (int &i) { std::cout << "outer::after(" << i << ")n"; --i; } }; struct inner { static void before(int &i) { std::cout << "inner::before(" << i << ")n"; i*=3; } static void after (int &i) { std::cout << "inner::after(" << i << ")n"; i/=2; } }; using namespace cacoethes; typedef action::empty<int> action1; typedef action::decorate<action1, inner> action2; typedef action::decorate<action2, outer> action3; int main() { int value = 10; std::cout << "Action 1 (" << value << ")n"; action1::execute(value); std::cout << "Action 2 (" << value << ")n"; action2::execute(value); std::cout << "Action 3 (" << value << ")n"; action3::execute(value); }
This example outputs, as expected:
Action 1 (10) Action 2 (10) inner::before(10) inner::after(30) Action 3 (15) outer::before(15) inner::before(16) inner::after(48) outer::after(24)
The basic principle of the library is as expected: an action is a class that has a static ‘execute()’ function taking a reference to an argument. The function communicates with its caller through that reference, both as input and output.
The simplest action is ‘action::empty’, which is in fact an action template with the type of the argument being a template parameter.
From an action, once can construct another action through decoration. This involves providing a type which has two static functions, ‘before()’ and ‘after()’, both having the same signature as the ‘execute()’ function. The decoration, performed with the ‘decorate<>’ meta-function, creates a new action from the old one. Executing the new action runs the ‘before()’ function, then the ‘execute()’ of the old action, and finally the ‘after()’ function.
Classic errors involve trying to decorate something that is not an action, or trying to decorate using a type that does not have static functions ‘before()’ and ‘after()’.
The “cacoethes/action.hpp” file itself is pretty simple:
// Copyright (c) 2009, Victor Nicollet // All rights reserved. #ifndef ACTION_HPP #define ACTION_HPP namespace cacoethes { namespace action { template <typename ARG> struct empty { typedef ARG argument_type; static inline void execute(argument_type &arg) {} }; template <typename ACTION, typename OPERATIONS> struct decorate { typedef ACTION inner; typedef typename inner::argument_type argument_type; static inline void execute(argument_type &arg) { OPERATIONS::before(arg); ACTION::execute(arg); OPERATIONS::after(arg); } }; } } #endif

Hi. I'm Victor Nicollet,
0 Responses to “C++ Metaprogramming : Actions”