8 Replies - 16160 Views - Last Post: 06 December 2011 - 04:02 PM

#1 ishkabible  Icon User is offline

  • spelling expret
  • member icon




Reputation: 1622
  • View blog
  • Posts: 5,709
  • Joined: 03-August 09

C++ Challenge: Template Meta Programming

Post icon  Posted 05 December 2011 - 09:16 PM

C++ Challenge: Template Meta Programming

Template Meta Programming (TMP) was accidentally discovered and was not an intended feature of the language. It has many applications such as generating complex code at compile time and writing compile time checks to see if a type satisfies a given condition. It is a Turing complete language in and of itself. It is purely functional as well. Like all functional languages it can manipulate lists. Lists can be represented as a type which stores 2 other types one of which can be another pair; that can continue until a terminating value is reached. Functions can be represented as template classes.

I present to you a small framework for creating template meta lists and a couple of meta functions which can be applied to lists using a left fold. Your challenge is to write a class (or meta function) that performs a left fold. 2 test cases are in used in main.

#include <iostream>

//specil type for ending lists
struct meta_null {};

//type to hold value pairs; these pairs can form lists
template<class Head, class Tail>
struct meta_pair {
	typedef Head head;
	typedef Tail tail;
};

//a simple type that holds a value
template<int x>
struct meta_int {
	static const int value = x;
};

template<class I1, class I2>
struct add {
	typedef meta_int<I1::value + I2::value> value;
};

template<class I1, class I2>
struct mul {
	typedef meta_int<I1::value * I2::value> value;
};

int main() {
	typedef meta_pair<meta_int<1>,
			meta_pair<meta_int<2>,
			meta_pair<meta_int<3>,
			meta_pair<meta_int<4>,
            meta_pair<meta_int<5>, meta_null> > > > > test_list;
	//find the sum of values in the list
    std::cout<<fold_l<add, meta_int<0>, test_list>::value::value;
    std::cout<<'\n';
	std::cout<<fold_l<mul, meta_int<1>, test_list>::value::value;
    return 0;
}



For added challenge write a...

  • right fold function
  • a map function
  • a sum function (sums a list)
  • a product function (product of list).
  • something i haven't thought of :)


Also, if you have any other TMP goodies, It would be nice if you could posts those too :)


hint 1:
Spoiler


hint 2:
Spoiler


hint 3:
Spoiler


If your struggling with TMP or simply want to know more about it, i recommend reading this article which compares TMP to Haskell. The same author also has this article on TMP

This post has been edited by ishkabible: 06 December 2011 - 03:59 PM


Is This A Good Question/Topic? 3
  • +

Replies To: C++ Challenge: Template Meta Programming

#2 sepp2k  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 2113
  • View blog
  • Posts: 3,235
  • Joined: 21-June 11

Re: C++ Challenge: Template Meta Programming

Posted 06 December 2011 - 07:04 AM

Is there a particular reason that you chose meta_null as the identity element for summation instead of meta_int<0>? Semantically it seems very strange to me that a binary addition function would even accept the empty list as a valid operand.

Oh, and regarding the rules of the contest: Is it okay if we modify the given template to some degree in our solution? And are C++11 features allowed (specifically I'm thinking about introducing a helper-template that allows easier construction of lists using variadic templates).

This post has been edited by sepp2k: 06 December 2011 - 07:08 AM

Was This Post Helpful? 1
  • +
  • -

#3 Karel-Lodewijk  Icon User is offline

  • D.I.C Addict
  • member icon

Reputation: 449
  • View blog
  • Posts: 849
  • Joined: 17-March 11

Re: C++ Challenge: Template Meta Programming

Posted 06 December 2011 - 08:44 AM

I was experimenting with something similar using c++11 variadic templates. It is something like the new std::tuple. Look at the main fucntion to see what it actually does.

Not actually an entry to the competition as the data is not meta_data, the types are, but interesting non the less.

Spoiler


compiled with g++ -std=c++0x

with g++ 4.6.1

Is that enough template specialization and template recursion for your taste ?


Basically the core of the idea is this.

template<typename T, typename... tailT> 
class Tuple : public Tuple<tailT...> {
  public:
    T head
};

template<> 
class Tuple<> {}



You have a type that takes any number of template parameters and creates a list. Where it inherits from the list that has all elements but the first and keeps a head element. Now head/tail functions are obvious. Mind some things are needlessly complex as I had to work with an incomplete implementation of c++11.

Edit: Added an actual foldl/foldr/sum/product/map, easy enough.

This post has been edited by Karel-Lodewijk: 06 December 2011 - 10:28 AM

Was This Post Helpful? 0
  • +
  • -

#4 ishkabible  Icon User is offline

  • spelling expret
  • member icon




Reputation: 1622
  • View blog
  • Posts: 5,709
  • Joined: 03-August 09

Re: C++ Challenge: Template Meta Programming

Posted 06 December 2011 - 09:14 AM

View Postsepp2k, on 06 December 2011 - 02:04 PM, said:

Is there a particular reason that you chose meta_null as the identity element for summation instead of meta_int<0>? Semantically it seems very strange to me that a binary addition function would even accept the empty list as a valid operand.

Oh, and regarding the rules of the contest: Is it okay if we modify the given template to some degree in our solution? And are C++11 features allowed (specifically I'm thinking about introducing a helper-template that allows easier construction of lists using variadic templates).


1) i didn't really think about actually :/ instead it would be better if i dropped the empty list as a specialization for the add meta function and made the start value of the left fold meta_int<0>. nice catch! i copied the boilerplate from the printer class and i then when i got the error saying that meta_null had no "value" member i just added the specialization to fix it.

2) absolutely. i was hoping someone would bring it up :) varadic templates make my list representation obsolete. feel free to use them instead.

