Vermeille's blog

Resume && contactProjetsC++BullshitLanguage TheoryArtificial Intelligence¬LinkedIn

Sound Synthesis (1/ Oscillators)

Introduction

Motivation

Yesterday, I was at BeMyApp contest, where we had to develop something about music. After a stupid lyrics generators using markov chains and an uninteresting game in html5, I decided to code a synthesizer (A dream I had for many years, since I'm really interested in sound synthesis).

Theory

Reminders

Okay, let's start with some sound theory.

Sound is mechanical wave propagating in the air. If those waves are periodic, it's a note. Taking a fundamental frequency (mostly 440 Hz, we can define our notes with the following formula:

\[f(n) = (\sqrt[12]{2})^{n-49} 440 \text{Hz}\]

As Fourier tells us, every periodic wave can be described in terms of a sum of sinus functions.

\[x_{signal}(t) = \sum_{k=0}^{N}A_{i}\cos(t * F_i)\]

The wave with the lowest \(F_i\) is named "fundamental", and multiples of this frequency are called "harmonics".

Let's see our 5 primitive waveforms:

Sinus

The simplest wave is then a sinus: pure signal, without any harmonics. The formula is:

\[x_{sinus}(t) = \sin(2\pi ft)\]

Square

A square waveform, in sense of Fourier is an infinite serie summing all odd frequencies multiple of the harmonic.

\[x_{square}(t) = \frac{4}{\pi}\sum_{k=1}^{\infty} \frac{\sin(2\pi(2k-1)ft)}{2k-1}\]

Hopefully, we won't need to code such a formula. Electrically, we do this by commutating a DC generator, and we will do almost the same programatically.

Sawtooth

A sawtooth is defined as a sum of all even harmonics

\[x_{saw}(t) = \frac{2}{\pi} \sum_{k=1}^{\infty}(-1)^{k+1}\frac{\sin(2\pi kft)}{k}\]

Electrically, I think that this is achieved with some RLC circuits (please, if you know, confirm it). Programatically, we don't need to code such a formula.

Triangle

A triangle is the sum of odd harmonics, multiplying every \((4n-1)\)th harmonic by \(-1\) and rolling off the harmonics by the inverse square of their relative frequency to the fundamental (thx Wikipedia :D)

\[x_{triangle} = \frac{8}{\pi^{2}} \sum_{k=0}^{\infty}(-1)^{k}\frac{\sin((2k+1)\omega t)}{(2k+1)^2}\]

Whitenoise

It's just random numbers guys.

\[x_{\text{whitenoise}}(t) = \text{rand}()\]

Recap

recap

Synthesis systems

Additive synthesis

This system is directly inspired from Fourier's series: take a lot of sinus, and add them to make a complex sound.

Substractive synthesis

Take complex sounds (like the 5 ones defined above), and then apply some filters to treat them.

FM synthesis

This kind of synthesis has almost an unpredictable result, and works like radio does: the timbre of a simple waveform is changed by frequency modulating it with a modulating frequency that is also in the audio range, resulting in a more complex waveform and a different-sounding tone.

Wavetable synthesis

This one may be though like a combinations of others: take some sounds, and play them each for few milliseconds. The result can really be crazy. This is the one we will implement, because it allows killing acid sounds without efforts.

We are in a digital world

A word about your sound card

Since we're using a computer, we're dealing with discrete values. We will send our sound to the sound card in the form of many int16, so we have to be able to give the amplitude of our signal considering sampling rate and the frequency. In most systems, the default sampling rate is 44100 Hz, it means that you have to feed your sound card with 44100 "values" per second (for each channel, that's why, assuming stereo, I'll duplicate each value).

Generating waveforms

Enter the world of algorithms. Since I wrote my synthesizer both in Haskell and C++, I'll show, for each part, the language which is the easier to understand. If you never read haskell, list are constructed with :, don't bother about infinite lists and recursions (allowed by laziness), and everything should be okay.

I need to introduce two magic numbers widely used in the code: 44100 is the default sampling rate we target, (-32767, 32768) are short_min and short_max

-- takes a frequency and returns the number of data needed to fill a period
getPeriod :: Float -> Int
getPeriod freq = freq * 2 * pi / 44100

-- takes a number of milliseconds and returns the corresponding number of data
msToDatas :: Int -> Int
msToDatas ms = truncate $ 44100.0 / (1000.0 / fromIntegral ms)

Sine

It's really straigthforward, generate the ith value using the period, and recurse on the (i+1)th value.

sinW :: Float -> Int -> [Int16]
sinW freq i = sinW' i (getPeriod freq)

sinW' :: Int -> Float -> [Int16]
sinW' i period =
	let new = (round $ (* 32767.0) $ sin (fromIntegral i * period)) in
	      new:new:sinW' (i + 1) period

Square

The square waveform is \(A.\mathrm{sign}(\sin(i * \mathit{period}))\).

squareW :: Float -> Int -> [Int16]
squareW freq i = squareW' i (getPeriod freq)

squareW' :: Int -> Float -> [Int16]
squareW' i period = let new = (round . (* 32767.0) . fromIntegral . sign
	                  $ sin (fromIntegral i * period)) in
	    new:new:squareW' (i + 1) period

