Classes.cpp
// Classes.cpp
//
// Author David Barrett-Lennard
// (C)opyright Cedanet Pty Ltd 2007
@import "Ceda/cxObject/Object.h"
@import "Ceda/cxObject/WCSpace.h"
@import "Ceda/cxObject/IObjectVisitor.h"
@import "Ceda/cxObject/gcroot.h"
@import "ExampleUtils.h"
#include "Ceda/cxUtils/HPTime.h"
#include <assert.h>
#include <vector>
///////////////////////////////////////////////////////////////////////////////////////////////////
// Classes1
/*
Forward declarations
--------------------
xcpp supports forwards declarations of classes and structs. For example
$struct X;
$class Y;
xcpp checks for consistency in use of the 'struct' or 'class' keyword.
These declarations cause a corresponding C++ forward declaration to be generated. In addition the
type is registered with the symbol tables of the xcpp parser making pointers and references to the
forward declared type available for use in interfaces etc.
*/
namespace Classes1
{
// Forward declaration of a struct called X
$struct X;
// It is permmisible to typedef a forward declared class or struct!
$typedef X M;
// Forward declaration of X makes it possible to pass X by reference or pointer in interface
// methods. However X may not be passed by value.
$interface Ix
{
void f1(X& x1, X* x2, M* x3);
};
// Later we can define X.
$struct X
{
$$() : x(10) {}
$ceda::int32 x;
};
// Although not particularly useful it is permissible for a forward declaration to appear after
// the definition.
$struct X;
void Run()
{
ceda::TraceGroup g("Classes example 1");
// The typedef can be used even though X wasn't defined at the point of the typedef
M m;
Tracer() << "m.x = " << m.x << '\n';
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// Classes2
/*
Deferred definitions
--------------------
In order to support "programming by interface", xcpp supports a syntax that is rather like
a forward declaration, but actually causes xcpp to emit code to allow for the ReflectedClass to
be accessed and for instances to be created.
An example is
$struct X isa Ix;
This declaration can appear in a header file, allowing clients to create instances of X without
seeing the definition of X.
*/
namespace Classes2
{
$interface+ Ix : ceda::IObject
{
void Foo();
};
// Deferred definition
$struct+ X isa Ix;
void Run()
{
ceda::TraceGroup g("Classes example 2");
ceda::CSpaceCreator cspace;
ceda::CSpaceLock lock; // Exclusive lock
// Clients can create and use instances of X without seeing or accessing the definition of X.
// This is "programming by interface"
ceda::ptr<Ix> p = ceda::qicast<Ix>(ceda::CreateInstance<X>());
p->Foo();
}
// There is no linkage dependency to the definition of X. Therefore the following code can be
// in a different DLL.
$struct+ X isa Ix
{
void Foo() { Tracer() << "calling Foo()\n"; }
};
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// Classes3
/*
$new
----
$new has the same syntax as new, and is used to register an object at the time of its allocation
in the currently locked CSpace
*/
@def DirectlyImplementsInterface(C,I) =
{
ceda::directly_implements_interface<C,I>::value
}
@def IndirectlyImplementsInterface(C,I) =
{
ceda::indirectly_implements_interface<C,I>::value
}
@def ImplementsInterface(C,I) =
{
ceda::implements_interface<C,I>::value
}
namespace Classes3
{
$interface+ Ix : ceda::IObject
{
void Foo();
};
$struct X isa Ix
{
void Foo() { Tracer() << "calling Foo()\n"; }
};
void Run()
{
static_assert( DirectlyImplementsInterface(X, Ix) );
static_assert( !DirectlyImplementsInterface(X, ceda::IObject) );
static_assert( !IndirectlyImplementsInterface(X, Ix) );
static_assert( IndirectlyImplementsInterface(X, ceda::IObject) );
static_assert( ImplementsInterface(X, Ix) );
static_assert( ImplementsInterface(X, ceda::IObject) );
ceda::TraceGroup g("Classes example 3");
ceda::CSpaceCreator cspace;
ceda::CSpaceLock lock; // Exclusive lock
ceda::ptr<Ix> p = $new X; // registered with the CSpace
p->Foo();
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// Classes4
/*
Illustrates ability for the front end to parse the base class list - including use of macros
for base classes.
*/
#define macroX(x) x
namespace Classes4
{
$class Y : public Classes3::X, private macroX(Classes2::X)
{
};
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// Classes5
/*
Illustrates ability for $new to support nested types
*/
namespace Classes5
{
$struct X_int32 isa ceda::IObject
{
X_int32(int x=0) {}
};
template <typename T>
struct X {};
template <>
struct X<ceda::int32>
{
typedef X_int32 Type;
};
void foo()
{
X<ceda::int32>::Type* p1 = $new X<ceda::int32>::Type;
X<ceda::int32>::Type* p2 = $new X<ceda::int32>::Type(10);
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////
// Classes6
namespace Classes6
{
$interface+ Ix : ceda::IObject
{
void foo();
};
$class X isa Ix
{
public:
X() : x(1) {}
~X() { x = 2; }
void foo() { cxAssert(x == 1); Tracer() << "calling foo()\n"; }
ceda::int32 x;
};
void Run()
{
ceda::TraceGroup g("Classes example 6");
ceda::CSpaceCreator cspace(10); // 10 msec per GC
@if (true)
{
ceda::gcroot<X*> vx;
X* x;
{
ceda::CSpaceLock lock;
x = $new X;
vx.reset(x);
}
}
@else
{
// x is deleted in the next GC, causing an assertion in the call to foo()
X* x;
{
ceda::CSpaceLock lock;
x = $new X;
}
}
Sleep(40); // Sleep long enough for a GC
{
ceda::CSpaceLock lock;
x->foo();
(*x).foo();
X* p = x;
p->foo();
}
}
}
namespace Classes7
{
$struct X;
$model Y
{
const X* x1;
X* x2;
};
// Non-IObject $struct
$struct X :
model
{
}
{
$$() : x(10) {}
$ceda::int32 x;
};
}
///////////////////////////////////////////////////////////////////////////////////////////////////
namespace Classes8
{
$struct X isa ceda::IObject
{
$$( ceda::int32 x ) : x_( x ) {}
ceda::int32 x_;
void OnGarbageCollect() const { Tracer() << "X::OnGarbageCollect - " << this << '\n'; }
};
void Run()
{
ceda::TraceGroup g("Classes example 8");
ceda::CSpaceCreator cspace(10);
{
std::vector< ceda::gcroot<X*> > vec;
{
ceda::CSpaceLock _;
vec.push_back( ceda::gcroot<X*>($new X( 1 ) ) );
vec.push_back( ceda::gcroot<X*>($new X( 2 ) ) );
vec.push_back( ceda::gcroot<X*>($new X( 3 ) ) );
}
Sleep( 40 );
//for (auto& v : vec)
//{
// Tracer() << "v = " << v->x_ << '\n';
//}
}
Tracer() << "vec now out of scope\n";
Sleep( 40 );
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////
namespace Classes
{
void Run()
{
Classes1::Run();
Classes2::Run();
Classes3::Run();
Classes6::Run();
//todo: trips assertion because we no longer support nested CSpace locks
//Classes8::Run();
}
}