@karel: i'll have to look at that later ;)

This post has been edited by ishkabible: 06 December 2011 - 01:05 PM

Was This Post Helpful? 0
  • +
  • -

#5 sepp2k  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 2113
  • View blog
  • Posts: 3,235
  • Joined: 21-June 11

Re: C++ Challenge: Template Meta Programming

Posted 06 December 2011 - 10:34 AM

Okay, so here's my solution. I implemented the following templates:

  • meta_null and meta_pair to represent lists
  • meta_int_list<int...> to easily create lists of ints
  • meta_int_range<from, to> to easily create lists of ints in a given range
  • foldl to perform a left fold on a list
  • foldr to perform a right fold on a list
  • map to apply a given transformation to each element of a list (uses foldr)
  • filter to create a list containing only the elements matching a given predicate (uses foldr)
  • reverse to reverse a list (uses foldl)
  • sum to sum all elements of an int list
  • product to multiply all elements of an int list
  • meta_for_each to apply a run-time function to each element of a list
  • print_all to print all elements of a list (uses meta_for_each)


Here's the code:

#include <iostream>

// The empty list
struct meta_null {};

// Type to hold value pairs; these pairs can form lists
template<class Head, class Tail>
struct meta_pair {
    typedef Head head;
    typedef Tail tail;
};


// Left fold

template<template<class, class> class F, class Acc, class List>
struct foldl;

template<template<class, class> class F, class Acc>
struct foldl<F, Acc, meta_null> {
    typedef Acc result;
};

template<template<class, class> class F, class Acc, class Head, class Tail>
struct foldl<F, Acc, meta_pair<Head, Tail> > {
    typedef typename foldl<F, typename F<Acc, Head>::result, Tail>::result result;
};


// Right fold

template<template<class, class> class F, class Acc, class List>
struct foldr;

template<template<class, class> class F, class Acc>
struct foldr<F, Acc, meta_null> {
    typedef Acc result;
};

template<template<class, class> class F, class Acc, class Head, class Tail>
struct foldr<F, Acc, meta_pair<Head, Tail> > {
    typedef typename F<Head, typename foldr<F, Acc, Tail>::result>::result result;
};


// Map

template<template<class> class F>
struct apply_and_cons {
    template<class X, class Acc>
    struct invoke {
        typedef meta_pair<F<X>, Acc> result;
    };
};

template<template<class> class F, class List>
struct map {
    typedef typename foldr<apply_and_cons<F>::template invoke, meta_null, List>::result result;
};


// Helper template to simulate a compile-time if-statement

template<bool cond, class then, class els>
struct meta_if;

template<class then, class els>
struct meta_if<true, then, els> {
    typedef then result;
};


template<class then, class els>
struct meta_if<false, then, els> {
    typedef els result;
};


// Filter

template<template<class> class P>
struct cons_if {
    template<class X, class Acc>
    struct invoke {
        typedef typename meta_if<P<X>::result,
                                 meta_pair<X, Acc>,
                                 Acc>::result result;
    };
};

template<template<class> class P, class List>
struct filter {
    typedef typename foldr<cons_if<P>::template invoke, meta_null, List>::result result;
};


