Home - Blog - Contact

IdOp Reference

Copyright © 2009, Matthew Orlando

This guide presents everything you need to know to get started using IdOp. To see these examples in action, see IdOpExamples.cpp.

General Concepts

In order to use IdOp, you will need the Boost Preprocessor library installed in your include path. Download Boost here.

In your source file you simply need to include IdOp.h to access all the features. All the other necessary headers will be included automatically (IdOpExamples.h is unnecessary for your own projects).

IdOp Components

IdOp uses an identifier flanked by two built-in binary operatiors to accomplish the infix notation associated with operators. The the operators and identifier together are called an idop (I like to pronounce it EYE-dee-ahp or EYE-dahp).

leftOperand leftOperator identifier rightOperator rightOperand
            \___________________________________/
                            idop

When leftOperator has equal or greater or equal precedence than rightOperator, the idop is said to be left-handed, and leftOperand is called the primary operand. Conversely, if rightOperator has greater precedence than the leftOperator, the idop is said to be right-handed, and rightOperand is the primary operand.

Note that the preprocessor has no way of determining these precedences automatically. You must specify the handedness of the operators by using the approprate macro (see IdOp Macros). The primary operand determines the default return type for the operation. The secondary operand must be—or must be convertible to—the type of the primary operand.

Here is the canonical example:

foo ^_^ bar
Left
operand
Left
operator
Identifier Right
operator
Right
operand
foo ^ _ ^ bar
Primary idop Secondary

Operations

The actual operations are carried out by a functor template, whose name you provide to the IdOp macros when you create the idop. The templates should have a single type parameter, and the operator()’s parameters should be references to this type (you can also use pass-by-value, but then you may miss out on some compiler optimizations). Since the functors will be instantiated as constants, you must also declare the operator() to be const.

Here are the templates we will be using for the examples below:

template<typename T> class Quotient {
public:
T operator()(const T& left, const T& right) const
{ return left / right; }
}; template<typename T> class Contains { public: bool operator()(const T& left, const T& right) const { return std::string(left).find(right) != std::string::npos; } }; template<typename T> class AddLeft { public: T& operator()(T& left, const T& right) const { left += right; return left; } }; template<typename T> class Sum { public: T operator()(const T& left, const T& right) const { return left + right; } }; template<typename T> class Difference { public: T operator()(const T& left, const T& right) const { T val = left - right; return val >= 0 ? val : -val; } }; template<typename T> class ThrowNotEqual { public: void operator()(const T& left, const T& right) const { if (left != right) throw std::runtime_error("left != right"); } }; template<typename T> class AssertNotEqual { public: void operator()(const T& left, const T& right) const { assert(left != right); } }; template<typename T> class Product { public: T operator()(const T& left, const T& right) const { return left * right; } };

IdOp Macros

IdOp uses several macros to generate the various templates necessary to implement an idop. While this makes it very simple to create idops, if things go wrong the compiler won't be much help. If you make an error in your use of a macro, you’ll likely get messages mentioning the various sub-macros involved (all the macros begin with either BOOST_PP or IDOP). Even if you get the macros right, errors in your usage of the operator or in the construction of your operation templates can make for similarly cryptic error messages. Unfortunately there is no way around these problems. Just keep this in mind when you’re debugging programs built with IdOp.

IDOP_CREATE_x_HANDED

Creates a simple x-handed idop. No other idops can use the same identifier.

Signatures

IDOP_CREATE_LEFT_HANDED( leftOperator, identifier, rightOperator, operationType )
IDOP_CREATE_RIGHT_HANDED( leftOperator, identifier, rightOperator, operationType )

Example

IDOP_CREATE_RIGHT_HANDED(^, __, -, Quotient)

int main() 
{
   std::cout << "50 / 7.0 == " << (50 ^__- 7.0) << std::endl;
}

IDOP_CREATE_x_HANDED_RET

Creates a simple x-handed idop with the specified return type. No other idops can use the same identifier.

Signatures

IDOP_CREATE_LEFT_HANDED_RET( leftOperator, identifier, rightOperator, operationType, returnType )
IDOP_CREATE_RIGHT_HANDED_RET( leftOperator, identifier, rightOperator, operationType, returnType )

Example

IDOP_CREATE_LEFT_HANDED_RET(<, _contains_, >, Contains, bool)
#define contains <_contains_>

int main()
{
   if ("Hello, World!" contains "Hello")
      std::cout << "Everything's fine." << std::endl;
   else
      std::cout << "Something went horribly wrong." << std::endl;
}

IDOP_OPERAND_TYPE

IDOP_OPERAND_TYPE resolves to the name of the template parameter used by IdOp so you can, e.g. return by reference.

Example

IDOP_CREATE_LEFT_HANDED_RET(<, plusequals, >, AddLeft, IDOP_OPERAND_TYPE &)

int main()
{
   double baz = 38;
   const double xyzzy = 2;
   baz <plusequals> xyzzy <plusequals> xyzzy;
   std::cout << "The answer to the ultimate question: " << baz << std::endl;
}

IDOP_OPERATION(_RET)

Specifies a secondary operator and operation to perform. These macros are used as arguments to the IDOP_x_HANDED macros shown below.

Signatures

IDOP_OPERATION( secondaryOperator, operationType )
IDOP_OPERATION_RET( secondaryOperator, operationType, returnType)

Example

See IDOP_CREATE.

IDOP_x_HANDED

Specifies a primary operator and a set of operations. These macros are used as arguments to IDOP_CREATE shown below.

Signatures

IDOP_LEFT_HANDED( leftOperator, operation [operation] ... )
IDOP_RIGHT_HANDED( rightOperator, operation [operation] ... )

The operations can be either of the IDOP_OPERATION macros shown above.

Example

See IDOP_CREATE.

IDOP_CREATE

Creates a set of idops for a given identifier using an arbitrary number of left and right operators. No other idops can use the same identifier.

Signature

IDOP_CREATE( identifier, primaryMacro [primaryMacro] ... )

The primaryMacros can be either of the IDOP_x_HANDED macros shown above.

Example

IDOP_CREATE( _ ,
   IDOP_LEFT_HANDED( - ,
      // x -_^ y <=> x + y
      IDOP_OPERATION(^, Sum)
   )
   IDOP_LEFT_HANDED( > ,
      // x >_> y <=> abs(x - y)
      IDOP_OPERATION(>, Difference) 
      // x >_< y <=> if (x != y) throw
      IDOP_OPERATION_RET(<, ThrowNotEqual, void)
   )
   IDOP_RIGHT_HANDED( - ,
      //  x ^_- y <=> assert(x != y)
      IDOP_OPERATION_RET(^, AssertNotEqual, void)
      //  x |_- y <=> x * y
      IDOP_OPERATION(|, Product)
   )
)

int main()
{
   using namespace std;
   const int foo = 42, bar = 69;

   cout << "foo -_^ bar == " << (foo -_^ bar) << endl;
   cout << "foo >_> bar == " << (foo >_> bar) << endl;

   try {
      foo >_< bar;
      cout << "foo >_< bar succeeded unexpectedly";
   } catch (runtime_error ex) {
      cout << "foo >_< bar failed as expected. Message: "
           << ex.what() << endl;
   }

   cout << "Doing foo ^_- bar assertion...\n";
   foo ^_- bar;

   cout << "foo |_- bar == " << (foo |_- bar) << endl;
}

Copyright ©2008-2009 Matthew Orlando
This site is not affiliated with Blizzard Entertainment or John Wiley and Sons.