TKernel forms the lowest layer of the OpenCASCADE Technology (OCCT) stack, providing portable abstractions for primitive data types, container templates, exception hierarchies, file systems, encoding, and OS-level services. Most of its directories wrap platform differences in ways familiar to anyone who has read a large cross-platform C++ codebase; however, a few subsystems introduce distinctive architectural patterns worth examining in detail.
Module Overview
| Directory | Purpose |
|---|---|
| FlexLexer | Interface layer for flex-generated tokenizers |
| FSD | Low-level file-system data serialization |
| Message | Diagnostic routing and alert dispatch |
| NCollection | Generic template containers: arrays, lists, graphs |
| OSD | Operating-system portability: paths, timers, threads |
| Plugin | Dynamic library discovery and factory binding |
| Quantity | Physical and geometric units: color, angle, distance |
| Resource | Application assets and localized string tables |
| Standard | Fundamental typedefs, allocators, and atomic reference counting |
| StdFail | Common exception types |
| Storage | Object persistence and schema migration |
| TCollection / TColStd | Concrete container specializations |
| TShort | Compact integer sequences |
| Units / UnitsAPI / UnitsMethods | Dimensional conversion and unit arithmetic |
Rather than analyzing every wrapper in depth, the following sections focus on three mechanisms that shape how the rest of OCCT interacts with TKernel: the plugin resolver, the message bus, and the custom RTTI registry.
Plugin Resolver
OCCT defers plugin instantiation to runtime. A plugin shared library must export a single C-linkage factory function. The host framework loads the module dynamically, retrieves the factory address, and requests a service instance by GUID.
Core loader interface:
class Plugin_Entry
{
public:
Standard_EXPORT static Handle(Standard_Transient) Resolve(
const Standard_GUID& theUUID,
const Standard_Boolean theWarnIfMissing = Standard_True);
};
Plugin authors satisfy this contract through a macro that generates the exported factory:
#define OCCT_PLUGIN_FACTORY(Impl) \
extern "C" Standard_EXPORT Standard_Transient* PluginFactory( \
const Standard_GUID& theKey) { \
return const_cast<Standard_Transient*>(Impl::GetFactory(theKey).get()); \
}
Each plugin module implements a static GetFactory method that maps GUIDs to concrete services. In interactive test harnesses such as Draw, plugins additionally register interpreter commands, making new algorithms available from the scripting console without recompiling the host.
Message Bus
The diagnostic layer decouples message producers from consumers. Code that detects an exceptional condition emits an alert tagged with a priority; independent drivers decide whether to print to a terminal, append to a log, or write to the system event journal.
Alert API:
class Msg_Alert
{
static Msg_Messenger::Buffer Emit(Msg_Priority thePriority)
{
return DefaultBus()->Emit(thePriority);
}
static void Emit(const TCollection_AsciiString& theText, Msg_Priority thePriority)
{
DefaultBus()->Emit(theText, thePriority);
}
};
The central messenger multiplexes output to any number of attached drivers:
class Msg_Messenger
{
Standard_EXPORT Standard_Boolean AddDriver(const Handle(Msg_Driver)& theDriver);
Standard_EXPORT Standard_Boolean RemoveDriver(const Handle(Msg_Driver)& theDriver);
Standard_EXPORT void Emit(const Standard_CString theText,
const Msg_Priority thePriority = Msg_Warning) const;
};
Concrete drivers inherit from a common base and filter by severity:
class Msg_Driver
{
Msg_Priority FilterLevel() const { return myLevel; }
Standard_EXPORT virtual void Emit(const Standard_CString theText,
const Msg_Priority thePriority) const;
};
class Msg_DriverConsole : public Msg_Driver;
class Msg_DriverFile : public Msg_Driver;
class Msg_DriverSyslog : public Msg_Driver;
class Msg_DriverReport : public Msg_Driver;
Because the bus supports concurrent drivers, a single call site can stream diagnostics to a regression report, a developer console, and a production log simultaneously without explicit branching.
Run-Time Type Registry
OCCT implements its own reflection layer rather than relying exclusively on compiler RTTI. Every reflectable type derives from Standard_Transient, which carries an atomic reference counter. Type identity is represented by Standard_Type descriptors maintained in a global, lazily initialized, thread-safe map.
Root class and handle template:
template <class T> class Handle;
class Standard_Transient
{
Standard_EXPORT Standard_Boolean TypeMatch(
const opencascade::Handle<Standard_Type>& theKind) const;
Standard_EXPORT Standard_Boolean TypeDerived(
const opencascade::Handle<Standard_Type>& theKind) const;
private:
std::atomic_int myCounter;
};
Subclasses declare reflective metadata through header macros:
class Adaptor2d_Curve2d : public Standard_Transient
{
OCCT_RTTI_DECL(Adaptor2d_Curve2d, Standard_Transient)
};
Macros split declaration and implementation:
#define OCCT_RTTI_DECL(Class, Base) \
public: \
using Super = Base; \
static const char* StaticName() { return #Class; } \
Standard_EXPORT static const Handle(Standard_Type)& StaticType(); \
Standard_EXPORT virtual const Handle(Standard_Type)& RuntimeType() const Standard_OVERRIDE;
#define OCCT_RTTI_IMPL(Class, Base) \
const Handle(Standard_Type)& Class::StaticType() { \
return Standard_Type::Fetch<Class>(); \
} \
const Handle(Standard_Type)& Class::RuntimeType() const { \
return Class::StaticType(); \
}
The regisrty uses recursive template instantiation to capture inheritance chains:
class Standard_Type : public Standard_Transient
{
public:
template <class T>
static const Handle(Standard_Type)& Fetch()
{
return opencascade::type_slot<T>::Value();
}
};
template <class T>
class type_slot
{
public:
static const Handle(Standard_Type)& Value()
{
static Handle(Standard_Type) aSlot =
Standard_Type::Register(typeid(T), T::StaticName(), sizeof(T),
type_slot<typename T::Super>::Value());
return aSlot;
}
};
Registration deduplicates under a static mutex:
Standard_Type* Standard_Type::Register(const std::type_info& theInfo,
const char* theLabel,
Standard_Size theBytes,
const Handle(Standard_Type)& theBase)
{
static Standard_Mutex theGate;
Standard_Mutex::Sentry aLock(theGate);
registry_type& aCatalog = Registry();
auto aHit = aCatalog.find(theInfo);
if (aHit != aCatalog.end())
return aHit->second;
Standard_Type* aResult = new Standard_Type(theInfo, theLabel, theBytes, theBase);
aCatalog.emplace(theInfo, aResult);
return aResult;
}
Each template specialization of type_slot stores exactly one static handle. The first request for a given type triggers insertion into the catalog; later requests return the existing descriptor. This mechanism gives OCCT deterministic type queries, sized allocation metadata, and explicit parent links without depending solely on compiler-generated type_info.