// -*- Mode: C++ -*- // Oct 2011 by Henrik Theiling // Licence: BSD // Checked to work with g++-4.6.2 -std=c++0x // Base char type for the strings: you can very well use char32_t, but unfortunately, // the compiler will not show it as a character in its messages, so to demonstrate // 'readable' type names, we use char. typedef char Char; // If template struct If { typedef Then Type; }; template struct If { typedef Else Type; }; // String template struct String { // The funny thing is that we do not store the string at all: // It is only stored in the type itself. If you want a // C string, use 'CArray::value'. }; // Conversion to C values: template struct CArray; template struct CArray< Elem, String > { enum { size = sizeof...(string) }; static Elem const value[size + 1]; }; template Elem const CArray >::value[] = { string..., '\0' }; // Edit a string, list style: template struct Car; template struct Car< String > { enum { value = first }; }; template struct Cdr; template struct Cdr< String > { typedef String Type; }; template struct Nth : public Nth< typename Cdr::Type, n - 1> {}; template struct Nth< String, 0 > : public Car< String > {}; template struct Conc; template struct Conc< first, String > { typedef String Type; }; // Concatenate strings: template struct Concat; template struct Concat { typedef String Type; }; template< Char... part1, Char... part2> struct Concat< String, String > { typedef String Type; }; template struct Concat : public Concat< typename Concat::Type, Rest...> {}; // Join: similar to Concat, but adds a string in between each element. template struct Join; template struct Join { typedef String<> Type; }; template struct Join { typedef Single Type; }; template struct Join : public Concat< First, Separator, typename Join::Type > {}; // Stringify integers: enum class Show0 { no, yes }; template struct Unsigned2String { typedef typename Concat< typename Unsigned2String< (num / 10), Show0::no >::Type, String<('0' + (num % 10))> >::Type Type; }; template<> struct Unsigned2String<0, Show0::yes> { typedef String<'0'> Type; }; template<> struct Unsigned2String<0, Show0::no> { typedef String<> Type; }; // Convert a single letter to upper case: // (FIXME: add Unicode support) template struct ToUpper { enum { value = orig }; }; template<> struct ToUpper<'a'> { enum { value = 'A' }; }; template<> struct ToUpper<'b'> { enum { value = 'B' }; }; template<> struct ToUpper<'c'> { enum { value = 'C' }; }; template<> struct ToUpper<'d'> { enum { value = 'D' }; }; template<> struct ToUpper<'e'> { enum { value = 'E' }; }; template<> struct ToUpper<'f'> { enum { value = 'F' }; }; template<> struct ToUpper<'g'> { enum { value = 'G' }; }; template<> struct ToUpper<'h'> { enum { value = 'H' }; }; template<> struct ToUpper<'i'> { enum { value = 'I' }; }; template<> struct ToUpper<'j'> { enum { value = 'J' }; }; template<> struct ToUpper<'k'> { enum { value = 'K' }; }; template<> struct ToUpper<'l'> { enum { value = 'L' }; }; template<> struct ToUpper<'m'> { enum { value = 'M' }; }; template<> struct ToUpper<'n'> { enum { value = 'N' }; }; template<> struct ToUpper<'o'> { enum { value = 'O' }; }; template<> struct ToUpper<'p'> { enum { value = 'P' }; }; template<> struct ToUpper<'q'> { enum { value = 'Q' }; }; template<> struct ToUpper<'r'> { enum { value = 'R' }; }; template<> struct ToUpper<'s'> { enum { value = 'S' }; }; template<> struct ToUpper<'t'> { enum { value = 'T' }; }; template<> struct ToUpper<'u'> { enum { value = 'U' }; }; template<> struct ToUpper<'v'> { enum { value = 'V' }; }; template<> struct ToUpper<'w'> { enum { value = 'W' }; }; template<> struct ToUpper<'x'> { enum { value = 'X' }; }; template<> struct ToUpper<'y'> { enum { value = 'Y' }; }; template<> struct ToUpper<'z'> { enum { value = 'Z' }; }; // Change the first character of a string to upper case: template struct Capitalise : public Conc< ToUpper< Car::value >::value, typename Cdr::Type > {}; template<> struct Capitalise< String<> > { typedef String<> Type; }; // Sentence components: typedef String<' '> _SPACE_; typedef String<'\n'> _LINEBREAK_; typedef String<'.'> _PERIOD_; typedef String<',',' '> _COMMA_SPACE_; typedef String<'a','n','d'> _and_; typedef String<'a','r','o','u','n','d'> _around_; typedef String<'b','e','e','r'> _beer_; typedef String<'b','o','t','t','l','e'> _bottle_; typedef String<'b','u','y'> _buy_; typedef String<'d','o','w','n'> _down_; typedef String<'g','o'> _go_; typedef String<'i','t'> _it_; typedef String<'m','o','r','e'> _more_; typedef String<'n','o'> _no_; typedef String<'o','f'> _of_; typedef String<'o','n','e'> _one_; typedef String<'o','n'> _on_; typedef String<'p','a','s','s'> _pass_; typedef String<'s','o','m','e'> _some_; typedef String<'s','t','o','r','e'> _store_; typedef String<'t','a','k','e'> _take_; typedef String<'t','h','e'> _the_; typedef String<'t','o'> _to_; typedef String<'w','a','l','l'> _wall_; // Pluralisation: the default is to add an 's': // Irregular plurals can be added by specilising PluralOf<>. // But we don't need that. template struct PluralOf : public Concat > {}; // Singular or Plural depending on a number: template struct SingularOrPlural : public If< (count == 1), Singular, typename PluralOf::Type > {}; // Add a number in front of a noun and adjust the pluralisation // of the noun accordingly: template struct CountNoun : public Concat< typename Unsigned2String::Type, _SPACE_, typename SingularOrPlural ::Type > {}; // 'N Units of beer': template struct NUnitsOfBeer : public Join< _SPACE_, typename CountNoun::Type, _of_, _beer_> {}; template struct NUnitsOfBeer<0,Unit> : public Join< _SPACE_, _no_, _more_, typename PluralOf::Type, _of_, _beer_> {}; // 'N Units of beer on the wall': template struct NUnitsOfBeerOnTheWall : public Join< _SPACE_, typename NUnitsOfBeer::Type, _on_, _the_, _wall_ > {}; // Last line of verse: template struct NMinus1UnitsOfBeerOnTheWall : public Concat< typename Join< _SPACE_, _take_, _one_, _down_, _and_, _pass_, _it_, _around_ >::Type, _COMMA_SPACE_, typename NUnitsOfBeerOnTheWall::Type > {}; template struct NMinus1UnitsOfBeerOnTheWall<0,totalCount,Unit> : public Concat< typename Join< _SPACE_, _go_, _to_, _the_, _store_, _and_, _buy_, _some_, _more_ >::Type, _COMMA_SPACE_, typename NUnitsOfBeerOnTheWall::Type > {}; // One Verse: template struct Verse { typedef typename Concat< typename NUnitsOfBeerOnTheWall::Type, _COMMA_SPACE_, typename NUnitsOfBeer::Type, _PERIOD_ >::Type Line1; typedef typename Concat< typename NMinus1UnitsOfBeerOnTheWall::Type, _PERIOD_ >::Type Line2; typedef typename Concat< typename Capitalise::Type, _LINEBREAK_, typename Capitalise::Type, _LINEBREAK_ >::Type Type; }; // Bottles: template struct MakeSong { typedef typename Concat< typename Verse::Type, _LINEBREAK_, typename MakeSong::Type >::Type Type; }; template struct MakeSong<0,totalCount,Unit> : public Verse<0, totalCount, Unit> {}; // Make the song: template struct Song: public MakeSong {}; ////////////////////////////////////////////////////////////////////// // Usage: typedef Song<99,_bottle_>::Type TheBeerSong; ////////////////////////////////////////////////////////////////////// // Usage 1: Let the compiler sing the song #if 1 static int i= TheBeerSong(); // cause an error => the compiler prints the type name... #endif ////////////////////////////////////////////////////////////////////// // Usage 2: Print the song #if 0 #include int main(void) { puts(CArray::value); return 0; } #endif