// Template to reverse a list

template<class Acc, class X>
struct cons_reversed {
    typedef meta_pair<X, Acc> result;
};

template<class List>
struct reverse {
    typedef typename foldl<cons_reversed, meta_null, List>::result result;
};


// meta_for_each: Template to call a given function on each element of a list at run-time

template<class Func, class List>
struct meta_for_each;

template<class Func>
struct meta_for_each<Func, meta_null> {
    static void apply(Func f) {}
};

template<class Func, class Head, class Tail>
struct meta_for_each<Func, meta_pair<Head, Tail> > {
    static void apply(Func f) {
        f(Head::value);
        meta_for_each<Func, Tail>::apply(f);
    }
};


// print_all: Prints all elements of a list:
template<class T>
void print(T x) {
    std::cout << x << " ";
}

template<class List, class T>
struct print_all {
    static void apply() {
        meta_for_each<void(*)(T), List>::apply(print<T>);
        std::cout << std::endl;
    }
};

// A simple type that holds a value
template<int x>
struct meta_int {
    static const int value = x;
};


// Helper template to quickly create lists of meta_ints

template<int... xs>
struct meta_int_list;
    
template<>
struct meta_int_list<> {
    typedef meta_null result;
};

template<int h, int... t>
struct meta_int_list<h, t...> {
    typedef meta_pair<meta_int<h>, typename meta_int_list<t...>::result> result;
};


// Helper template to create a list containing a range of integers

template<int from, int to>
struct meta_int_range {
    typedef meta_pair<meta_int<from>, typename meta_int_range<from+1, to>::result> result;
};

template<int from>
struct meta_int_range<from, from> {
    typedef meta_pair<meta_int<from>, meta_null> result;
};


// Templates to add and multiply two integers

template<class X, class Y>
struct add {
    typedef meta_int< X::value + Y::value > result;
};

template<class X, class Y>
struct mult {
    typedef meta_int< X::value * Y::value > result;
};



// Templates to calculate sums and products of lists

template<class List>
struct sum {
    static const int value = foldr<add, meta_int<0>, List>::result::value;
};


template<class List>
struct product {
    static const int value = foldr<mult, meta_int<1>, List>::result::value;
};

// Templates to find whether a meta_int is even or odd
template<class I>
struct even {
    static const bool result = I::value % 2 == 0;
};

template<class I>
struct odd {
    static const bool result = !even<I>::result;
};


// Template to square a meta_int
template<class I>
struct square {
    static const int value = I::value * I::value;
};

int main() {
	typedef typename meta_int_list<1,2,3,4,5>::result test_list;

    print_all<test_list, int>::apply();

    int sum1 = foldl<add, meta_int<0>, test_list>::result::value;
    std::cout << sum1 << std::endl;

    int sum2 = foldr<add, meta_int<0>, test_list>::result::value;
    std::cout << sum2 << std::endl;

    int sum3 = sum<test_list>::value;
    std::cout << sum3 << std::endl;

    int prod = product<test_list>::value;
    std::cout << prod << std::endl;

    typedef typename meta_int_range<1,10>::result one_to_ten;
    print_all<one_to_ten, int>::apply();

    typedef typename filter<even, one_to_ten>::result even_nums;
    print_all<even_nums, int>::apply();

    typedef typename filter<odd, one_to_ten>::result odd_nums;
    print_all<odd_nums, int>::apply();

    typedef typename reverse<one_to_ten>::result ten_to_one;
    print_all<ten_to_one, int>::apply();

    typedef typename map<square, one_to_ten>::result squares;
    print_all<squares, int>::apply();

    return 0;
}



The code requires -std=c++0x because of the usage of variadic templates in meta_int_list. My apologies for the lack of readability.
Was This Post Helpful? 2
  • +
  • -

#6 ishkabible  Icon User is offline

  • spelling expret
  • member icon




Reputation: 1622
  • View blog
  • Posts: 5,709
  • Joined: 03-August 09

Re: C++ Challenge: Template Meta Programming

Posted 06 December 2011 - 01:10 PM

wow! awesome work!! I think I'm going to build off of this when i get home if you don't mind. i want to see if i can implement all of Haskell Data.List functions now :P
Was This Post Helpful? 0
  • +
  • -

#7 sepp2k  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 2113
  • View blog
  • Posts: 3,235
  • Joined: 21-June 11

Re: C++ Challenge: Template Meta Programming

