66 Skeleton
Example
Consider the following rpc interface Ix:
$interface rpc Ix
{
void f1(int32 x, uint16 y);
void f2(const X& x);
void f3(const xstring& s);
};
Xcpp generates the following code to register a skeleton
struct SkeletonIx : ceda::Skeleton
{
SkeletonIx() : ceda::Skeleton(GetSkeletonThunkFnTable(),3) {}
ceda::ptr<Ix> Invoke()
{
return ceda::ptr<Ix>(m_delegate.m_self, m_delegate.m_table);
}
void Thunk0()
{
ceda::int32 x;
ceda::uint16 y;
Pop() >> x >> y;
Invoke()->f1(x,y);
}
void Thunk1()
{
X x;
Pop() >> x;
Invoke()->f2(x);
}
void Thunk2()
{
ceda::string8 s;
Pop() >> s;
Invoke()->f3(s);
}
static void sThunk0(void* _self) { ((SkeletonIx*)_self)->Thunk0(); }
static void sThunk1(void* _self) { ((SkeletonIx*)_self)->Thunk1(); }
static void sThunk2(void* _self) { ((SkeletonIx*)_self)->Thunk2(); }
static const ceda::SkeletonThunkFn* GetSkeletonThunkFnTable()
{
static const ceda::SkeletonThunkFn t[] =
{
&sThunk0,
&sThunk1,
&sThunk2,
};
return t;
}
};
struct SkeletonIxFactory : public ceda::ISkeletonFactory
{
SkeletonIxFactory()
{
ceda::RegisterSkeleton(ceda::GetIpcInterfaceName<Ix>(),this);
}
~SkeletonIxFactory()
{
ceda::UnregisterSkeleton(ceda::GetIpcInterfaceName<Ix>());
}
virtual ceda::Skeleton* CreateSkeleton()
{
return new SkeletonIx;
}
};
void _Register_SkeletonIx()
{
static SkeletonIxFactory s;
}
Xcpp generates code such as the following for an RPC interface.
struct SkeletonIServer : public ceda::Skeleton
{
SkeletonIServer() : ceda::Skeleton(GetSkeletonThunkFnTable(), 3) {}
IServer& Invoke()
{
return reinterpret_cast(m_delegate);
}
void Thunk0()
{
int32 x;
Pop() >> x;
Invoke().PrintInt(x);
}
void Thunk1()
{
int32 a;
int32 b;
Pop() >> a;
Invoke().Square(a,b);
Push() << b;
}
void Thunk2()
{
int32 a;
int32 b;
float32 c;
Pop() >> a >> b;
Invoke().Sum(a,b,c);
Push() << c;
}
static void sThunk0(void* self) { ((SkeletonIServer*)self)->Thunk0(); }
static void sThunk1(void* self) { ((SkeletonIServer*)self)->Thunk1(); }
static void sThunk2(void* self) { ((SkeletonIServer*)self)->Thunk2(); }
static const SkeletonThunkFn* GetSkeletonThunkFnTable()
{
static const SkeletonThunkFn t[] =
{
&sThunk0,
&sThunk1,
&sThunk2,
};
return t;
}
};
Skeleton.h
// Skeleton.h
//
// Author David Barrett-Lennard
// (C)opyright Cedanet Pty Ltd 2020
@import "cxRpc.h"
@import "Ceda/cxObject/Object.h"
#include "Ceda/cxUtils/Archive.h"
namespace ceda
{
class Skeleton;
///////////////////////////////////////////////////////////////////////////////////////////////////
// ISkeletonFactory
struct ISkeletonFactory
{
virtual Skeleton* CreateSkeleton() = 0;
};
///////////////////////////////////////////////////////////////////////////////////////////////////
// Skeleton factory registry
// The SkeletonFactoryRegistry is a transient registry indexed by fully qualified name of the
// interface.
// This allows the system to automatically generate a suitable skeleton object for a given interface
// The following three functions can be called by different threads - they are fully threadsafe.
// Returns false if skeleton factory has already been registered
@api bool RegisterSkeleton(ConstStringZ qualifiedInterfaceName, ISkeletonFactory* factory);
@api void UnregisterSkeleton(ConstStringZ qualifiedInterfaceName);
// Create a skeleton for the given interface for which a skeleton factory has previously been
// registered.
// This must be deleted using C++ delete keyword
// Returns nullptr if no skeleton factory for the given interface has been registered.
@api Skeleton* CreateSkeleton(ConstStringZ qualifiedInterfaceName);
///////////////////////////////////////////////////////////////////////////////////////////////////
// Skeleton
typedef void (*SkeletonThunkFn)(void* self);
/*
template<typename T>
struct SkeletonThunkFnTable3
{
static void sThunk0(void* self) { ((T*)self)->Thunk0(); }
static void sThunk1(void* self) { ((T*)self)->Thunk1(); }
static void sThunk2(void* self) { ((T*)self)->Thunk2(); }
static const SkeletonThunkFn* Table()
{
static const SkeletonThunkFn t[] =
{
&sThunk0,
&sThunk1,
&sThunk2,
};
return t;
}
};
*/
class Skeleton
{
cxNotCloneable(Skeleton)
public:
explicit Skeleton(const SkeletonThunkFn* skeletonThunkFnTable, ssize_t numMethods) :
m_ar(nullptr),
m_skeletonThunkFnTable(skeletonThunkFnTable),
numMethods(numMethods)
{
}
// Returns read-archive for reading the in-parameters
inline InputArchive& Pop() { return m_ar; }
// Test whether the given index is valid
inline bool ValidIndex(ssize_t index) const { return 0 <= index && index < numMethods; }
// The ultimate receiver of the messages. It is assumed this can be reinterpret cast to the
// interface associated with this Skeleton.
AnyInterface m_delegate;
InputArchive m_ar;
const SkeletonThunkFn* m_skeletonThunkFnTable;
ssize_t numMethods;
};
} // namespace ceda
Skeleton.cpp
// Skeleton.cpp
//
// Author David Barrett-Lennard
// (C)opyright Cedanet Pty Ltd 2020
@import "Skeleton.h"
#include "Ceda/cxUtils/xstring.h"
#include "Ceda/cxUtils/Tracer.h"
#include <map>
#include <mutex>
@def bool tf_SkeletonFactory = false
namespace ceda
{
/////////////////////////////////////////////////////////////////////////////////////////////////
// SkeletonFactoryRegistry
class SkeletonFactoryRegistry
{
public:
static SkeletonFactoryRegistry& GetInstance()
{
static SkeletonFactoryRegistry s;
return s;
}
SkeletonFactoryRegistry() { m_magic = MAGIC; }
~SkeletonFactoryRegistry() { m_magic = 0; }
bool Register(ConstStringZ qualifiedInterfaceName, ISkeletonFactory* factory);
void Unregister(ConstStringZ qualifiedInterfaceName);
Skeleton* CreateSkeleton(ConstStringZ qualifiedInterfaceName);
private:
enum
{
MAGIC = 0xceda2009
};
int32 m_magic;
// Map from ReflectedInterface to factory
typedef std::map<xstring, ISkeletonFactory*> MAP;
MAP m_map;
// Mutex used to control access to the map
std::mutex mapMutex_;
};
bool SkeletonFactoryRegistry::Register(ConstStringZ qualifiedInterfaceName, ISkeletonFactory* factory)
{
@if (tf_SkeletonFactory)
{
Tracer() << "SCION: Registering skeleton for interface " << qualifiedInterfaceName << '\n';
}
std::lock_guard<std::mutex> lock(mapMutex_);
MAP::iterator i = m_map.find(qualifiedInterfaceName);
if (i == m_map.end())
{
m_map[qualifiedInterfaceName] = factory;
return true;
}
else
{
return false;
}
}
void SkeletonFactoryRegistry::Unregister(ConstStringZ qualifiedInterfaceName)
{
// When the application is closed ungracefully, Unregister can be called after
// the SkeletonFactoryRegistry has already destructed, so we check for this using a magic
// value.
if (m_magic == MAGIC)
{
std::lock_guard<std::mutex> lock(mapMutex_);
MAP::iterator i = m_map.find(qualifiedInterfaceName);
cxAssert(i != m_map.end());
m_map.erase(i);
}
}
Skeleton* SkeletonFactoryRegistry::CreateSkeleton(ConstStringZ qualifiedInterfaceName)
{
@if (tf_SkeletonFactory)
{
Tracer() << "SCION: Creating skeleton for interface " << qualifiedInterfaceName << '\n';
}
std::lock_guard<std::mutex> lock(mapMutex_);
MAP::iterator i = m_map.find(qualifiedInterfaceName);
if (i == m_map.end())
{
// Not found
@if (tf_SkeletonFactory)
{
Tracer() << "SCION: *** ERROR : Skeleton interface " << qualifiedInterfaceName << " not registered\n";
}
return nullptr;
}
else
{
return i->second->CreateSkeleton();
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////
@api bool RegisterSkeleton(ConstStringZ qualifiedInterfaceName, ISkeletonFactory* factory)
{
return SkeletonFactoryRegistry::GetInstance().Register(qualifiedInterfaceName,factory);
}
@api void UnregisterSkeleton(ConstStringZ qualifiedInterfaceName)
{
SkeletonFactoryRegistry::GetInstance().Unregister(qualifiedInterfaceName);
}
@api Skeleton* CreateSkeleton(ConstStringZ qualifiedInterfaceName)
{
return SkeletonFactoryRegistry::GetInstance().CreateSkeleton(qualifiedInterfaceName);
}
} // namespace ceda