Monday, July 14, 2014

Runtime value to Compile time value/action conversion via variadic selection tree

With c++11 some of the older macro or table based generator techniques can be tossed out. One of these is the runtime to compile time translation of a value.

For example lets say you have a table of of idents and related actions. 2 possible solution are
* Populate an array with function pointers.
* Generate a switch statement with marcos..

But c++11 has opened a new and what seems to flexible possibility: The ability to use a variaditic template to generated a reduction tree. This seems to have some very interesting possibilities.
* The redux tree can have its action templated... (But I seemed to have a compiler bug that stops it working)
* Its not locked into a single function signature a "Param..." argument pack gives it the flexibility to adapt on both entry and exit of the selection tree.
* good use of inlining lets the tree flatten out and the compiler gets to hit it with opt routines like the macro generated select version.


#include <iostream>
#include <cstring>

// -----------------------------------------------
//                         IDs
// -----------------------------------------------

enum {
    UNKNOWN = 0,
    MIN_TAG = UNKNOWN,
    INT,
    FLOAT,
    MAX_TAG
};

// -----------------------------------------------
//            ID -> Type translation trait
// -----------------------------------------------

template <int Tag> struct Trait {};

template <> struct Trait<UNKNOWN>
{
    typedef void Type;
    static const char* id() { return "unknown";   }
};

template <> struct Trait<INT>
{
    typedef int Type;
    static const char* id() { return "int";   }
};

template <> struct Trait<FLOAT>
{
    typedef float Type;
    static const char* id() { return "float"; }
};

// -----------------------------------------------
//                    Execution point
// -----------------------------------------------

template <int I>
void function(char* data)
{
    typename Trait<I>::Type value =
        (*static_cast<typename Trait<I>::Type*>(
            static_cast<void*>(data)));

    std::cout
        << " type:"  << Trait<I>::id()
        << " value:" << value
        << "\n";
}

template <>
void function<UNKNOWN>(char* data)
{
    std::cout
        << " unknown type number given!"
        << "\n";
}

template <>
void function<MAX_TAG>(char* data) { function<UNKNOWN>(data); }

struct DoAction
{
    template <int I, typename... Params>
    static void call(Params... params)
    {
        function<I>(params...);
    }
};

// -----------------------------------------------
//  SelectTree: runtime -> complie time selector
// -----------------------------------------------

template <typename Action, int min, int max>
struct SelectTree
{
    enum { mid = (max+min)/2 };

    template <typename... Params>
    static void call(int i, Params... params)
    {
        if (i <= mid) SelectTree<Action,min  ,mid>::call(i, params...);
        else          SelectTree<Action,mid+1,max>::call(i, params...);
    }
};

template <typename Action, int value>
struct SelectTree<Action, value, value>
{
    template <typename... Params>
    static void call(int i, Params... params)
    {
        //Action::call<value>(params...);    // compiler bug!
        DoAction::call<value>(params...);
    }
};

// -----------------------------------------------
//                        MAIN
// -----------------------------------------------

int main()
{
    float x = 1.0;

    char data[4];
    std::memcpy(data, &x, sizeof(x));

    int sel = 3;
    std::cin >> sel;

    SelectTree<DoAction,MIN_TAG,MAX_TAG>::call(sel, data);
}


the output looks like
$ a.exe
1
 type:int value:1065353216
$ a.exe
2
 type:float value:1
$ a.exe
-2
 unknown type number given!
$ a.exe
0
 unknown type number given!
$ a.exe
123213
 unknown type number given!

No comments:

Post a Comment