FreeElectron
Component Writer's Guide

Purpose

This document should describes how to make your own components and package them into a library.

Component

Each component has to be virtually derived from fe::Component, but not neccessarily directly. Since components are instantiated through automatically generated factories, all component implementation classes should have a single constructor with no arguments.

Any component can participate in a post-constructor mechanism. This allows all participating classes in the the class hierarchy to have their initialize methods called in the same order as the original construction. Post-construction is useful for constructive operations that cannot be done safely or appropriately during real construction. See fe::Initialized for details.

Smart Pointers

Smart pointers use a reference-counting mechanism in order to destruct object once the count of references drops to zero. The FE smart pointer mechanism is intrusive; the count is held in the object itself. These objects are presumed to be derived from fe::Counted, but all that is technically required is that they provide some of the same method functions as fe::Counted.

There are three variations of smart pointers that are used in FE.

The regular smart pointer fe::sp, once assigned to a valid pointer, will stay valid as long as that reference is held in some persisting scope.

The weak smart handle fe::hp, can become invalid even if it was once valid. The isValid() method can check this status, but it is safer to simply cast an fe::hp to an fe::sp in a narrow scope where it is used, in case it becomes invalid between when it is checked and when it is accessed.

The protectable copy-on-write pointer fe::cp acts much like a regular smart pointer fe::sp except that it can be protected against writing by spontaneous cloning a duplicate. This is not used as often as smart pointer and handle pointer, but it is very important in the few cases where it is needed.

All components can be used in smart pointers (fe::sp), smart handles (fe::hp), copy-on-write pointers (fe::cp), and smart hubs (fe::Component::adjoin).

Interfaces and Implementations

The plugin system does not require an interface/implementation design paradigm. However, it does promote and facilitate its use.

This is how you can use interfaces in the context of this framework.

Interfaces are abstract classes. They contain a set of public virtual functions that state what implementions fulfilling that interface can do. Generally, interfaces are always virtually inherited to prevent redundant base classes. By convention, all interface class names in FE end with a capital 'I', such as fe::SurfaceI.

An implememention defines actual code for any number of interfaces. Under normal conditions, implementations should only be accessed through their interfaces.

Implementations can inherit from other implementions to extend or reuse existing code, even across multiple dynamic libraries. They generally inherit non-virtually since it is more efficient.

Inherited implementations have more intimate access to the base classes than would normally be available through just the interfaces. It is basically the same design responsibility that exists in normal class inheritance.

Dirty interfaces are deviations that contain state and/or functionality in the interface. They are frowned upon, but there is no mechanism to prevent them.

Component Naming

The string names for components are expected to follow a specific dot-based convention of the form "InterfaceI.Implementation.origin.misc".

By convention, the first token exactly states a C++ interface class that the component fulfills. A component can register as multiple interfaces if it would like to make itself explicitly available to fulfill all those interfaces. A component is not required to register for all interfaces that it could potentially fulfill.

The second token exactly states the C++ class that is explicitly instantiated for this name.

The third token states who is providing the interface, presumably a organization or company name.

The fourth token is basically anything that follows, perhaps a description or notice. It shouldn't be expected, and it probably shouldn't ever be matched against.

Requests for component creation are usually abbreviated. If a request string matches a candidate exactly up to where the terminator in the request aligns with a period in the candidate, the pair is considered a match.

For example, a request for "WindowI" can be resolved with "WindowI.NativeWindow.fe".

An asterisk between two periods matches anything in that token. Using the same example, "WindowI.*.fe" matches "WindowI.NativeWindow.fe" or "WindowI.SpecialWindow.fe", but not "WindowI.NativeWindow.substitute".

Complex regular expression matching is not supported in this context.

Library Packaging

A set of closely related components are grouped into a module. Each of these modules is compiled into a dynamic library. Each "DL" is represented as a shared object (.so) under Linux or a DLL (.dll) under Win32.

By convention, each module should include a moduleDL.cc file as described in fe::Library.