sign x | x < 0 = -1
       | otherwise = 1

Sawtooth

Algorithmically, this is simple : samplesPerWavelength is the number of data to play during one period, ampStep is the "amount of amplitude" to add between each step. Start at int16_min, add ampStep until we reach int16_max, then restart.

sawW :: Int -> Float -> [Int16]
sawW i period = sawW' i period (-32767)

sawW' :: Int -> Float -> Int -> [Int16]
sawW' i period tempSample
	| i < samplesPerWavelength =
	        let new = fromIntegral tempSample in
	        new:new:sawW' (i + 1) period (tempSample + ampStep period)
	| otherwise = (-32767):(-32767):sawW' 0 period (-32767 + ampStep period)
	where
            samplesPerWavelength :: Int
	    samplesPerWavelength = truncate $ 44100.0 / period
            ampStep :: Int
	    ampStep = (44100 * 2) `div` samplesPerWavelength period

Triangle

This looks like the sawtooth: samplesPerWavelength is the number of data to play during one period, ampStep is the "amount of amplitude" to add or substract between each step. Start at int16_min, add ampStep until we reach int16_max, then substract ampStep until we reach int16_min etc...

triW :: Float -> Int-> [Int16]
triW period i = triW' period (-32766)
	           $ (44100 * 3)
	               `div` (samplesPerWavelength period)
       where
           samplesPerWavelength :: Float -> Int
	   samplesPerWavelength freq = truncate $ 44100.0 / freq

triW' :: Float -> Int -> Int -> [Int16]
triW' period tempSample ampStep
       | abs tempSample > 32767 =
	       let new = fromIntegral $ tempSample + ampStep in
	           new:new:triW' period (tempSample - ampStep) (-ampStep)
       | otherwise =
	   fromIntegral tempSample
	   :fromIntegral tempSample:triW' period (tempSample + ampStep) ampStep

Playing notes

Okay, now you can play frequencies. You should create a simple array which allows a note number to be converted to a frequency, and you'll be able to play notes.

Wavetable Synthesis

As I promised, here, we do some wavetable synthesis: just play some waveforms a few milliseconds.

int main()
{
    double freqs[] = {
#define X(A) A,
# include "freqs.def"
#undef X
    };

    srand(time(nullptr));

    // One theme I composed for Subliminal AEon
    int note = 0;
    int song[] = {
	48, 55, 56, 48, 51, 55, 50, 53,
	48, 55, 56, 48, 51, 55, 50, 53,
	47, 50, 51, 55, 51, 50, 51, 48,
	47, 50, 51, 55, 51, 50, 51, 48
    };

    while (true)
    {
	for (size_t i = 0; i < 5; ++i)
	{
	    // Second parameter is the number or milliseconds to play
	    Sinus(freqs[song[note]], 6);
	    Square(freqs[song[note]], 5);
	    Sinus(freqs[song[note]], 7);
	    Saw(freqs[song[note]], 3);
	    Triangle(freqs[song[note]], 3);
	    Sinus(freqs[song[note]], 7);
	    White(2);
	    Sinus(freqs[song[note]], 7);
	}
	note = (note + 1) % (sizeof (song) / sizeof (song[0]));
    }
    return (0);
}

And just play that using

./a.out | aplay -c 2 -f U16_LE -r 44100

You may have to adjust parameters of aplay depending on your architectures

Going further

New waves

As an example, you can now merge waveforms by adding or multiplying them to create additionnal waveforms. Feel free to create whatever you want when merging.

