[Snark] [CPP/C++] Beer Song

sac_boy submissions at badcode.rocks
Wed Dec 5 00:55:42 UTC 2018


Please find attached my submission for your October challenge, suitable for 
compilation with g++ or whatever C++ compiler you have laying around.  (It 
didn't have to be C++ of course as there are no C++ specifics in there bar 
the use of the standard library for output.)

    g++ -o beer beer.cpp
    ./beer

I realise of course that this is October 64th or so and technically October
proper has thus come and gone, but I came up with an idea for your
challenge while reminding myself how C++ macros work (or, should not work)
and submit this for what the kids call 'shits' and 'giggles'.

This beer counting song implementation makes horrific use of a recursive
inclusion of beer.h to define functions from beer0() to beer99(), which
call each other in a chain from 99 to 0. The functions themselves are
thereby *not recursive at all* (build it with g++ -E to see the
intermediate preprocessor output to see what I mean). Additionally, the
code that generates the lines has been DRYed to a point of complete
dessication.

99 levels of recursive inclusion of beer.h just happens to be the maximum
my compiler will allow, which was lucky.

== Editor Notes ==

This is actually a submission to the November challenge, Beer Song, and 
while it is technically past the date for that, it was decided to extend the 
deadline slightly.  So, this is an eligible entry.

== License ==

Entirely the work of /u/sac_boy, who is me, to my eternal shame
Licensed under CC0

== beer.cpp ==

#include "beer.h"

/* 
 * Beer Counting Song Generator V1.0.0
 * Entirely the work of /u/sac_boy, who is me, to my eternal shame
 * Licensed under CC0
 */

int main()
{
  HIGHEST_BEER_FUNC()();
}

== beer.h ==

#include <iostream>
#include <string>

/* 
 * Beer Counting Song Generator V1.0.0
 * Entirely the work of /u/sac_boy, who is me, to my eternal shame
 * Licensed under CC0 but anyone who uses this technique for real
 * aught to have a long, hard look at themselves
 */

#ifndef CONCAT
  // Concatenate two tokens.
  #define CONCAT(x, y) x ## y
  // Concatenate two tokens, but resolve macro params first.
  // Stands for Concat, But Resolve Macros First Please
  #define CBRMFP(x, y) CONCAT(x, y)
#endif

// Work out the name of the beer function we want to create
// in this particular inclusion of beer.h
#undef FUNCNAME
#define FUNCNAME() CBRMFP(beer, __INCLUDE_LEVEL__)

// We also want to know the 'next' function to call, which is
// __INCLUDE_LEVEL__ - 1...so use __COUNTER__ instead.
// Let's hope nobody else uses __COUNTER__! 
#undef NEXTFUNCNAME
#define NEXTFUNCNAME CBRMFP(beer, __COUNTER__)

// If this is the first inclusion of beer.h we need to create beer0(),
// our final function in the chain of calls from beer99() down
#if __INCLUDE_LEVEL__ == 1
  // Coincidentally, 99 is the maximum that both the song requires and my
  // compiler will allow
  #define BEER_MAX 99

  // We reuse these patterns quite a lot so let's DRY them up
  #define STRINGIFY(x...) #x
  #define SLASHN std::endl
  #define SPACE ' '
  #define PERIOD STRINGIFY(.)
  #define BOB STRINGIFY(bottles of beer)
  #define SPACE_BOB SPACE << BOB
  #define SPACE_BOB_SPACE SPACE_BOB << SPACE
  #define SPACE_BOB_PERIOD SPACE_BOB << PERIOD
  #define OTW STRINGIFY(on the wall)
  #define OTW_COMMA_SPACE OTW << STRINGIFY(,) << SPACE
  #define OTW_PERIOD OTW << PERIOD
  #define O_MORE STRINGIFY(o more)

  // We'll define HIGHEST_BEER_FUNC() so main knows what to call.
  // This means we are future-proofed and ready for the demands of the 
  // 21st century
  #define HIGHEST_BEER_FUNC() CBRMFP(beer, BEER_MAX)

  void beer0() {
    std::cout << "N" << O_MORE << SPACE_BOB_SPACE << OTW_COMMA_SPACE
              << "n" << O_MORE << SPACE_BOB_PERIOD << SLASHN
              << "Go to the store and buy some more," << SPACE
              << BEER_MAX
              << SPACE_BOB_SPACE << OTW_PERIOD << SLASHN;
  }
#endif

void FUNCNAME()() {
  std::cout << __INCLUDE_LEVEL__
     #undef BOB
     #undef BEER_PRONOUN
     #undef BEER_REMAINING
     #if __INCLUDE_LEVEL__ == 1
       #define BOB STRINGIFY(bottle of beer)
       #define BEER_PRONOUN STRINGIFY(it)
       #define BEER_REMAINING STRINGIFY(n) << O_MORE
     #else
       #define BOB STRINGIFY(bottles of beer)
       #define BEER_PRONOUN STRINGIFY(one)
       #define BEER_REMAINING __INCLUDE_LEVEL__ - 1
     #endif
     << SPACE_BOB_SPACE << OTW_COMMA_SPACE
     << __INCLUDE_LEVEL__
     << SPACE_BOB_PERIOD << SLASHN
     << "Take" << SPACE << BEER_PRONOUN << SPACE << "down and pass it around,"
     << SPACE << BEER_REMAINING
     #undef BOB
     #if __INCLUDE_LEVEL__ == 2
       #define BOB STRINGIFY(bottle of beer)
     #else
       #define BOB STRINGIFY(bottles of beer)
     #endif
     << SPACE_BOB_SPACE << OTW_PERIOD << SLASHN << SLASHN;

  // Call the next beer function in the chain. For example, if this is
  // function beer78(), here we would call beer77() and so on. Us computer
  // scientists might call this beerN and beerN-1.
  NEXTFUNCNAME();
}

// Include the file again to define the next beer function
#if __INCLUDE_LEVEL__ != BEER_MAX
  #include __FILE__
#endif


More information about the Snark mailing list