99 Bottles of Beer/C++/Object Oriented

From Rosetta Code
Revision as of 20:57, 6 August 2009 by rosettacode>Mwn3d (Moved from main task page to shorten it)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

Another solution, which in addition correctly handles the grammar. This solution is object-oriented. It is completely overkill for this problem.

Works with: GCC version 4.1.2 20061115 (prerelease) (SUSE Linux)

<lang cpp>

  1. include <iostream>
  2. include <string>
  3. include <sstream>

namespace bottle_song {

 // =================================================================
 // ***********************************
 // * Abstract base class for things. *
 // ***********************************
 class thing
 {
 public:
   // return the singular of the thing
   virtual std::string singular() const = 0;
   // return the plural of the thing
   virtual std::string plural() const = 0;
   // we need a virtual destructor, too
   virtual ~thing() {}
 };
 // =================================================================
 // ***************
 // * Containers. *
 // ***************
 // Containers are things which can contain other things. The
 // following class makes any thing into a container. The container
 // class is actually a decorator which makes any thing into a
 // container. Note that the contained thing is actually mutable,
 // even if the container is not. Note that the container can only
 // contain a single thing; if it shall contain several things, make
 // it contain a collection instead.
 class container: public thing
 {
 public:
   // The format gives the name. %self% is replaced by the containing
   // object's name (in proper pluralization), %contained% is
   // replaced by the contained object's name.
   container(std::string fmt, thing const& what, thing const& contained);
   std::string singular() const;
   std::string plural() const;
 private:
   std::string format;
   thing const& self;
   thing const& contained_thing;
   // helper function to replace strings
   static void replace(std::string& str, std::string from, std::string to);
 };
 container::container(std::string fmt,
                      thing const& what,
                      thing const& contained):
   format(fmt),
   self(what),
   contained_thing(contained)
 {
 }
 std::string container::singular() const
 {
   std::string result = format;
   replace(result, "%self%", self.singular());
   replace(result, "%contained%", contained_thing.singular());
   return result;
 }
 std::string container::plural() const
 {
   std::string result = format;
   replace(result, "%self%", self.plural());
   replace(result, "%contained%", contained_thing.singular());
   return result;
 }
 void container::replace(std::string& str, std::string from, std::string to)
 {
   std::string::size_type pos = str.find(from);
   if (pos != std::string::npos)
     str.replace(pos, from.length(), to);
 }
 // =================================================================
 // *********************************
 // * A collection of equal things. *
 // *********************************
 // In the context of this program, a collection of things is again
 // considered a single thing.
 // This is a concrete class.
 class equal_collection: public thing
 {
 public:
   // constructor
   equal_collection(int count, thing const& what);
   // get singular
   std::string singular() const;
   // get plural. This has to be implemented, even if it isn't used,
   // because the inherited version is pure virtual, and not
   // implementing this would make the class abstract.
   std::string plural() const;
   // this just returns whether thwere are still things left to take away.
   bool there_is_some_left();
   // this takes one thing away from the collection. Taking a thing
   // away from an empty collection is undefined behaviour (i.e. not
   // explicitly checked).
   void take_one_away();
 private:
   int count_of_things;
   thing const& type_of_thing;
 };
 // equal_collection constructor
 equal_collection::equal_collection(int count, thing const& what):
   count_of_things(count),
   type_of_thing(what)
 {
 }
 // get singular. The singular of the collection is just the number
 // followed by the thing, proper pluralized. The fact that it's
 // grammatically still a plural form doesn't matter for the problem
 // at hand.
 std::string equal_collection::singular() const
 {
   std::ostringstream oss;
   oss << count_of_things << " ";
   if (count_of_things == 1)
     oss << type_of_thing.singular();
   else
     oss << type_of_thing.plural();
   return oss.str();
 }
 // get plural. For collections, the plural is just "times " followed
 // by the singular. That is 3 collections of 4 bottles each give 3
 // times 4 bottles.
 std::string equal_collection::plural() const
 {
   return "times " + singular();
 }
 // tell if there are still things to take away. There are things to
 // take away if there are more than 0 things.
 bool equal_collection::there_is_some_left()
 {
   return count_of_things > 0;
 }
 // take one thing away from the collection. That is, just decrement
 // the count of things.
 void equal_collection::take_one_away()
 {
   --count_of_things;
 }
 // =================================================================
 // ************
 // * The beer *
 // ************
 class beer: public thing
 {
 public:
   std::string singular() const { return "beer"; }
   std::string plural() const { return "beers"; }
 };
 // =================================================================
 // **************
 // * The bottle *
 // **************
 class bottle: public thing
 {
 public:
   std::string singular() const { return "bottle"; }
   std::string plural() const { return "bottles"; }
 };
 // =================================================================
 // ************
 // * The wall *
 // ************
 class wall: public thing
 {
 public:
   std::string singular() const { return "wall"; }
   std::string plural() const { return "walls"; }
 };
 // =================================================================
 // this is the class for the song.
 class song
 {
 public:
   song(int bottle_count);
   void sing(std::ostream& where); // note: singing the song modifies it!
 private:
   beer beverage;
   bottle drink_source;
   container bottle_of_beer;
   equal_collection collection_of_bottles;
   wall bottle_storage;
   container wall_of_bottles;
 };
 song::song(int bottle_count):
   bottle_of_beer("%self% of %contained%", drink_source, beverage),
   collection_of_bottles(bottle_count, bottle_of_beer),
   wall_of_bottles("%contained% on the %self%",
                   bottle_storage, collection_of_bottles)
 {
 }
 void song::sing(std::ostream& where)
 {
   while (collection_of_bottles.there_is_some_left())
   {
     where << wall_of_bottles.singular() << ".\n"
           << collection_of_bottles.singular() << ".\n"
           << "Take one down, pass it around.\n";
     collection_of_bottles.take_one_away();
     where << wall_of_bottles.singular() << ".\n\n";
   }
 }

}

int main() {

 bottle_song::song song(100);
 song.sing(std::cout);
 return 0;

} </lang>