DynamicDispatch.cpp
// DynamicDispatch.cpp
//
// Author David Barrett-Lennard
// (C)opyright Cedanet Pty Ltd 2007
@import "Ceda/cxObject/Object.h"
@import "Ceda/cxObject/DynamicDispatch.h"
@import "Ceda/cxObject/ReflectionByteCode.h"
@import "ExampleUtils.h"
/*
Dynamic Displatch
-----------------
Dynamic dispatch refers to the use of reflection information to invoke a function.
The following types of function are supported:-
1. a global function
2. a non-virtual, non-static method on a class
3. a method on an interface
A client makes use of the reflection information available in a ReflectedGlobalFunction,
ReflectedClass or ReflectedInterface respectively.
The invocation is made without actually compiling a direct function call in the normal way. In
fact typically at compile time the client will be unaware of the very existance of the function.
Dynamic dispatch is useful for language bindings - say to .net or Python. For example, it may
allow a Python invocation to be made on a PyObject that wraps an interface pointer. The binding
to the method, passing of arguments and return value is all made at run time.
*/
///////////////////////////////////////////////////////////////////////////////////////////////////
// DynamicDispatch1
/*
This example shows how to use a DynamicDispatcher to dynamically invoke a global function
*/
namespace DynamicDispatch1
{
$function+ ceda::float64 sum(ceda::int32 i, ceda::float64 j)
{
Tracer() << "sum(" << i << ',' << j << ')' << '\n';
return i+j;
};
void Run()
{
ceda::TraceGroup g("DynamicDispatch example 1");
const ceda::ReflectedGlobalFunction& rgf = ceda::FindReflectedGlobalFunction("DynamicDispatch1::sum");
ceda::float64 r = 0;
#if CEDA_ENABLE_ORIGINAL_DYNAMIC_DISPATCH
mDynamicDispatch_PrepareToCallGlobalFunction(rgf)
mDynamicDispatch_SetReturnAddress(&r)
mDynamicDispatch_ReserveArgs
new (mDynamicDispatch_ArgPtr) ceda::int32(100);
new (mDynamicDispatch_ArgPtr) ceda::float64(3.14);
mDynamicDispatch_InvokeFunction
mDynamicDispatch_UnreserveArgs
#elif CEDA_ENABLE_DYNAMIC_DISPATCH_USING_LIBFFI
// todo
#endif
Tracer() << " ---> returned " << r << '\n';
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// DynamicDispatch2
// This example shows how to use a DynamicDispatcher to dynamically invoke a class method
namespace DynamicDispatch2
{
$struct+ X
{
X(int offset = 0) : m_offset(offset) {}
$ceda::float64 offset(ceda::int32 i)
{
Tracer() << "offset(" << i << ')' << '\n';
return i + m_offset;
}
ceda::int32 m_offset;
};
void Run()
{
ceda::TraceGroup g("DynamicDispatch example 2");
// Declare an X
X x(20);
const int OFFSET_METHOD = 0;
//const ceda::ReflectedClass& rc = ceda::FindReflectedClass("DynamicDispatch2::X");
const ceda::ReflectedClass& rc = ceda::GetReflectedClass<DynamicDispatch2::X>();
ceda::float64 r = 0;
#if CEDA_ENABLE_ORIGINAL_DYNAMIC_DISPATCH
mDynamicDispatch_PrepareToCallClassMethod(rc,OFFSET_METHOD,&x)
mDynamicDispatch_SetReturnAddress(&r)
mDynamicDispatch_ReserveArgs
new (mDynamicDispatch_ArgPtr) ceda::int32(100);
mDynamicDispatch_InvokeFunction
mDynamicDispatch_UnreserveArgs
#elif CEDA_ENABLE_DYNAMIC_DISPATCH_USING_LIBFFI
// todo
#endif
Tracer() << " ---> returned " << r << '\n';
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// DynamicDispatch3
/*
This example shows how to use a DynamicDispatcher to dynamically invoke a method on an interface
pointer.
*/
namespace DynamicDispatch3
{
$interface+ Ix
{
ceda::float64 inc(ceda::int32 i);
};
struct X
{
ceda::float64 inc(ceda::int32 i)
{
Tracer() << "inc(" << i << ')' << '\n';
return i+1;
}
};
void Run()
{
ceda::TraceGroup g("DynamicDispatch example 3");
// Declare an X and coerce to a ptr<Ix>
X x;
ceda::ptr<Ix> p = &x;
const int INC_METHOD = 0;
const ceda::ReflectedInterface& ri = ceda::FindReflectedInterface("DynamicDispatch3::Ix");
ceda::float64 r = 0;
#if CEDA_ENABLE_ORIGINAL_DYNAMIC_DISPATCH
mDynamicDispatch_PrepareToCallInterfaceMethod(ri,INC_METHOD,p)
mDynamicDispatch_SetReturnAddress(&r)
mDynamicDispatch_ReserveArgs
new (mDynamicDispatch_ArgPtr) ceda::int32(100);
mDynamicDispatch_InvokeFunction
mDynamicDispatch_UnreserveArgs
#elif CEDA_ENABLE_DYNAMIC_DISPATCH_USING_LIBFFI
// todo
#endif
Tracer() << " ---> returned " << r << '\n';
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// DynamicDispatch4
/*
Example where a function returns a class by value with a non-trivial destructor. The client can
destruct the object using ReflectedClass::ops.m_DestructArrayFn
*/
namespace DynamicDispatch4
{
/*
In order to support pass by value or return by value, the class must have a copy ctor defined
*/
$struct+ X
{
X(int val = 1) { v = new int(val); }
X(const X& rhs) { v = new int(*rhs.v); }
X& operator=(const X& rhs) { *v = *rhs.v; return *this; }
~X() { delete v; }
int* v;
};
// Return X by value. Note that VC8 will avoid a redundant copy constructor
$function+ X ReturnX()
{
return X(2);
};
// Pass X by value
$function+ void PassX(X x)
{
Tracer() << "PassX() called with value " << *x.v << '\n';
};
void Run()
{
ceda::TraceGroup g("DynamicDispatch example 4");
// Pass by value example
{
const ceda::ReflectedGlobalFunction& rgf = ceda::FindReflectedGlobalFunction("DynamicDispatch4::PassX");
{
X x(3);
#if CEDA_ENABLE_ORIGINAL_DYNAMIC_DISPATCH
mDynamicDispatch_PrepareToCallGlobalFunction(rgf)
mDynamicDispatch_ReserveArgs
new (mDynamicDispatch_ArgPtr) X(x); // Placement new copy constructor on argument
mDynamicDispatch_InvokeFunction
mDynamicDispatch_UnreserveArgs
#elif CEDA_ENABLE_DYNAMIC_DISPATCH_USING_LIBFFI
// todo
#endif
}
}
// Return by value example
{
const ceda::ReflectedGlobalFunction& rgf = ceda::FindReflectedGlobalFunction("DynamicDispatch4::ReturnX");
@if (false)
{
// This is an example of how not to do it. It causes a memory leak because it breaks the
// rule that the caller must not run the constructor on the return value
{
X r;
mDynamicDispatch_PrepareToCallGlobalFunction(rgf)
mDynamicDispatch_SetReturnAddress(&r)
mDynamicDispatch_ReserveArgs
mDynamicDispatch_InvokeFunction
mDynamicDispatch_UnreserveArgs
Tracer() << "ReturnX() returned " << *r.v << '\n';
}
}
{
// Prepare unitialised buffer to hold the return value
ceda::octet_t buffer[sizeof(X)];
X& r = * (X*) buffer;
#if CEDA_ENABLE_ORIGINAL_DYNAMIC_DISPATCH
mDynamicDispatch_PrepareToCallGlobalFunction(rgf)
mDynamicDispatch_SetReturnAddress(&r)
mDynamicDispatch_ReserveArgs
mDynamicDispatch_InvokeFunction
mDynamicDispatch_UnreserveArgs
#elif CEDA_ENABLE_DYNAMIC_DISPATCH_USING_LIBFFI
// todo
#endif
Tracer() << "ReturnX() returned " << *r.v << '\n';
// Run the ~X() destruct function. Equivalent to r.~X();
// Without this we get a memory leak
ceda::ReflectionByteCode(rgf.returnType.byteCode, rgf.stringTable).GetReflectedClass()->ops.DestructVariable(buffer);
}
}
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////
namespace DynamicDispatch
{
void Run()
{
DynamicDispatch1::Run();
DynamicDispatch2::Run();
DynamicDispatch3::Run();
// todo: this is seg faulting (in Win64-Debug-dll)
//DynamicDispatch4::Run();
}
}