Voting

Category

real language

Bookmarking

Del.icio.us Digg Diigo DZone Earthlink Google Kick.ie
Windows Live LookLater Ma.gnolia Reddit Rojo StumbleUpon Technorati

Language C++

(extreme template metaprogramming)

Date:04/20/05
Author:Richard Wolf
URL:n/a
Comments:5
Info:n/a
Score: (2.97 in 152 votes)
/* Richard Wolf, 2002

99 bottles of beer on the wall in an extreme template-metaprogramming style.  
It uses template specialisation to decided how to print numbers, whether 
'bottles' should be plural, and to finish the song correctly.  
Macros are used to generate the large number of specialisations required for
printing numbers nicely, eg 45 printed as "forty five" with pretty_print<45>().

Note that this will probably no compile immediately, since it requires the 
compiler to instantiate templates to a depth of (number of bottles + 4).  
Either reduce the number of starting bottles, or increase the maximum 
template depth on your compiler. 

Eg. using g++ use the -ftemplate-depth-103 command line option.

Tested on gcc, other compilers at your risk
*/

#include <iostream>

using namespace std;

template<bool small, int I>
struct pretty_printer;

#define SMALL_PRETTY_PRINTER(num, string) \
template<>\
struct pretty_printer<true, num>\
{\
    static void print()\
    {\
        cout << string;\
    }\
};

SMALL_PRETTY_PRINTER(0, "No")
SMALL_PRETTY_PRINTER(1, "One")
SMALL_PRETTY_PRINTER(2, "Two")
SMALL_PRETTY_PRINTER(3, "Three")
SMALL_PRETTY_PRINTER(4, "Four")
SMALL_PRETTY_PRINTER(5, "Five")
SMALL_PRETTY_PRINTER(6, "Six")
SMALL_PRETTY_PRINTER(7, "Seven")
SMALL_PRETTY_PRINTER(8, "Eight")
SMALL_PRETTY_PRINTER(9, "Nine")
SMALL_PRETTY_PRINTER(10, "Ten")
SMALL_PRETTY_PRINTER(11, "Eleven")
SMALL_PRETTY_PRINTER(12, "Twelve")
SMALL_PRETTY_PRINTER(13, "Thirteen")
SMALL_PRETTY_PRINTER(14, "Fourteen")
SMALL_PRETTY_PRINTER(15, "Fifteen")
SMALL_PRETTY_PRINTER(16, "Sixteen")
SMALL_PRETTY_PRINTER(17, "Seventeen")
SMALL_PRETTY_PRINTER(18, "Eighteen")
SMALL_PRETTY_PRINTER(19, "Nineteen")

#undef SMALL_PRETTY_PRINTER

template<int ones>
inline void 
print_ones();

#define ONES_PRINTER(ones, string) \
template<> \
inline void \
print_ones<ones>() \
{\
  cout << string;\
}

ONES_PRINTER(0, " ")
ONES_PRINTER(1, " one")
ONES_PRINTER(2, " two")
ONES_PRINTER(3, " three")
ONES_PRINTER(4, " four")
ONES_PRINTER(5, " five")
ONES_PRINTER(6, " six")
ONES_PRINTER(7, " seven")
ONES_PRINTER(8, " eight")
ONES_PRINTER(9, " nine")

#undef ONES_PRINTER

template<int tens>
inline void
print_tens();

#define TENS_PRINTER(tens, string) \
template<> \
inline void \
print_tens<tens>() \
{\
  cout << string;\
}

TENS_PRINTER(2, "Twenty")
TENS_PRINTER(3, "Thirty")
TENS_PRINTER(4, "Forty")
TENS_PRINTER(5, "Fifty")
TENS_PRINTER(6, "Sixty")
TENS_PRINTER(7, "Seventy")
TENS_PRINTER(8, "Eighty")
TENS_PRINTER(9, "Ninety")

#undef TENS_PRINTER