triPlusSquare :: Float -> [Int16]
triPlusSquareW freq = zipWith addW (triW freq) (squareW (freq * 2))

addW :: Int16 -> Int16 -> Int16
addW x y = (x `div` 2 + y `div` 2)

Filters

It sounds really acid. We will give us the possibility (in the next article :D) to use Fourier's transform to create {low,high}-pass filters.

Conclusion

I was really proud to create my own synthesizer, you should do it too :D !

Make a perfect bridge between templates and dynamic execution

Introduction

Global View

This article isn't about automata, they just are a pretext for me to introduce the issue solved with this bridge-design

I'm currently studying at the Epita Research and Development Laboratory, working on the Vaucanson project. Because of the genericity we want, we make an overuse of template programming. We have automata templated with contexts, algorithms valids with automaton of precise contexts and... we wanted to put all this people in the same house. To be more precise, we have automata that can be templated with chars, words, automata and regexp, and labeled with weights in R, Z, Zmin, with epsilons, and bools. Label type and automaton type defines the Context of our automatonSome of our algorithms works with char automaton, ignoring the labelling type. So, our automata are:

template <class Context>
class automaton { ... };

And our algorithms looks like:

template <class Automaton>
Automaton Determinize(const Automaton& aut) { ... }

The issue

We are perfectly compile-time with this design... But we want to be able to use it a runtime : why the hell should the user recompile to use another kind of automata ?

We have:

  1. Automata templated by their context
  2. Algorithms templated by automata. Their prototype are totally free

And we want to be able to instantiate automata of certain type depending on runtime execution (first issue), and then, call algorithms on these automata (second issue)

Dealing with dynamic automata

Automaton handling is quite simple: we just have to inherits our templated automata from a mother class, say dynaut, for dynamic automaton, and manipulate it with a pointer.

template <class Contex>
class automaton : public dynaut { ... };

We simply upcast our automaton<T> to dynaut automaton, and then downcast it into their static type when needed for the algorithm. That's a great improvement from calling a virtual function every time! So, our dynamic automaton will contain context informations in a member string, and we have to use that to downcast it.

Algorithms

The second part is more complicated. We want our algorithms to be functors, so that they can hold their code and their documentation in a single entity.

template <class Aut>
class Determinize
{
    Aut operator()(const Aut& aut) { ... }

    std::string Doc() { return "documentation"; }
};

To use these algorithms we need to instantiate:

  1. The template specialization of this code using Algorithm<automaton<context>> somewhere
  2. An instance of this specialization

And the most practical datastructure which comes in mind would be a std::map<std::string, ?Algorithm?> to register our algorithms and call them with register[my_dynaut.context()](my_dynaut);

But it's too simple :) Remind: algorithms can have any prototype. Prepare yourself to the heat of Hell, we starts C++11's black magick

Abracadabra

The first thing to do is to inherit our algorithms from a non-templated base, itself deriving from an empty class which represents our algorithms.

class Algo
{
    virtual std::string Doc() = 0; // documentation
};

class DeterminizeBase : public Algo
{
    virtual dynaut& operator()(const dynaut&) = 0;

    static std::string name()
    {
        return "determinize";
    }
};

template <class Aut>
class Determinize : public DeterminizeBase
{
    Aut operator()(const Aut& aut) { ... }

    std::string Doc() { return "documentation"; }

    dynaut& operator()(const dynaut& aut)
    {
        return operator()(dynamic_cast<Aut&>(aut));
    }

    static std::string name()
    {
        return std::string("determinize") + Aut::name();
    }
};

The Algo base class will be useful later. The DeterminizeBase allows to fetch the prototype of Determinize (as any other algorithm) without any consideration of the automaton type. See how the actual implementation in Determinize downcasts the dynaut in order to call the real implementation, using overload.

Our register can now be a std::map<std::string, std::unique_ptr<Algo>> and we need to wrap it in a class to handle function calls and registration.

Above, we see that we included methods name() which provides us unique identifiers for those methods, for indexing them in the register

class AlgoReg
{
    private:
        std::map&ltstd::string, std::unique_ptr<Algo>> reg_;

    public:
        template <class Algorithm>
        void Register()
        {
            reg_[Algorithm::name()] = std::make_unique<Algorithm>();
        }

        ??? Call(???) { ??? }
};

