The Tic Tac Toe Example
The following single file of Macaroni code creates
multiple headers and implementation files, which
together form a library (or DLL) along with an
executable program (i.e. an exe file) which plays
Tic Tac Toe.
Project System
Because Macaroni aims to simplify creating,
maintaining, and building large C++ projects, it
needs to operates with some knowledge of what an
entire project looks
like rather than just a single file at a time.
Thus it's entry point is always a Lua script
named "project.lua" which lives in the root of
a project directory and defines its project:
import("Macaroni", "ProjectTemplates", "1")
require "SimpleProject"
SimpleProject{
group="Macaroni.Examples",
project="TicTacToe",
version="DEV",
libShortName = "TicTacToeLib",
src="src",
target="target",
dependencies = {
load("Macaroni", "Boost-headers", "1.55"):Target("lib"),
load("Macaroni", "CppStd", "2003"):Target("lib"),
},
};
This file does a few things.
First off, it imports
a project in the group "Macaroni" named
"ProjectTemplates" with version "1". Macaroni has
it's own project system complete with repo inspired
by Maven and
Fantom which makes
it possible to import a project by name without
having to muck with it's file path, assuming it's
been correctly installed and configured. More on
that in a bit.
After the ProjectTemplates project is loaded,
Macaroni is able to run Lua scripts that were
defined there, including "SimpleProject" which
appropriately enough defines the function
"SimpleProject" which can then be used. Like it's
name implies, this allows us to create a simple
project which consists of a single library along
with some arbitrary number of executables (which
can be unit tests depending on the build tool).
Note that the Macaroni project system can be
used for more customized and complicated layouts
than this. The SimpleProject function is actually
just a bit of Lua code to try to emulate a
"build by convention" system for ease of use on
smaller projects. If you're curious how it
works you
can view it's code here.
By default, the library name is simply "lib", but
this doesn't matter as libraries are grouped by
their projects which avoids namespace collissions,
and the names of the produced lib, so, or dll files
reflect this. However, as that can cause file path
issues at build time there's an ability to specify
a special short name with "libShotName". In
Windows this will translate to a lib called
"TicTacToeLib.dll".
Again, since one of Macaroni's jobs is keeping
you from mucking with files, it simply accepts
entire directories for where to find source code,
as well as for where to dump generated source (
"target").
Finally, the library loads as it's dependencies
two other libraries- a simple project filled with
some standard C++ classes, and another one for
the Boost C++
Library headers. Both of these libraries are
defined by Macaroni and are included with it.
In the case of Boost, the user is required to add
the path to the Boost header files to a special
file Macaroni installed to the root of the Macaroni
repo directory. While Boost is not required to
use Macaroni, it does make using it (and C++)
even nicer.
See "Getting Started" for information on how to
set this up.
Macaroni Source Code
Next, let's look at the file "tictactoe.mcpp" which
will live in the "src" directory. Because it's in
Macaroni, you can call it anything you want.
~import tictactoe::AlreadySet;
~import BOOST_ASSERT;
~import tictactoe::Board;
~import tictactoe::BadLocation;
~import std::exception;
~import boost::optional;
~import std::ostream;
~namespace tictactoe;
class Board {
~block "h" {
private: boost::optional<bool> spots[9];
public: static const bool X = true;
public: static const bool O = false;
}
public Board() {
for (int i = 0; i < 9; ++ i) { spots[i] = boost::none; }
}
public ~global ~friend ostream & operator<<(
ostream & out, const Board & board) {
for (unsigned int r = 0; r < 3; ++r) {
out << "[ ";
for (unsigned int c = 0; c < 3; ++ c) {
out << (board.get(r, c) ? (
board.get(r, c).get() ? "X" : "O")
: (" ")) << " ";
}
out << "]\n";
}
return out;
}
public bool has_free_spots() const {
for (int i = 0; i < 9; ++ i) {
if (!spots[i]) { return true; } }
}
public optional<bool> get(const unsigned int r,
const unsigned int c) const {
BOOST_ASSERT(r < 3 && c < 3);
return spots[r * 3 + c];
}
~hidden bool matches(const unsigned int r,
const unsigned int c, const bool value) const {
BOOST_ASSERT(r < 3 && c < 3);
return spots[r * 3 + c]
&& spots[r * 3 + c].get() == value;
}
public void set(const unsigned int r, const unsigned int c,
const bool value) {
if (r >= 3 || c >= 3) { throw BadLocation(); }
else if (spots[r * 3 + c]) { throw AlreadySet(); }
else { spots[r * 3 + c] = value; }
}
~hidden bool winner_is(bool v) const {
return ( (matches(0, 0, v) && matches(0, 1, v)
&& matches(0, 2, v))
|| (matches(1, 0, v) && matches(1, 1, v)
&& matches(1, 2, v))
|| (matches(2, 0, v) && matches(2, 1, v)
&& matches(2, 2, v))
|| (matches(0, 0, v) && matches(1, 0, v)
&& matches(2, 0, v))
|| (matches(0, 1, v) && matches(1, 1, v)
&& matches(2, 1, v))
|| (matches(0, 2, v) && matches(1, 2, v)
&& matches(2, 2, v))
|| (matches(0, 0, v) && matches(1, 1, v)
&& matches(2, 2, v))
|| (matches(0, 2, v) && matches(1, 1, v)
&& matches(2, 0, v)) );
}
public optional<bool> winner() const {
if (winner_is(X)) { return X; }
else if (winner_is(O)) { return O; }
else { return boost::none; }
}
};
class AlreadySet : public virtual exception {
public virtual const char * what() const BOOST_NOEXCEPT
{
return "Location already taken.";
}
};
class BadLocation : public virtual exception {
public virtual const char * what() const BOOST_NOEXCEPT
{
return "Bad location for X or O.";
}
};
~unit "ttt" type=exe
{
~import std::cin;
~import std::cout;
~block "cpp"
{
int main(int argc, char ** argv)
{
Board board;
int turnCount = 0;
while (true) {
cout << "~ ROUND " << ++ turnCount << " ~\n";
for (unsigned int i = 1; i <= 2; ++ i) {
cout << "Player " << i << "'s turn:\n";
cout << board;
try {
cout << "Which row?\n";
unsigned int r;
cin >> r;
cout << "Which column?\n";
unsigned int c;
cin >> c;
board.set(r, c,
i == 1 ? Board::X : Board::O);
} catch(const AlreadySet) {
cout << "Spot is already taken.\n";
i -= 1;
} catch(const BadLocation) {
cout << "That spot is out of bounds.\n";
i -= 1;
}
if (board.winner()) {
cout << board;
cout << "Player " <<
(tictactoe::Board::X == board.winner()
? "1" : "2") << " wins the game!\n";
return 0;
}
}
cout << "\n";
}
}
}
}
The ~import statement and NodeSpace
First off, notice the "~import" statement.
~import tictactoe::AlreadySet;
Macaroni is not a real C++ compiler by any means
and thus does not need the full definition of a
class before it's code can make use of that class.
However, it does at the very least need to know the
names of classes or other items before they are
used in the definition of classes or functions.
That way it can associate it correctly to it's full
path (so it associated "Board" with "tictactoe::Board"
everywhere it sees it).
Note that in Macaroni, unlike C++, there is only
a single namespace for everything. In C++, there
are actually different namespaces for structs /
classes and functions. So some legal (and perhaps
questionable) C++ code is
illegal in Macaroni.
Officially, Macaroni calls it's namespace
"NodeSpace" and refers to any items it imports as
"nodes". So when it sees
"~import tictactoe::AlreadySet", it's all like
"cool, whatev's" and moves along with its life. If
"AlreadySet" appears in a function argument list,
it remember this, but doesn't do anything else
until later on, after everything has been parsed
when it begins generating code. At that point
it has already seen the definition of "AlreadySet"
(which appears later in the file) and knows it's
a class and knows what it's forward declaration
looks like as well as where it's header file is.
It then relies on the C++ compiler to make sure
everything works.
The ~namespace directive
It's possible to define namespaces in Macaroni
just like C++, using braces. Unlike C++ however
Macaroni can define multiple namespaces using double
colons. Additionally, if a namespace directive
starts with a tilde (implying Macaroni specific
functionality) and ends with a semicolon, it
tells Macaroni that all classes, functions etc
defined from that point on will be in the given
namespace.
How Macaroni generates code
class Board {
~block "h" {
private: boost::optional<bool> spots[9];
public: static const bool X = true;
public: static const bool O = false;
}
Macaroni does not generate expressions or statements
in C++ itself. Instead, it simply scoops up whole
blocks of text and dumps them directly into
the generated source.
For example, when the class "Board" is defined,
the "~block" directive tells Macaroni to just shove
the code in the braces into the header file. When
it does, it inserts a "#line" directive containing
the Macaroni source file and line number where
it got the block of code. This means if the code
doesn't compile, the error message will reference
the exact line / column of the Macaroni source file
where the error occurred rather than the generated
file. The vast majority of the time, this works
flawlessly, and makes it possible to even step
around Macaroni code using the debugger.
The "~block" directive is also a get out of jail
free card for Macaroni, in that it allows you to
escape it's confines and write any C++ code you
need in the event Macaroni can't handle some
construct.
Next up, there's Board's constructor:
class Board {
...
public Board() {
for (int i = 0; i < 9; ++ i) { spots[i] = boost::none; }
}
You might have noticed that Macaroni puts the
access keyword public right before each member,
unlike C++ which allows you to specify the access
keyword followed by a colon before several members.
This is optional and the C++ technique will work
as well. I find that grouping member definitions
by access makes less sense when the functions are
also being defined and prefer the Java / C# style.
However, in the spirit of C++ feel free to go with
the style you believe is appropriate.
As is clear by now Macaroni is also capable
of evaluating a lot of C++ constructs such as
function definitions and generating the code for
you. For example, here's a method definition:
public optional<bool> get(const unsigned int r,
const unsigned int c) const {
BOOST_ASSERT(r < 3 && c < 3);
return spots[r * 3 + c];
}
Macaroni will correctly write out the function
prototype into the class's interface in the header
file, taking care to fully qualify the return
type "boost::optional" to avoid using
directives in the header file and prevent namespace
pollution. However, it will add the using
statements to the ".cpp" file which enables the
Macaroni source code to not care at all about fully
spelling out "boost" a dozen times, as Macaroni
handles the declarations in the header and in the
code block itself can assume that the
namespace is in use.
Global Friends
Macaroni allows you to define free standing
functions as part of a class using the ~global
keyword, which gaurantees
that they will end up living with that classes
header and implementation file. The "~friend"
keyword also specifies that the function should
be a friend of the class. For example:
public ~global ~friend ostream & operator<<(
ostream & out, const Board & board) {
for (unsigned int r = 0; r < 3; ++r) {
out << "[ ";
for (unsigned int c = 0; c < 3; ++ c) {
out << (board.get(r, c) ? (
board.get(r, c).get() ? "X" : "O")
: (" ")) << " ";
}
out << "]\n";
}
return out;
}
Here a free standing function is defined
which allows instances of the Board class to
be passed through to an output stream.
~hidden access
Because Macaroni is already generating code, i
t can also do some things that
would normally be considered too burdensome or
error-prone in typical C++.
An example of this is the new "~hidden" access
keyword:
This isn't really adding anything new to C++,
but rather tells Macaroni to add this method
to only the implementation files copy of the
header.
You see, Macaroni generates two headers, or
interfaces, for each class. One lives in its
own .h file. The other lives right where the
.cpp file would normally #include it's
corresponding .h file.
Have you ever wanted to add a single helper method
or function to a class, but didn't want to risk
having to recompile the fifty other classes which
depended on it? This is possible in Macaroni thanks
to ~hidden members which allow you to add methods
to a class that don't become part of its interface.
If you're freaking out just reading this, then take
a deep breath and calm down. This technique works
on every compiler yet used with Macaroni and fixes
a subtle flaw in C++, which is that private methods
always form part of a class's public interface even
if they aren't used by inlined or template
functions.
By the way, because there is still a need for
traditional private methods, Macaroni supports
those too. :)
The ~unit directive
Finally, at the end of the code there's a directive
called "~unit":
~unit "ttt" type=exe
{
~import std::cin;
~import std::cout;
~block "cpp"
{
int main(int argc, char ** argv)
{
...
Remember how Macaroni has it's
own Project system? Basically this directive tells
Macaroni to create a "unit" which takes it's name
from translation unit and consists of two files,
a header and an implementation file. Normally,
the later would become a simple object file during
build time, but argument "type=exe" tells Macaroni
to go ahead and build a runnable binary from it
whose name will be "ttt.exe" or Windows and just
"ttt" on Linux.
It is possible to define classes within units in
order to group them into the same resulting
header and implementation files. It's also possible
to stick a chunk of code into either the .h or .cpp
file as shown above, which is how the main class
comes to exist.
Using "~unit" it is possible to define multiple
executables in a single Macaroni file. However in
larger projects it's probably best to make a single
Macaroni file for each unit, and in some cases
resort to typical C++ code (Macaroni will
automatically pick up any files ending in .c or
.cpp it finds in the source directories you give
it and turn them into units).
The Generated Source
Below is a listing of all of the files generated
by Macaroni which end up in the the "target"
directory specified by project.lua
when "macaroni generate" is specified at the command
line.
#include <tictactoe/AlreadySet.h>
#include <boost/assert.hpp>
#include <tictactoe/Board.h>
#include <tictactoe/BadLocation.h>
#include <exception>
#include <boost/optional.hpp>
#include <iostream>
#include <iostream>
#include <iostream>
using tictactoe::AlreadySet;
using tictactoe::Board;
using tictactoe::BadLocation;
using std::exception;
using boost::optional;
using std::ostream;
using std::cin;
using std::cout;
#line 107 "tictactoe.mcpp"
int main(int argc, char ** argv)
{
Board board;
int turnCount = 0;
while (true) {
cout << "~ ROUND " << ++ turnCount << " ~\n";
for (unsigned int i = 1; i <= 2; ++ i) {
cout << "Player " << i << "'s turn:\n";
cout << board;
try {
cout << "Which row?\n";
unsigned int r;
cin >> r;
cout << "Which column?\n";
unsigned int c;
cin >> c;
board.set(r, c,
i == 1 ? Board::X : Board::O);
} catch(const AlreadySet) {
cout << "Spot is already taken.\n";
i -= 1;
} catch(const BadLocation) {
cout << "That spot is out of bounds.\n";
i -= 1;
}
if (board.winner()) {
cout << board;
cout << "Player " <<
(tictactoe::Board::X == board.winner()
? "1" : "2") << " wins the game!\n";
return 0;
}
}
cout << "\n";
}
}
#line 58 "C:/Users/Tim/Work/Lp3/Projects/Macaroni/Code/macaroni-site/example/TicTacToe/target/ttt.cpp"
#ifndef MACARONI_COMPILE_GUARD_Config_Macaroni_46_Examples___TicTacToe___DEV___lib_H
#define MACARONI_COMPILE_GUARD_Config_Macaroni_46_Examples___TicTacToe___DEV___lib_H
#ifdef BOOST_BUILD_PCH_ENABLED
#include
#endif
#include <boost/config.hpp>
// Creates macros needed to create portable library code.
//
// Macros:
//
// MACARONI_LIB_DYN_LINK_Macaroni_46_Examples___TicTacToe___DEV___lib
// If defined, this code will be built or used as a library.
// If not defined, then the code will be compiled into the resulting binary.
//
// MACARONI_LIB_CREATE_Macaroni_46_Examples___TicTacToe___DEV___lib
// If defined, we are building this library. If not, we are using it.
//
// MACARONI_LIB_DECL_Macaroni_46_Examples___TicTacToe___DEV___lib
// This is defined by this header file by looking at the above values.
// This may or may not become a compiler annotation affixed to all symbol
// definitions. This is necessary for certain compilers (specifically MSVC++).
//
#if defined(BOOST_ALL_DYN_LINK) || defined(MACARONI_LIB_DYN_LINK_Macaroni_46_Examples___TicTacToe___DEV___lib)
# if defined(MACARONI_LIB_CREATE_Macaroni_46_Examples___TicTacToe___DEV___lib)
# define MACARONI_LIB_DECL_Macaroni_46_Examples___TicTacToe___DEV___lib BOOST_SYMBOL_EXPORT
# else
# define MACARONI_LIB_DECL_Macaroni_46_Examples___TicTacToe___DEV___lib BOOST_SYMBOL_IMPORT
# endif
#else
# define MACARONI_LIB_DECL_Macaroni_46_Examples___TicTacToe___DEV___lib
#endif
#endif // end of MACARONI_COMPILE_GUARD_Config_Macaroni_46_Examples___TicTacToe___DEV___lib_H
#ifndef MACARONI_COMPILE_GUARD_tictactoe_AlreadySet_H
#define MACARONI_COMPILE_GUARD_tictactoe_AlreadySet_H
// This class was originally defined in tictactoe.mcpp
#include <Config_TicTacToeLib.h>
// Forward declaration necessary for anything which also depend on this.
namespace tictactoe {
class AlreadySet;
} // End tictactoe
#include <exception>
namespace tictactoe {
// Define class AlreadySet
class MACARONI_LIB_DECL_Macaroni_46_Examples___TicTacToe___DEV___lib AlreadySet
: public virtual std::exception
{
public : virtual const char * what() const BOOST_NOEXCEPT;
}; // End of class AlreadySet
} // End tictactoe
#endif // end of MACARONI_COMPILE_GUARD_tictactoe_AlreadySet_H
#ifndef MACARONI_COMPILE_GUARD_tictactoe_AlreadySet_CPP
#define MACARONI_COMPILE_GUARD_tictactoe_AlreadySet_CPP
// The following configures symbols for export if needed.
#define MACARONI_LIB_CREATE_Macaroni_46_Examples___TicTacToe___DEV___lib
/*--------------------------------------------------------------------------*
* Internal header. *
*--------------------------------------------------------------------------*/
#ifndef MACARONI_COMPILE_GUARD_tictactoe_AlreadySet_H
#define MACARONI_COMPILE_GUARD_tictactoe_AlreadySet_H
// This class was originally defined in tictactoe.mcpp
#include <Config_TicTacToeLib.h>
// Forward declaration necessary for anything which also depend on this.
namespace tictactoe {
class AlreadySet;
} // End tictactoe
#include <exception>
namespace tictactoe {
// Define class AlreadySet
class MACARONI_LIB_DECL_Macaroni_46_Examples___TicTacToe___DEV___lib AlreadySet
: public virtual std::exception
{
public : virtual const char * what() const BOOST_NOEXCEPT;
}; // End of class AlreadySet
} // End tictactoe
#endif // end of MACARONI_COMPILE_GUARD_tictactoe_AlreadySet_H
/*--------------------------------------------------------------------------*
* Definition. *
*--------------------------------------------------------------------------*/
#include <tictactoe/AlreadySet.h>
#include <boost/assert.hpp>
#include <tictactoe/Board.h>
#include <tictactoe/BadLocation.h>
#include <exception>
#include <boost/optional.hpp>
#include <iostream>
#include <iostream>
#include <iostream>
using tictactoe::AlreadySet;
using tictactoe::Board;
using tictactoe::BadLocation;
using std::exception;
using boost::optional;
using std::ostream;
using std::cin;
using std::cout;
namespace tictactoe {
MACARONI_LIB_DECL_Macaroni_46_Examples___TicTacToe___DEV___lib
const char * AlreadySet::what() const BOOST_NOEXCEPT
{
#line 89 "tictactoe.mcpp"
return "Location already taken.";
#line 74 "C:/Users/Tim/Work/Lp3/Projects/Macaroni/Code/macaroni-site/example/TicTacToe/target/tictactoe/AlreadySet.cpp"
}
} // End tictactoe
#endif // end of MACARONI_COMPILE_GUARD_tictactoe_AlreadySet_CPP
#ifndef MACARONI_COMPILE_GUARD_tictactoe_BadLocation_H
#define MACARONI_COMPILE_GUARD_tictactoe_BadLocation_H
// This class was originally defined in tictactoe.mcpp
#include <Config_TicTacToeLib.h>
// Forward declaration necessary for anything which also depend on this.
namespace tictactoe {
class BadLocation;
} // End tictactoe
#include <exception>
namespace tictactoe {
// Define class BadLocation
class MACARONI_LIB_DECL_Macaroni_46_Examples___TicTacToe___DEV___lib BadLocation
: public virtual std::exception
{
public : virtual const char * what() const BOOST_NOEXCEPT;
}; // End of class BadLocation
} // End tictactoe
#endif // end of MACARONI_COMPILE_GUARD_tictactoe_BadLocation_H
#ifndef MACARONI_COMPILE_GUARD_tictactoe_BadLocation_CPP
#define MACARONI_COMPILE_GUARD_tictactoe_BadLocation_CPP
// The following configures symbols for export if needed.
#define MACARONI_LIB_CREATE_Macaroni_46_Examples___TicTacToe___DEV___lib
/*--------------------------------------------------------------------------*
* Internal header. *
*--------------------------------------------------------------------------*/
#ifndef MACARONI_COMPILE_GUARD_tictactoe_BadLocation_H
#define MACARONI_COMPILE_GUARD_tictactoe_BadLocation_H
// This class was originally defined in tictactoe.mcpp
#include <Config_TicTacToeLib.h>
// Forward declaration necessary for anything which also depend on this.
namespace tictactoe {
class BadLocation;
} // End tictactoe
#include <exception>
namespace tictactoe {
// Define class BadLocation
class MACARONI_LIB_DECL_Macaroni_46_Examples___TicTacToe___DEV___lib BadLocation
: public virtual std::exception
{
public : virtual const char * what() const BOOST_NOEXCEPT;
}; // End of class BadLocation
} // End tictactoe
#endif // end of MACARONI_COMPILE_GUARD_tictactoe_BadLocation_H
/*--------------------------------------------------------------------------*
* Definition. *
*--------------------------------------------------------------------------*/
#include <tictactoe/AlreadySet.h>
#include <boost/assert.hpp>
#include <tictactoe/Board.h>
#include <tictactoe/BadLocation.h>
#include <exception>
#include <boost/optional.hpp>
#include <iostream>
#include <iostream>
#include <iostream>
using tictactoe::AlreadySet;
using tictactoe::Board;
using tictactoe::BadLocation;
using std::exception;
using boost::optional;
using std::ostream;
using std::cin;
using std::cout;
namespace tictactoe {
MACARONI_LIB_DECL_Macaroni_46_Examples___TicTacToe___DEV___lib
const char * BadLocation::what() const BOOST_NOEXCEPT
{
#line 96 "tictactoe.mcpp"
return "Bad location for X or O.";
#line 74 "C:/Users/Tim/Work/Lp3/Projects/Macaroni/Code/macaroni-site/example/TicTacToe/target/tictactoe/BadLocation.cpp"
}
} // End tictactoe
#endif // end of MACARONI_COMPILE_GUARD_tictactoe_BadLocation_CPP
#ifndef MACARONI_COMPILE_GUARD_tictactoe_Board_H
#define MACARONI_COMPILE_GUARD_tictactoe_Board_H
// This class was originally defined in tictactoe.mcpp
#include <Config_TicTacToeLib.h>
// Forward declaration necessary for anything which also depend on this.
namespace tictactoe {
class Board;
} // End tictactoe
#include <boost/optional.hpp>
#include <iostream>
namespace tictactoe {
MACARONI_LIB_DECL_Macaroni_46_Examples___TicTacToe___DEV___lib std::ostream & operator<<(std::ostream & out, const tictactoe::Board & board);
} // End tictactoe
namespace tictactoe {
// Define class Board
class MACARONI_LIB_DECL_Macaroni_46_Examples___TicTacToe___DEV___lib Board
{
friend MACARONI_LIB_DECL_Macaroni_46_Examples___TicTacToe___DEV___lib std::ostream & ::tictactoe::operator<<(std::ostream & out, const tictactoe::Board & board);
#line 12 "tictactoe.mcpp"
private: boost::optional<bool> spots[9];
public: static const bool X = true;
public: static const bool O = false;
#line 35 "C:/Users/Tim/Work/Lp3/Projects/Macaroni/Code/macaroni-site/example/TicTacToe/target/tictactoe/Board.h"
public : Board();
public : bool has_free_spots() const;
public : boost::optional<bool > get(const unsigned int r, const unsigned int c) const;
public : void set(const unsigned int r, const unsigned int c, const bool value);
public : boost::optional<bool > winner() const;
}; // End of class Board
} // End tictactoe
#endif // end of MACARONI_COMPILE_GUARD_tictactoe_Board_H
#ifndef MACARONI_COMPILE_GUARD_tictactoe_Board_CPP
#define MACARONI_COMPILE_GUARD_tictactoe_Board_CPP
// The following configures symbols for export if needed.
#define MACARONI_LIB_CREATE_Macaroni_46_Examples___TicTacToe___DEV___lib
/*--------------------------------------------------------------------------*
* Internal header. *
*--------------------------------------------------------------------------*/
#ifndef MACARONI_COMPILE_GUARD_tictactoe_Board_H
#define MACARONI_COMPILE_GUARD_tictactoe_Board_H
// This class was originally defined in tictactoe.mcpp
#include <Config_TicTacToeLib.h>
// Forward declaration necessary for anything which also depend on this.
namespace tictactoe {
class Board;
} // End tictactoe
#include <boost/optional.hpp>
#include <iostream>
namespace tictactoe {
MACARONI_LIB_DECL_Macaroni_46_Examples___TicTacToe___DEV___lib std::ostream & operator<<(std::ostream & out, const tictactoe::Board & board);
} // End tictactoe
namespace tictactoe {
// Define class Board
class MACARONI_LIB_DECL_Macaroni_46_Examples___TicTacToe___DEV___lib Board
{
friend MACARONI_LIB_DECL_Macaroni_46_Examples___TicTacToe___DEV___lib std::ostream & ::tictactoe::operator<<(std::ostream & out, const tictactoe::Board & board);
#line 12 "tictactoe.mcpp"
private: boost::optional<bool> spots[9];
public: static const bool X = true;
public: static const bool O = false;
#line 44 "C:/Users/Tim/Work/Lp3/Projects/Macaroni/Code/macaroni-site/example/TicTacToe/target/tictactoe/Board.cpp"
public : Board();
public : bool has_free_spots() const;
public : boost::optional<bool > get(const unsigned int r, const unsigned int c) const;
private : bool matches(const unsigned int r, const unsigned int c, const bool value) const;
public : void set(const unsigned int r, const unsigned int c, const bool value);
private : bool winner_is(bool v) const;
public : boost::optional<bool > winner() const;
}; // End of class Board
} // End tictactoe
#endif // end of MACARONI_COMPILE_GUARD_tictactoe_Board_H
/*--------------------------------------------------------------------------*
* Definition. *
*--------------------------------------------------------------------------*/
#include <tictactoe/AlreadySet.h>
#include <boost/assert.hpp>
#include <tictactoe/Board.h>
#include <tictactoe/BadLocation.h>
#include <exception>
#include <boost/optional.hpp>
#include <iostream>
#include <iostream>
#include <iostream>
using tictactoe::AlreadySet;
using tictactoe::Board;
using tictactoe::BadLocation;
using std::exception;
using boost::optional;
using std::ostream;
using std::cin;
using std::cout;
/* "Public" adopted global functions and variables. */
namespace tictactoe {
MACARONI_LIB_DECL_Macaroni_46_Examples___TicTacToe___DEV___lib
std::ostream & operator<<(std::ostream & out, const tictactoe::Board & board)
{
#line 23 "tictactoe.mcpp"
for (unsigned int r = 0; r < 3; ++r) {
out << "[ ";
for (unsigned int c = 0; c < 3; ++ c) {
out << (board.get(r, c) ? (
board.get(r, c).get() ? "X" : "O")
: (" ")) << " ";
}
out << "]\n";
}
return out;
#line 99 "C:/Users/Tim/Work/Lp3/Projects/Macaroni/Code/macaroni-site/example/TicTacToe/target/tictactoe/Board.cpp"
}
} // End tictactoe
namespace tictactoe {
MACARONI_LIB_DECL_Macaroni_46_Examples___TicTacToe___DEV___lib
Board::Board()
{
#line 18 "tictactoe.mcpp"
for (int i = 0; i < 9; ++ i) { spots[i] = boost::none; }
#line 113 "C:/Users/Tim/Work/Lp3/Projects/Macaroni/Code/macaroni-site/example/TicTacToe/target/tictactoe/Board.cpp"
}
MACARONI_LIB_DECL_Macaroni_46_Examples___TicTacToe___DEV___lib
bool Board::has_free_spots() const
{
#line 36 "tictactoe.mcpp"
for (int i = 0; i < 9; ++ i) {
if (!spots[i]) { return true; } }
#line 123 "C:/Users/Tim/Work/Lp3/Projects/Macaroni/Code/macaroni-site/example/TicTacToe/target/tictactoe/Board.cpp"
}
MACARONI_LIB_DECL_Macaroni_46_Examples___TicTacToe___DEV___lib
boost::optional<bool > Board::get(const unsigned int r, const unsigned int c) const
{
#line 42 "tictactoe.mcpp"
BOOST_ASSERT(r < 3 && c < 3);
return spots[r * 3 + c];
#line 133 "C:/Users/Tim/Work/Lp3/Projects/Macaroni/Code/macaroni-site/example/TicTacToe/target/tictactoe/Board.cpp"
}
bool Board::matches(const unsigned int r, const unsigned int c, const bool value) const
{
#line 48 "tictactoe.mcpp"
BOOST_ASSERT(r < 3 && c < 3);
return spots[r * 3 + c]
&& spots[r * 3 + c].get() == value;
#line 143 "C:/Users/Tim/Work/Lp3/Projects/Macaroni/Code/macaroni-site/example/TicTacToe/target/tictactoe/Board.cpp"
}
MACARONI_LIB_DECL_Macaroni_46_Examples___TicTacToe___DEV___lib
void Board::set(const unsigned int r, const unsigned int c, const bool value)
{
#line 55 "tictactoe.mcpp"
if (r >= 3 || c >= 3) { throw BadLocation(); }
else if (spots[r * 3 + c]) { throw AlreadySet(); }
else { spots[r * 3 + c] = value; }
#line 154 "C:/Users/Tim/Work/Lp3/Projects/Macaroni/Code/macaroni-site/example/TicTacToe/target/tictactoe/Board.cpp"
}
bool Board::winner_is(bool v) const
{
#line 61 "tictactoe.mcpp"
return ( (matches(0, 0, v) && matches(0, 1, v)
&& matches(0, 2, v))
|| (matches(1, 0, v) && matches(1, 1, v)
&& matches(1, 2, v))
|| (matches(2, 0, v) && matches(2, 1, v)
&& matches(2, 2, v))
|| (matches(0, 0, v) && matches(1, 0, v)
&& matches(2, 0, v))
|| (matches(0, 1, v) && matches(1, 1, v)
&& matches(2, 1, v))
|| (matches(0, 2, v) && matches(1, 2, v)
&& matches(2, 2, v))
|| (matches(0, 0, v) && matches(1, 1, v)
&& matches(2, 2, v))
|| (matches(0, 2, v) && matches(1, 1, v)
&& matches(2, 0, v)) );
#line 177 "C:/Users/Tim/Work/Lp3/Projects/Macaroni/Code/macaroni-site/example/TicTacToe/target/tictactoe/Board.cpp"
}
MACARONI_LIB_DECL_Macaroni_46_Examples___TicTacToe___DEV___lib
boost::optional<bool > Board::winner() const
{
#line 80 "tictactoe.mcpp"
if (winner_is(X)) { return X; }
else if (winner_is(O)) { return O; }
else { return boost::none; }
#line 188 "C:/Users/Tim/Work/Lp3/Projects/Macaroni/Code/macaroni-site/example/TicTacToe/target/tictactoe/Board.cpp"
}
} // End tictactoe
#endif // end of MACARONI_COMPILE_GUARD_tictactoe_Board_CPP
One thing you may notice is the ugly compiler macro
"MACARONI_LIB_DECL_Macaroni_46_Examples___TicTacToe___DEV___lib".
What the $@&*!? is that?!
Basically, it's something necessary to produce
a DLL in Microsoft Windows. As crazy as it sounds,
this is something you will find in all major
multiplatform C and C++ libraries, from pure Ansi C
code like Lua to the cutting edge C++ code in
Boost. In fact, Macaroni piggybacks
off compiler flags the Boost config header libraries
define to make this a bit more consistent.
By the way, if the Boost headers are *not* included
as a dependency of a project, the file
"Config_TicTacToeLib.h" above will not be generated
and the ugly compiler macro will dissappear.
A related question might be why the macro is so
ugly. This is by decision as a long name is needed
to avoid namespace collisions. The reality is many
projects pick short cute names and then feel
entitled to pollute the global namespace by
adding a three letter prefix to everything, so
their macros end up looking simple, like "LUA_API".
Given how stable and wonderful Lua is, it's probably
okay to let it do that. However, since Macaroni
is a code generator and thus no human being is
going to need to deal with the nasty name (except
maybe a few times as part of a build script) it
makes sense to give it a long name to reduce the
potential of a namespace collision. This also allows
Macaroni's users to easily make DLLs without
much fuss.
Tying into a Build System
Because Macaroni knows about a program's physical
structure it has the ability to generate
build scripts as well (this is optional).
So, the SimpleProject function in project.lua
generates one more file produced by
Macaroni: jamroot.jam, which is used by Boost Build:
# Generated by Macaroni.
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# ~~ Library Info ~~
# Group : Macaroni.Examples
# Name : TicTacToe
# Version : DEV
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
import boost ;
import path ;
using testing ;
# Group="Macaroni", Project="Boost-headers", Version="1.55"
boost.use-project 1.55 ;
# Group="Macaroni", Project="CppStd", Version="2003"
#~<( I don't know how to 'use' this dependency. )
# Default Project Library : lib
project
Macaroni_46_Examples___TicTacToe___DEV
: requirements
<include>"C:/Users/Tim/Work/Lp3/Projects/Macaroni/Code/macaroni-site/example/src"
<include>"C:/Users/Tim/Work/Lp3/Projects/Macaroni/Code/macaroni-site/example/target"
<include>"C:/Users/Tim/Work/Lp3/Projects/Macaroni/Code/macaroni-site/example/src"
# Group="Macaroni", Project="Boost-headers", Version="1.55", Target="lib"
<library>/boost//headers
# Group="Macaroni", Project="CppStd", Version="2003", Target="lib"
<include>"C:/Users/Tim/Work/Lp3/Projects/Macaroni/Code/trunk/Main/Libraries/Macaroni/CppStd/2003/Source"
# Group="Macaroni.Examples", Project="TicTacToe", Version="DEV", Target="Calculator"
# ...
# Group="Macaroni.Examples", Project="TicTacToe", Version="DEV", Target="tictactoe"
# ...
# Group="Macaroni.Examples", Project="TicTacToe", Version="DEV", Target="tictactoe::Board"
# ...
# Group="Macaroni.Examples", Project="TicTacToe", Version="DEV", Target="tictactoe::AlreadySet"
# ...
# Group="Macaroni.Examples", Project="TicTacToe", Version="DEV", Target="tictactoe::BadLocation"
# ...
: usage-requirements
<include>"C:/Users/Tim/Work/Lp3/Projects/Macaroni/Code/macaroni-site/example/src"
<include>"C:/Users/Tim/Work/Lp3/Projects/Macaroni/Code/macaroni-site/example/target"
<include>"C:/Users/Tim/Work/Lp3/Projects/Macaroni/Code/macaroni-site/example/src"
# Group="Macaroni", Project="Boost-headers", Version="1.55", Target="lib"
<library>/boost//headers
# Group="Macaroni", Project="CppStd", Version="2003", Target="lib"
<include>"C:/Users/Tim/Work/Lp3/Projects/Macaroni/Code/trunk/Main/Libraries/Macaroni/CppStd/2003/Source"
# Group="Macaroni.Examples", Project="TicTacToe", Version="DEV", Target="Calculator"
# ...
# Group="Macaroni.Examples", Project="TicTacToe", Version="DEV", Target="tictactoe"
# ...
# Group="Macaroni.Examples", Project="TicTacToe", Version="DEV", Target="tictactoe::Board"
# ...
# Group="Macaroni.Examples", Project="TicTacToe", Version="DEV", Target="tictactoe::AlreadySet"
# ...
# Group="Macaroni.Examples", Project="TicTacToe", Version="DEV", Target="tictactoe::BadLocation"
# ...
# When linking dynamically, define the following flag:
<link>shared:<define>MACARONI_LIB_DYN_LINK_Macaroni_46_Examples___TicTacToe___DEV___lib=1
# Default LibraryProject Headers
;
alias library_dependencies
: ;
# Units
# -----
# ~unit~add
obj 719442705
: "C:/Users/Tim/Work/Lp3/Projects/Macaroni/Code/macaroni-site/example/target/add.cpp"
: # When linking dynamically, define the following flag:
<link>shared:<define>MACARONI_LIB_DYN_LINK_Macaroni_46_Examples___TicTacToe___DEV___add=1
# Default LibraryProject Headers
;
# ~unit~subtract
obj 3248299621
: "C:/Users/Tim/Work/Lp3/Projects/Macaroni/Code/macaroni-site/example/target/subtract.cpp"
: # When linking dynamically, define the following flag:
<link>shared:<define>MACARONI_LIB_DYN_LINK_Macaroni_46_Examples___TicTacToe___DEV___subtract=1
# Default LibraryProject Headers
;
# ~unit~ttt
obj 720172901
: "C:/Users/Tim/Work/Lp3/Projects/Macaroni/Code/macaroni-site/example/target/ttt.cpp"
: # When linking dynamically, define the following flag:
<link>shared:<define>MACARONI_LIB_DYN_LINK_Macaroni_46_Examples___TicTacToe___DEV___ttt=1
# Default LibraryProject Headers
;
# Calculator
obj 2011401496
: "C:/Users/Tim/Work/Lp3/Projects/Macaroni/Code/macaroni-site/example/target/Calculator.cpp"
: # When linking dynamically, define the following flag:
<link>shared:<define>MACARONI_LIB_DYN_LINK_Macaroni_46_Examples___TicTacToe___DEV___lib=1
# Default LibraryProject Headers
;
# tictactoe
obj 3607485395
: "C:/Users/Tim/Work/Lp3/Projects/Macaroni/Code/macaroni-site/example/target/tictactoe.cpp"
: # When linking dynamically, define the following flag:
<link>shared:<define>MACARONI_LIB_DYN_LINK_Macaroni_46_Examples___TicTacToe___DEV___lib=1
# Default LibraryProject Headers
;
# tictactoe::Board
obj 1553560322
: "C:/Users/Tim/Work/Lp3/Projects/Macaroni/Code/macaroni-site/example/target/tictactoe/Board.cpp"
: # When linking dynamically, define the following flag:
<link>shared:<define>MACARONI_LIB_DYN_LINK_Macaroni_46_Examples___TicTacToe___DEV___lib=1
# Default LibraryProject Headers
;
# tictactoe::AlreadySet
obj 984448269
: "C:/Users/Tim/Work/Lp3/Projects/Macaroni/Code/macaroni-site/example/target/tictactoe/AlreadySet.cpp"
: # When linking dynamically, define the following flag:
<link>shared:<define>MACARONI_LIB_DYN_LINK_Macaroni_46_Examples___TicTacToe___DEV___lib=1
# Default LibraryProject Headers
;
# tictactoe::BadLocation
obj 1975926474
: "C:/Users/Tim/Work/Lp3/Projects/Macaroni/Code/macaroni-site/example/target/tictactoe/BadLocation.cpp"
: # When linking dynamically, define the following flag:
<link>shared:<define>MACARONI_LIB_DYN_LINK_Macaroni_46_Examples___TicTacToe___DEV___lib=1
# Default LibraryProject Headers
;
# Libraries
# ---------
# lib
lib TicTacToeLib
: # Sources:
/boost//headers
# NO DEP FOR YOU! Group="Macaroni", Project="CppStd", Version="2003", Target="lib"
2011401496
3607485395
1553560322
984448269
1975926474
:
;
# Tests
# ---------
# Executables
# ---------
# add
exe add
: # Sources:
TicTacToeLib
719442705
:
;
# subtract
exe subtract
: # Sources:
TicTacToeLib
3248299621
:
;
# ttt
exe ttt
: # Sources:
TicTacToeLib
720172901
:
;
# Makes the EXEs easier to run
# ---------
install exe :
add
subtract
ttt
: <install-dependencies>on <install-type>EXE
<install-type>LIB ;
Note that while this file builds things defined in
Macaroni code, Macaroni will also add any stray .cpp
files it finds in the src directory to the main
library it creates (there are ways to allow Macaroni
to add pure .cpp files you write to a project as
executable programs instead, but that's outside the
scope of this example).
Macaroni will execute Boost Build for this project
if "macaroni build" is typed on the command line
from the same directory at project.lua. Before
it begins, the "build" will also execute "generate".
Installing the Project
It's unlikely we'll need to use the code from the
Tic-Tac-Toe example from another project, but
if we did, we could allow Macaroni to install it
by running "macaroni install" from the command
line.
The "install" phase of Macaroni, like the other
phases, are defined by the project.lua files.
However, the typical behavior (supported by the
SimpleProject function) is to copy the entirety
of the directory to the Macaroni repo
directory at "Macaroni.Examples/TicTacToe/DEV"
(group name / project name / version).
Another project could then use it by running
the function
"load("Macaroni.Examples", "TicTacToe", "DEV"):Target("lib")".