syntax highlight

Tuesday, 14 September 2010

Template Metaprogramming XIV: Marathon

If you remember previous entry, we got our evil device to the point of getting a specific instance using only a type hint. Now we need to put all the code together. I won't add much to the code, you should be able to parse it yourself.

/***********************************************/
struct NIL {
    typedef NIL head;
    typedef NIL tail;
};

template <class H, class T=NIL> struct LST {
    typedef H head;
    typedef T tail;
};
/***********************************************/

/***********************************************/
template <class X, class Y> struct Eq { static const bool result = false; };
template <class X> struct Eq<X, X> { static const bool result = true; };
/***********************************************/

/***********************************************/
template <class Elm, class LST> struct Position {
private:
    typedef typename LST::head H;
    typedef typename LST::tail T;
    static const bool found = Eq<H, Elm>::result;
public:
    static const int result = found? 1 : 1 + Position<Elm, T>::result;
};

template <class Elm> struct Position<Elm, NIL> {
    static const int result = 0;
};
/***********************************************/


/***********************************************/
template <typename LST, int N> struct Nth {
	typedef typename LST::Tail Tail;
	typedef typename Nth<Tail, N-1>::result result;
};

template <typename LST> struct Nth<LST, 0> {
	typedef typename LST::head result;
};
/***********************************************/


/***********************************************/
template <typename Lst> struct Instances {
    typedef typename Lst::head Elm;
    Elm instance;
    Instances<typename Lst::tail> next;
};
template <> struct Instances<NIL> {};
/***********************************************/


/***********************************************/
template <int N, typename TypeLst> struct NthInstance {
    // This one isnt easy...
    
    // This is the next type in the list
    typedef typename TypeLst::tail TypeNext;

    //  * Nth::result is the Nth type in Lst (i.e. char, int, ...)
    typedef typename NthInstance<N-1, TypeNext>::NthInstanceType NthInstanceType;

    //  * typename Nth::result & is a reference to said type and the ret type
    template <typename InstancesLst>
    static NthInstanceType& get(InstancesLst &instances_lst) {
        return NthInstance<N-1, TypeNext>::get(instances_lst.next);
    }
};

// Remember, just for fun we choose a 1-based system (wtf..)
template <typename TypeLst> struct NthInstance<1, TypeLst> {
    typedef typename TypeLst::head NthInstanceType;

    template <typename InstancesLst>
    static NthInstanceType& get(InstancesLst &instances_lst) {
        return instances_lst.instance;
    }
};
/***********************************************/


class Facade {
    typedef LST<int, LST<char, LST<double> > > Lst;
    Instances<Lst> instances;

    public:
    template <typename PK> int find(PK) {
        return Position<PK, Lst>::result;
    }

    template <typename PK>
    // This is a difficult one... it should be parsed like this:
    //  1) Get the desired instance position using Position::result 
    //  2) Get the type @ the desired position with NthInstance::Type
    //  3) Define said type as a return type (with an & at the end, i.e. make
    //      it a reference to the return type)
    typename NthInstance< Position<PK, Lst>::result, Lst >::NthInstanceType&
    get_instance(PK) {
        const int idx_position = Position<PK, Lst>::result;
        typedef typename NthInstance<idx_position, Lst>::NthInstanceType IdxType;
        IdxType &idx = NthInstance<idx_position, Lst>::get( instances );
        return idx;
    }
};

#include <iostream>
int main() {
    Facade f;
    int &a = f.get_instance(1);
    char &b = f.get_instance('a');
    double &c = f.get_instance(1.0);
    a = 42; b = 'n'; c = 4.2;
    std::cout << f.get_instance(1) << "\n";
    std::cout << f.get_instance('a') << "\n";
    std::cout << f.get_instance(1.0) << "\n";
    a = 43; b = 'm'; c = 5.2;
    std::cout << f.get_instance(1) << "\n";
    std::cout << f.get_instance('a') << "\n";
    std::cout << f.get_instance(1.0) << "\n";
    return 0;
}

The only thing missing now is a map, to convert a primitive type to an index type, but that's trivial and so it will be left as an exercise for the reader (?). We just implemented the most evil code in the whole world. Next time, the conclusions.

No comments:

Post a Comment