The Register() method is straightforward. We can use it like that:

AlgosReg reg; //you can turn it to a singleton
reg.Register<Determinize<automaton&ltcontext_lal_char_b>>>();

The Call() is really much more complicated. Focus on prototype. First, we want to be able to tell which algorithm we want:

template <class Algorithm>
??? Call(???) { ??? }

We don't know how much parameters takes the algorithm, nor their type:

template <class Algorithm, class... Args>
??? Call(Args&&... args) { ??? } //The move semantics is not mandatory

And it should return the value returned by the algorithm itself:

template <class Algorithm, class... Args>
auto Call(Args&&... args)
-> decltype (std::declval<Algorithm>()(args...))
{ ??? }

This deserves a deeper explanation. decltype is a C++11 keyword which evaluates to the type of the expression in argument ; decltype (3) a; is equivalent to int a;. std::declval is made for use in decltype expressions, and returns an "instance" of its template parameter. We then call operator() with provided args, and all this expression evaluates to the return type of our algorithm.

Dive into the implementation. We have to build the string corresponding to the right instanciation we have to call. In order to do that, we have to iterate through arguments, and concatenate string-context of our dynamics automata. This concept is shown here:

class VarStringBuilder
{
    // if there is no argument, return the empty string
    static inline std::string Build()
    {
        return "";
    }

    // else, if the first argument is a dynaut, extract its context, then
    // recurse on the next argument
    template <class T, class... Ts>
    static inline
    std::enable_if<std::is_same<T, dynaut>::value, std::string>::type
    Build(const T& dynaut, Ts... args)
    {
        return dynaut.ctx + VarStringBuilder::Build(args...);
    }

    template <class T, class... Ts>
    static inline
    std::enable_if<!std::is_same<T, dynaut>::value, std::string>::type
    Build(const T& dynaut, Ts... args)
    {
        return VarStringBuilder::Build(args...);
    }
};

And we're almost done! The final code of our Call() method is:

template <class Algorithm, class... Args>
auto Call(Args&&... args)
-> decltype (std::declval<Algorithm>()(args...))
{
    auto fun = reg_[Algorithm::name() + VarStringBuilder::Build(args...)]
    return (*dynamic_pointer_cast<Algorithm>(fun))(args...);
}

And we enabled compile-time time checking, on a function with a complete variadic signature. Use it:

AlgosReg reg; // Really, make it as a singleton
reg.Register<Determinize<automaton<lal_char_b>>(); //register

...

automaton<lal_char_b> static_aut;
dynaut& da = static_aut;
reg.Call<DeterminizeBase>(da);

Conclusion

This pattern allows a perfect bridge between the static world and dynamic execution. It keeps C++'s type checking (better than using boost::any), resolves correctly overloads, and provides multimethods for free. Use it with care.

Metaprogramming a full Virtual Machine

Metaprogramming a full Virtual Machine

Introduction

If you are currently in Epita, you might be coding Corewar. In the second part of the project, you have to code the Virtual Machine.

If you are not concerned with Corewar, you may be interested in VM in any way, and this article won't be Corewar specific.

In the end, you will be able to write:

RegisterOpcode<0x83, Instr<ADD, Register<A>, Register<E>>>();

Decoding will be in O(1), and efficiency of our code will good, since operands will be bounds with operation at compile-time. You kill one level of indirection.

Concepts

Virtual Machine

A virtual machine is a program which simulates a processor and a memory. It does it the following way :

  1. Fetch instruction
  2. Decode
  3. Execute
  4. Restart

Each instruction is associated to an opcode in binary. This opcode contains the instruction, and information about operands (the value itself, or its adress into memory, or in which register it is).

All this pipeline is static. Instructions won't change etc. This is a perfect playground for Metaprogramming. Perfect. We handle instructions with operands from Register.

Memory

The memory is just an indexed linear array, starting at address 0 or 1 depending on your architecture. A word may be 8 bits, 16 bits, or whatever depending on your architecture.

CPU

Since I first developped this library to code a Gameboy emulator, we will handle registers, even if it doesn't improve performance. It will improve genericity of your code. It is composed by the following element:

  1. Some registers, indexed with a letter.
  2. A "flags" byte.
  3. A stack pointer (Word), which points to the top of the stack.
  4. A program counter (Word), wich points to the instruction currently executed.

