A simple script to serialize files

Table of contents

Example 1 (introduction example)

Jun 15, 2010 by cesla

Let's say you have written a C++ program that does something. Your directory structure is as follows:

./program/ main.cpp Makefile source/ engine.h engine.cpp

Let's say you defined a constant inside main.cpp:

const double VARIABLE = 1.234;

Now imagine that you need to run this program with a set of different VARIABLEs: 1.234, 2.345, 3.456, 4.567 and so on. Your options now are:

  • make VARIABLE a table and iterate over its values
    1. FINE if one run of the program takes few seconds
    2. BAD if one takes more than one day and you COULD run all VARIABLEs parallelly (as they don't depend on each other)
  • rewrite the program in a parallel manner, so that every value of VARIABLE would run in a different thread
    1. FINE if you know how to do it fast
    2. BAD if not; especially when you have access to computer cluster where you could run all versions of your program at once without the need of parallell programming
  • prepare different versions of the program, every with VARIABLE changed to specific value
    1. FINE if you have time to do it (depends on number of values, number of different variables to change, number of files to copy without changes...)
    2. BAD if not
    3. REALLY FINE if you have CodeIgor ;)

CodeIgor deals with this type of problem by using option 3), creating modified code for you (you only need to modify a little your files and make a config file with definitions for each solution). After using CodeIgor, you can have:

./solution1 main.cpp (modified; VARIABLE = 1.234) Makefile source/ engine.h engine.cpp ./solution2 main.cpp (modified; VARIABLE = 2.345) Makefile source/ engine.h engine.cpp (and so on)

Achieving this is very simple. You need to change your constant to

const double VARIABLE = {VAR};

Now you only need the config file - let's name it 'config.xml':

<config> <solution> <parse> <file>program/main.cpp</file> <param pattern="{VAR}">1.234</param> </parse> </solution> <solution> <parse> <file>program/main.cpp</file> <param pattern="{VAR}">2.345</param> </parse> </solution> <!-- (and any more solutions you need) --> </config>

Now the only thing left is to run the CodeIgor:

python -c config.xml -o output_dir

If everything went fine, you should have an 'output_dir' in your current directory, with all solutions put into separate subdirectories.

This is, of course, just a simple example. It may seem that you could easily do this one on your own. Imagine, however, when you need to modify more than one file, with more than one parameter. Doing all of this on your own will become tedious quite fast and prone to mistakes.

Example 2

Jun 15, 2010 by cesla

Let's say you need to send personalized data to many recipients.

./input/ mail_template.txt

You set your 'mail_template.txt', so it would contain usable params:

Dear {TITLE} {NAME}, I must inform you that bla, bla, bla, bla. Regards, Igor

You set your '', so it can send specified file to an email:

#!/bin/sh mailprogram --to {EMAIL} --message mail.txt

Your config file (which you can e.g. generate from some database containing your recipients' info) might look like this:

<config> <solution> <parse> <file output="mail">input/mail_template.txt</file> <param pattern="{TITLE}">Mr</param> <param pattern="{NAME}">John Smith</param> </parse> <parse> <file output="">input/</file> <param pattern="{EMAIL}"></param> </parse> </solution> <solution> <parse> <file output="mail">input/mail_template.txt</file> <param pattern="{TITLE}">Mrs</param> <param pattern="{NAME}">Anne Smith</param> </parse> <parse> <file output="">input/</file> <param pattern="{EMAIL}"></param> </parse> </solution> (...) </config>

After running CodeIgor, you will get a directory structure with personalized mail files and scripts like this:

./output/ solution1/ mail.txt solution2/ mail.txt (...)

Example 3

Jun 19, 2010 by cesla

Let's say that you want to change a name of a class inside your C++ code. This is an example of situation, where CodeIgor may be very helpful. Changing a class name is a trivial job - but imagine if your code consists of 20, maybe even 50 files, in many different subdirectories; you don't remember where the class name is used and which files have to be changed.

A great help here is the regexp param - your config file could not be simpler:

<config> <preproc> <parse> <dir>program</dir> <param pattern="{codeigor:regexp}"><![CDATA[ /ClassName(\W*)/NewName\1/ ]]></param> </parse> </preproc> <solution> </solution> </config>

Now for every file (from directory 'program' and its subdirectories) in every solution (since the parse block is inside 'global' block), name ClassName will be changed to NewName. Mind that you need at least one (even empty) solution block to produce an output.