Posted 06 December 2011 - 01:25 PM

View Postishkabible, on 06 December 2011 - 10:10 PM, said:

wow! awesome work!!

Thanks.

Quote

I think I'm going to build off of this when i get home if you don't mind.

I don't mind.

Quote

i want to see if i can implement all of Haskell Data.List functions now :P

That would be a very insane ambitious undertaking. There are quite a lot of them.
Was This Post Helpful? 0
  • +
  • -

#8 Karel-Lodewijk  Icon User is offline

  • D.I.C Addict
  • member icon

Reputation: 449
  • View blog
  • Posts: 849
  • Joined: 17-March 11

Re: C++ Challenge: Template Meta Programming

Posted 06 December 2011 - 02:39 PM

This time I made a real implementation of a meta-int list using c++11 variadic template. I did modify the interface a little, looks much nicer.

#include <iostream>
#include <string>
#include <functional>

template <int n, int n2>
struct add {
    const static int value = n + n2;
};

template <int n, int n2>
struct mul {
    const static int value = n * n2;
};

template <int n>
struct mul2 {
    const static int value = n * 2;
};


//g++ fix
template <int... T>
class Meta_int_list;

template<int n, int... tailT> 
class Meta_int_list<n, tailT...> {
  public:
    typedef Meta_int_list<tailT...> tail_type;
    const static int value = n;

    template <template <int, int> class T, int initial_value>
    struct foldr {
        const static int value = T<n, Meta_int_list<tailT...>::template foldr<T, initial_value>::value>::value;
    };

    template <template <int, int> class T, int initial_value>
    struct foldl {
        const static int value = Meta_int_list<tailT...>::template foldl<T, T<n, initial_value>::value>::value;
    };
    
    struct sum {
        const static int value = foldr<add, 0>::value;
    };

    struct product {
        const static int value = foldr<mul, 1>::value;
    };
    
    static std::ostream& print(std::ostream& out) {
        out << "( " << value, ", ";
        tail_type::print(out);
        return out << ")";
    }
    
    template <template <int> class T>
    struct map {
        typedef Meta_int_list<T<n>::value, T<tailT>::value...> type;
    };
    
};

template<> 
class Meta_int_list<> {
  public:
    typedef void tail_type;
  
    template <template <int, int> class T, int initial_value>
    struct foldr {
        static const int value = initial_value;
    };

    template <template <int, int> class T, int initial_value>
    struct foldl {
        static const int value = initial_value;
    };

    struct sum {
        const static int value = 0;
    };

    struct product {
        const static int value = 1;
    };
    
    template <template <int> class T>
    struct map {
        typedef Meta_int_list<> type;
    };
    
    static std::ostream& print(std::ostream& out) {return out;}
};

int main() {
    std::cout << "2+3 = " << add<2,3>::value << std::endl;
    std::cout << "2*3 = " <<mul<2,3>::value << std::endl;
    std::cout << "2*5 = " << mul2<5>::value << std::endl;
    
    typedef Meta_int_list<1,2,3,4> my_list;
    
    my_list::print(std::cout);
    std::cout << std::endl;
    std::cout << "sum: " << my_list::sum::value << std::endl;
    std::cout << "product: " << my_list::product::value << std::endl;
    
    std::cout << "foldr (sum): " << my_list::foldr<add, 0>::value << std::endl;
    std::cout << "foldl (product): " << my_list::foldl<mul, 1>::value << std::endl;
    
    typedef my_list::map<mul2>::type double_list;
    std::cout << "map (double): ";
    double_list::print(std::cout);
    std::cout << std::endl;    
}



compiled with

g++ -std=c++0x

with g++ 4.6.1


It implements foldl/foldr/sum/product/map

This post has been edited by Karel-Lodewijk: 06 December 2011 - 02:43 PM

Was This Post Helpful? 1
  • +
  • -

#9 ishkabible  Icon User is offline

  • spelling expret
  • member icon




Reputation: 1622
  • View blog
  • Posts: 5,709
  • Joined: 03-August 09

Re: C++ Challenge: Template Meta Programming

Posted 06 December 2011 - 04:02 PM

@Karel-Lodewijk: nice, you added an OO twist to it; i like that :) very clean

@sepp2k: after looking at your implementation of foldl, i decided mine was wrong. sure mine functioned and all but it had some really weird quirks that i just attributed to the nature of TMP. i updated my example code to fit the correct solution :)
Was This Post Helpful? 0
  • +
  • -

Page 1 of 1