Instruction

Our instructions will be composed by : an operation, one or two operands. Each operand can be a register, an address in the memory, an immediate value located in the next byte(s) of the program.

Memory

This is really simple to write. In the source code of my original emulator, this is a little bit more complicated: devices (like graphic card, sound module etc) are mapped directly into the adress space:

#include <cstdint>

uint8_t ram_[RAM_SIZE];

CPU

No surprise here, the basic code is really straight forward

class CPU
{
    public:
        CPU();
        void Process();

    private:

        byte _regs[REG_NBR];
        byte _flags;
        word _sp;
        word _pc;
        Ram _addr;
        std::function<void(CPU*)> _instr[256];
};

But this class does nothing. We need to add more concepts. Something which could be noted is the _instr attribute: this is an array of functions returning nothing (if there is a return value, it will be stored into the memory or a register) and takes a context, a CPU*, the this pointer of our CPU.

Operands source

We want to index our registers with a good syntax, and at compile-time

enum class RegisterLetter {A = 0, B, C, D, E, F };

template <int Reg>
class Register
{
    static inline byte Get(CPU* proc)
    {
        return proc->_regs[Reg];
    }

    static inline void Set(CPU* proc, byte val)
    {
        proc->_regs[Reg] = val;
    }
};

Here is defined an important concept: the ValueInterface, and its two static methods, Get and Set. Great. now we can access our registers with compile-time adressing doing:

byte reg_a = Register<A>::Get(cpu);

Similarly, we have to write a wrapper around around RAM to match this interface too:

template <class Addr>
struct ToAddr
{
    static inline byte Get(CPU* p)
    {
        return p->_addr.Get(Addr::Get(p));
    }

    static inline void Set(CPU* p, byte val)
    {
        p->_addr.Set(Addr::Get(p), val);
    }
};

And, we have to be able to take operands from the next byte in the program code

struct NextByte
{
    static byte Get(CPU* p)
    {
        ++p->_pc;
        return p->_addr.Get(p->_pc);
    }
};

This code is not really surprising. We increment the program counter to the next byte, and fetch the value here.

Instructions

Here starts the metaprogramming symphony. We need the general concept of an instruction, expressed with templates

template <
  template <class, class>
    class Action,
  class Op1,
  class Op2
>
class Instr
{
    public:
        static void Do(CPU* p)
        {
            Action<Op1, Op2>::Do(p);
        }
};

Okay, take a breath. We defined another concept: ExecutableInterface, caracterized with the presence of T::Do(CPU*). This class is the general interface of any instruction. Three template parameters:

  1. An operation: this is a template parameter, itself templated with two template parameters, the operands. Because we want to write ADD<Register<A>, Register<B>>::Do(cpu);
  2. The first operand<
  3. The second operand

This class just binds

Instr<ADD, Register<A>, Register<B>>::Do(cpu);

to

Add<Register<A>, Register<B>>::Do(cpu);

And after this wrapper, we have to define a real instruction:

template <class A, class B>
struct ADD
{
    static void Do(CPU* p)
    {
        int res = B::Get(p) + A::Get(p);

        int flag = 0;
        flag = (res == 0 ? (1 << 7) : 0);
        flag += ((res >> 4) ? 1 << 5 : 0);
        if (std::is_same<decltype(A::Get(p)), byte>::value)
            flag += ((res >> 8) ? 1 << 4 : 0);
        else
            flag += ((res >> 16) ? 1 << 4 : 0);
        CPU::Register<Z80::F>::Set(p, flag);
        A::Set(p, res);
    }
};

This class represents the ADD asm instruction, totally with generic inputs. Template parameters A and B are just locations of operands. I let you as an exercise to write a lot of operations. A trickier part is the range of JP instructions. JP, JPZ, JPNZ etc. We first write a JP templated with a Test class (do we jump ?), the location where is the adress (a register ? a pointer in the memory ?) and a dummy parameter, just to match the two template parameter pattern we already defined:

template <class Test, class A, class>
struct JP_Impl
{
    static void Do(CPU* p)
    {
        word jmp = A::Get(p);
        if (Test::Do(p))
            Z80::Register<CPU::PC>::Set(p, jmp-1);
    }
};

... and some tests to work with

struct True
{
    static inline bool Do(CPU*)
    {
        return true;
    }
};