template<int I>
struct pretty_printer<false, I>
{
    static void print(){
        print_tens<(I - I%10)/10>();
        print_ones<(I%10)>();
    }
};

template<int I>
void pretty_print()
{
    pretty_printer<(I<20), I>::print();
}

template<int I>
inline void
BottlesOfBeer()
{
    pretty_print<I>();
    cout << " bottles of beer" ;
}

template<>
inline void
BottlesOfBeer<1>()
{
    pretty_print<1>();
    cout << " bottle of beer" ;
}

template<int I>
inline void
BottlesOfBeerOnTheWall()
{
    BottlesOfBeer<I>();
    cout << " on the wall";
}

template<int I>
inline void stanza()
{
    BottlesOfBeerOnTheWall<I>(); 
    cout << ",\n";
    BottlesOfBeer<I>(); 
    cout <<",\n";
}

template<int I>
inline void bridge()
{
    cout   << "Take one down, pass it around," << endl;
    BottlesOfBeerOnTheWall<I-1>();
    cout <<",\n";
}

template<>
inline void bridge<0>()
{
    cout << "Go to the store and buy some more," << endl;
    BottlesOfBeerOnTheWall<99>();
}

template<int I>
inline void verse()
{
    stanza<I>();
    bridge<I>();
}

template<int I>
inline void sing () 
{
    verse<I>();
    cout << endl;
    sing<I-1>();
} 


template<>
inline void sing<0> ()
{
    verse<0>();    
}

int main () {
  sing<99>();
}

Download Source | Write Comment

Alternative Versions

VersionAuthorDateCommentsRate
hacking styleTim Robinson04/20/0514
object-oriented versionTim Robinson04/20/054
meta programmingArion Lei04/20/055
Preprocessor & self-include recursionChocapic02/27/076
feature creepJono04/27/091
most extreme template metaprogrammingHenrik Theiling12/04/110
7Jono04/15/090
ob fuscatedTapi (Paddy O'Brien)08/11/054
GUI versionMartyn Davies05/28/051

Comments

>>  cbuilder said on 06/28/05 09:15:39

cbuilder If you compile with mingw - think of the following: add the parameter "-ftemplate-depth-999", otherwise you can't compile this (very hard ;)) source

>>  andrea0102 said on 08/08/07 02:44:24

andrea0102 how can i let it run?

>>  Chad said on 10/10/07 15:27:21

Chad I would replace ONES_PRINTER( 0, " " ) with ONES_PRINTER( 0, "" ) to eliminate the double spacing. I would also replace the calculation in print_tens<(I - I%10)/10>(); with the simpler: print_tens<I/10>(); thanks to integer division and the rules of rounding, it is superfluous to subtract I%10 before dividing. I would also finish with a cout << endl in bridge<0>() becuase the text is not terminated with a newline. Good job!

>>  Jono said on 04/30/09 01:21:24

Jono yeah, i initially wrote mine by instantiating the templates with something like...

//___________________________________________
template <unsigned n>
inline void print_beer()
{
verse(n);
print_beer<n-1>();
}

template <>
inline void print_beer<0>()
{
verse(0);
print_beer<99>();
}


int main(int argc, char* argv[])
{
print_beer<99>();
return 0;
}
//___________________________________________

this worked very well, but i wanted to expand warehouse space to shelve 4 billion bottles, so i decided that approach was no longer ideal...

something with a recursive descent through significant places, now.. hmmm.... i'll be back!





>>  ech said on 11/11/09 21:05:34

ech To fully embrace the paradigm, the program shouldn't compile, and instead should print out the verses as compiler errors.

Download Source | Write Comment

Add Comment

Please provide a value for the fields Name, Comment and Security Code.
This is a gravatar-friendly website.
E-mail addresses will never be shown.
Enter your e-mail address to use your gravatar.

Please don't post large portions of code here! Use the form to submit new examples or updates instead!

Name:

eMail:

URL:

Security Code:
  
Comment: