Functors.cpp

// Functors.cpp
//
// Author David Barrett-Lennard
// (C)opyright Cedanet Pty Ltd 2007

@import "Ceda/cxObject/Object.h"
@import "ExampleUtils.h"

///////////////////////////////////////////////////////////////////////////////////////////////////
/*
Functors
--------

Before describing what a functor is, it is a good idea to clarify the meaning of "variable", 
"value", "type" and "function", as follows

    variable :   can be assigned with a value.

    value    :   exists independently of particular variables.

    type     :   associated with a variable or value
                 type = set of values + set of operators on those values

    function :   can be invoked

A functor is a type, analogous to types like int and float.  A functor variable can be assigned
with a functor value.

A functor value can be regarded as a pointer to some function that can be invoked with the () 
operator.

The implementation of a $functor is similar to a $interface and consists of two pointers.  The
only difference is that rather than provide a pointer to a function pointer table, we have a 
pointer directly to a function.  This avoids one indirection making invocation slightly faster.

A $functor is better than a $interface when

    *   It makes sense to only allow the client to call a single function

    *   We don't want the expense of the extra indirection

    *   We like the way we can initialise the functor by name with one of the member functions of
        the class.
*/

namespace Functors1
{
	// The following declares a type called MyFunctor.
	// A MyFunctor value is some unspecified function that takes two int32 arguments and returns a 
	// float64.
	$functor ceda::float64 MyFunctor(ceda::int32 n1, ceda::int32 n2);
	
    // foo() receives a Myfunctor passed by value in formal argument x.  foo() can easily invoke x
    // even though it doesn't actually know what code will run.
    // On a 32 bit plaform all functors are only 64 bits, so they are very efficient to pass around
    // like this.
	void foo(MyFunctor x)
	{
	    Tracer() << "x(1,2) = " << x(1,2) << '\n';
	    Tracer() << "x(10,20) = " << x(10,20) << '\n';
	}

	void Run()
	{
		ceda::TraceGroup g("Functors example 1");

		// The $makefunctor directive is used to declare a functor variable that is initialised
		// with a functor value (i.e. to define the code to be run).  The block of code, enclosed in 
		// braces, can refer to the formal arguments declared in the functor declaration (in this case,
		// n1 and n2 both of which are of type int32) and must return a value implicitly coercible to 
		// the return type declared in the functor (unless the return type is void). For example:
		$makefunctor MyFunctor sum
		{
			return n1 + n2;
		}
		foo(sum);

		$makefunctor MyFunctor prod
		{
			return n1 * n2;
		}
		foo(prod);

	}	
}

///////////////////////////////////////////////////////////////////////////////////////////////////
/*
null values for functors
*/
namespace Functors2
{
	$functor ceda::int32 MyFunctor(ceda::int32 i);

	void Run()
	{
		ceda::TraceGroup g("Functors example 2");

	    // It is possible to declare a variable of type MyFunctor without explicitly initialising 
	    // its value.  In this case the initial value of the variable is null
	    MyFunctor x;
	    
        // x hasn't been initialised to a non-null functor value, so invoking x would cause a 
        // general protection fault
        // x(2);
	    
	    // x can be tested as a boolean.  In this case x is null so evaluates to false
	    cxAssert(!x);
	    if (x) Tracer() << "'x' evaluated to true\n";
	    if (!x) Tracer() << "'x' evaluated to false\n";
	    
		$makefunctor MyFunctor y { return i+1; }
		
	    // y isn't null so evaluates as a boolean to true
	    cxAssert(y);
	    if (y) Tracer() << "'y' evaluated to true\n";
	    if (!y) Tracer() << "'y' evaluated to false\n";
	    
	    // Functor variables can be assigned
	    x = y;

        // Now x evaluates as a boolean to true
	    cxAssert(x);
	    
        // x can be invoked
		Tracer() << "x(10) = " << x(10) << '\n';
		
		// A functor variable can be assigned to null
		x = ceda::null;
		
		// Now x evaluates to false
		cxAssert(!x);
	}	
}

///////////////////////////////////////////////////////////////////////////////////////////////////
/*
$makefunctor - execution in the context of a class

The previous examples of $makefunctor can be regarded as global functions.  This example shows how
a functor can bind to what is essentially a member function of a class.
*/
namespace Functors3
{
    $functor ceda::int32 MyFunctor(ceda::int32 x);

    class W
    {
    public:
        W() : m(10) {}
        
        int sqr() const { return m*m; }
        
    protected:
        int m;
    };

    void Run()
    {
        ceda::TraceGroup g("Functors example 3");

        W w;

        // The $makefunctor directive can be used to bind a functor to a block of code that
        // runs in the context of some class/struct
        // In the following example the functor y binds to a block of code that can access the 
        // public or protected members of instance w of W.
        // Note that formal argument names will hide class members of the same name.
        $makefunctor MyFunctor y(&w,W)
        {
            // Can access formal argument x of MyFunctor, and members m, sqr() of instance w of W
            return x + m + sqr();      
        }

        Tracer() << "y(1) = " << y(1) << '\n';


        const W* p = &w;
        $makeconstfunctor MyFunctor y2(p,W)
        {
            // Can access formal argument x of MyFunctor, and members m, sqr() of instance w of W
            return x + m + sqr();      
        }

        Tracer() << "y2(1) = " << y2(1) << '\n';
    }
}

///////////////////////////////////////////////////////////////////////////////////////////////////
/*
This example shows how a CD Player can internally wire its play and stop buttons to its 
Play() and Stop() methods.

Each button stores a functor to be invoked when it is pressed.  A functor allows for a 
call back (i.e. synchronous notification) to some client.  There are some nice features of this 
technique

    1.  it is not type intrusive on the client
        (note how the CDPlayer doesn't implement any interface such as IButtonListener
        in order to receive notifications from its buttons)
        
    2.  A functor is small, lightweight and very fast.
        On 32 bit platforms a functor is only 64 bits
        
    3.  The wiring is achieved without any need for additional heap allocations.  This contrasts
        with Java anonymous inner classes or C# delegates.
        
    4.  The syntax is simple, typesafe and elegant.
*/
namespace Functors4
{
	///////////////////////////////////////////////////////////////////////////////////////////////
	// Button.h

    // A functor is useful for declaring the signature of a call back function. A client provides 
    // an implementation in order to receive a notification that a button has been pressed.
	$functor void OnButtonPressed();
	
	class Button
	{
	public:
		// Client calls SetHandler() in order to set the handler that receives the
		// notification that the button has been pressed
		void SetHandler( OnButtonPressed handler ) { m_handler = handler; }
		
        // It is assumed OnMouseClicked() is called when the user clicks the mouse button while the
        // mouse cursor is over the button
		void OnMouseClicked() 
		{ 
        	// Invoke the functor.  I.e. send the notification that the button has been pressed
		    m_handler(); 
		}
	private:
		// Functor is a member of the button
		OnButtonPressed m_handler;
	};

	///////////////////////////////////////////////////////////////////////////////////////////////
	// CDPlayer.cpp

	class CDPlayer
	{
	public:
		CDPlayer()
		{
            // Constructor of the CDPlayer wires up the button notifications to the Play() and
            // Stop() methods.
            
			$makefunctor OnButtonPressed cb1(this,CDPlayer)
			{
				// This functor binds to the Play() member function, note that
				// it passes a parameter into the function.
				Play(5);
			}
			m_playButton.SetHandler(cb1);

			$makefunctor OnButtonPressed cb2(this,CDPlayer)
			{
				// This functor binds to the Stop() member function of CdPlayer
				Stop();
			}
			m_stopButton.SetHandler(cb2);
		}
	    
		void Play( int speed )
		{
			Tracer() << "CD Player playing with speed = " << speed << '\n';
		}

		void Stop()
		{
			Tracer() << "CD Player stopped\n";
		}
	    
		void Test()
		{
			m_playButton.OnMouseClicked();
			m_stopButton.OnMouseClicked();
		}

	private:    
		Button m_playButton;
		Button m_stopButton;
	};

	void Run()
	{
		ceda::TraceGroup g("Functors example 4");
		
		CDPlayer cd;
		cd.Test();
	}	
}

///////////////////////////////////////////////////////////////////////////////////////////////////
/*
This example looks at the optimisation level achieved by the VC8 compiler.  It seems that the
function foo() was optimised out of existance.

Run() executes the following code to issue to the call foo(sum) :-
    mov         eax,dword ptr [Functors5::sum (40C27Ch)]    // equivalent to mov eax,0
    push        2    
    push        1    
    push        eax  
    call        dword ptr [Functors5::sum+4 (40C280h)]

The call to Functors5::sum+4 runs the following instructions

    mov         eax,dword ptr [esp+0Ch] 
    mov         ecx,dword ptr [esp+8] 
    add         eax,ecx 
    ret         0Ch  

Note that the dynamic nature of the call was replaced with a static call directly to the stub.
However it didn't go as far as inlining the stub itself.  Why not?

It is curious that the optmiser didn't directly push a zero for the 'self' argument.
*/
namespace Functors5
{
	$functor+ ceda::int32 MyFunctor(ceda::int32 n1, ceda::int32 n2);
	
    // NOTE functor arguments can appear in reflected functions
	$function+ ceda::int32 foo(MyFunctor x)
	{
	    return x(1,2);
	}

	$makefunctor MyFunctor sum { return n1 + n2; }

    void Run()
    {
        int x = foo(sum);
        
        @if (@str(@platform) == "Win32")
        {
            // According to VC8 documentation, __asm blocks can never be optimised.  Therefore this
            // forces the calculation of local variable x
            __asm
            {
                mov eax,x
            }
        }
    }
}

///////////////////////////////////////////////////////////////////////////////////////////////////
namespace Functors
{
    void Run()
    {
        Functors1::Run();
        Functors2::Run();
        Functors3::Run();
        Functors4::Run();
        Functors5::Run();
    }
}