struct IfZ
{
    static inline bool Do(CPU* p)
    {
        return CPU::Register<Flags>::Get(p) & 10000000_b;
    }
};

... and define legal instructions from that with templated using from C++11

template <class A, class B>
using JR = JR_Impl<True, A, B>;

template <class A, class B>
using JRZ = JR_Impl<IfZ, A, B>;

Processing the whole thing

Okay, we did a lot of thing, but nothing works. We still have to associate our instructions with opcodes, and process the whole thing. The first goal will be reached with a simple function. We already added a _instr member in our CPU, now we will fill it.

// Register function
template <unsigned char Opcode, class Inst>
void CPU::RegisterOpcode()
{
    _instr[Opcode] = std::function<void(Z80*)>(&Inst::Do);
}

// Register some operations somewhere
...
    RegisterOpcode<0xBB, Instr<CP, Register<A>, Register<E>>>();
    RegisterOpcode<0xBC, Instr<CP, Register<A>, Register<H>>>();
    RegisterOpcode<0xBD, Instr<CP, Register&ltA>, Register<L>>>();
    RegisterOpcode<0xBE, Instr<CP, Register<A>, ToAddr<Register<HL>>>>();
    RegisterOpcode<0xBF, Instr<CP, Register<A>, Register<A>>>();
    RegisterOpcode<0xC0, Instr<RETNZ, void, void>>();
    RegisterOpcode<0xC1, Instr<POP, Register<BC>, void>>();
    RegisterOpcode<0xC2, Instr<JPNZ, NextWord, void>>();
    RegisterOpcode<0xC3, Instr<JP, NextWord, void>>();
    RegisterOpcode<0xC4, Instr<CALLNZ, NextWord, void>>();
    RegisterOpcode<0xC5, Instr<PUSH, Register<BC>, void>>();
    RegisterOpcode<0xC6, Instr<ADD, Register<A>, NextByte>>();
...

We take the pointer to function of our instruction, and associate it whith the opcode. And we need our main loop:

void CPU::Process()
{
    while (true)
    {
        _instr[_addr.Get(_pc)](this);
        ++_pc;
    }
}

And we are done !

Conclusion

That was not so hard, and we have a design easily extensible and efficient. Any questions and appreciations are welcome in the comments !

Video

Bonjour,

Voilà enfin la vidéo, j'espère que vous passerez un bon moment. Désolé pour les baffouilles.

N'hésitez surtout pas à vous exprimer, à critiquer et à poser des questions en utilisant les commentaires.

Les slides

Playing with templates

Playing with templates

Hello,

Some weeks ago, I was trying to simulate a little country : economisc, health, etc. To have a realistic simulation, there is a huge amount of parameters to consider, each of them influencing others in various ways, and each of them is unique. A good playground for some metaprogramming.

A value is updated on Update() call. It undergoes a lot of influences and maybe have to satisfy some conditions and constraints. Let's use variadic templates, and try to obtain a good final result, to have a sexy and beautiful syntax, easily extandable, almost natural english. Something like that:

// let's declare some values
Value<Lodgement>;
Value<Budget>;
Value<Population,
        Do<Population, Plus, Number<50>>,
        MustBe<LessEqThan, Logement>>;

// A main() example
int main(void)
{
    Lodgement::Set(1000);
    Population::Set(200);
    Budget::Set(5000);
    while (true)
    {
        Population::Update();
        std::cout << Parameter<Population>()
            << " people for a capacity of "
            << Parameter<Logement>() << ". Budget : "
            << Parameter<Budget>() << std::endl;
        std::cout << "Buy some lodgements ?" << std::endl;
        int rep;
        std::cin >> rep;
        if (rep)
        {
            BuyBuilding::Do();
        }
    }
    std::cout << Parameter<Logement>() << std::endl;
}

The result will be close than Value<NameOfValue, influences and constraints...>

Start with class Value

template <class... Actions>
class Value;

A simple declaration of a class with variadic templates: some policies.

Templates must be thinked in functionnal: values are not mutables, so arguments list must be used recursively. Start with the final case:

template <class T>
class Value<T>
{
    public:
        static inline bool Update()
        {
            return true;
        }

        static inline void Set(float val)
        {
            Parameter<T>() = val;
        }
}

Some explanations: template parameter T is only the value's name. The Update() function is the most interesting. It is static because the value is unique and return a bool stating if the update succeeded. It can return false if some constraints or conditions was not satisfied. It will be recursively called in policies implementation. The Set() function is very simple, it simply assign the value given in parameter. Talk more about Parameter<T>()

template <class T>
float& Parameter(void)
{
  static std::map<std::type_index, float> values;
  return values[typeid(T)];
}

typeid is an operator which is applied on a type to take some information about it on runtime. So, we couple a std::type_index with a float.

We now have to define the recursive case.

template <template Action, class... Actions, class Me>
class Value<Me, Action, Actions...> : public Value<Me, Actions...>
{
    public:
        static inline bool Update()
        {
            float val = Parameter<Me>();
            Action::Update();
            if (!Value<Me, Actions...>::Update())
            {
                Parameter<Me>() = val;
                return false;
            }
            return true;
        }
};

Template parameters are :

  • Me: the value's name, that we have to remember until the end to use it in Parameter<Me>()
  • Action: the current action.
  • Actions...: variadic template parameter, the rest of actions we have to apply

We inherits value parametrized by the rest of actions, basic technique exposed by Andrei Alexandrescu in his typelists usage.

After considerations about templates, the method is not complicated: we save the current value in val, we do the current action, and if following actions performs bad, we can reset the value to val to rollback changes. Define what is an "operation type": it has a static void Modify(float& val, float rate);. val is the value to modify, rate is an amount, and the implementation describes how to to the modification. Example:

struct Plus
{
    static inline void Modify(float& val, float rate)
    {
        val += rate;
    }
};

Now, define the Do class which will wrap everything and give operands:

template <class What, class Method, class Rate>
struct Do
{
    static inline bool Update()
    {
        Method::Modify(Parameter<What>(), Parameter<Rate>());
        return true;
    }
};

Modify changes the value what if function of the implementation of Method::Modify and with the parameter Rate (another value). If we want to harcode Rate with some numbers, we have to define a simple class:

template <int>
class Number{};

And to specialize Do:

template <class What, class Method, int N>
struct Do<What, Method, Number<N>>
{
    static inline bool Update()
    {
        Method::Modify(Parameter<What>(), N);
        return true;
    }
};

Nothing difficult. That's it. Now, all actions just works. We still have to handle conditions.

Define a template condition class : the classe must define a static bool Check(float param1, float param2); Exemple:

struct MoreThan
{
    static bool inline Check(float v1, float v2)
    {
        return v1 < v2;
    }
};

Define the class which will provide the second operand and wrap it in a MustBe class used later for template pattern matching:

template &lt;class Comparator, class T&gt;
struct MustBe
{
    static bool inline Check(float v)
    {
        return Comparator::Check(Parameter<T>(), v);
    }
};

And the specialization for Number

template <class Comparator, int N>
struct MustBe<Comparator, Number<N>>
{
    static bool inline Check(float v)
    {
        return Comparator::Check(N, v);
    }
};

We can now write conditions like MustBe<LessEqThan, Logement> but conditions are still not handled by Value. Using a condition right now will end in a compilation error: compiler will try to match it with the parameter Action, try to execute Action::Update() which does not exist. We have to specialize Value to match the type MustBe<Comparator, Rate>:

template<class T, class Cmp, class... Actions, class Me>
class Value<Me, MustBe<Cmp, T>, Actions...> : public Value<Me, Actions...>
{
    public:
        static inline bool Update()
        {
            if (MustBe<Cmp, T>::Check(Parameter<Me>()))
            {
                return Value<Me, Actions...>::Update();
            }
            else
            {
                std::cout << "Erf, dépendance non satisfaite" << std::endl;
                return false;
            }
        }
};

Here we are. But there is still an issue.

In the beginning I said that the syntaxe won't be that beautiful. To write Value<Lodgement>, Lodgement need to be previously declared. So, use a macro:

#define VALUE(T) class T : public Value<T>{}
#define PARAM(T, V...) class T : public Value<T, V>{}

And write the final declaration in the main():

VALUE(Logement);
VALUE(Budget);
PARAM(Population,
        Do<Population, Plus, Number<50>>,
        MustBe<LessEqThan, Logement>);

Thanks for reading, do not hesitate to comment. I think my solution is not the best but is not the worst, and is a good usage of typeid (T) operator, nice in an article.

Source is avalaible on my bitbucket

- page 1 of 2