Inspection interface
Every entity within the SmartCGMS framework may support multiple additional entity interfaces. As stated in object model page, besides the reference counting methods, every object is required to implement
the QueryInterface
method.
The method signature is as follows:
virtual HRESULT IfaceCalling QueryInterface(const GUID* riid, void **ppvObj) = 0;
This follows the COM standard in all aspects - outer code may call the QueryInterface
method on an object with any interface GUID given in riid
. This may have two outcomes:
- the object (its class) does not support, neither implements the queried interface; in this case,
E_NOINTERFACE
is returned - the object (its class) supports and implements the queried interface
- the object typecasts itself (or related object pointer) to the interface type, and stores the result into the
ppvObj
parameter - the reference count is incremented, as a new reference is being constructed
- when using the method
Internal_Query_Interface
ofrefcnt::CReferenced
(part of C++ SDK), the reference count is incremented automatically
- when using the method
S_OK
is returned
- the object typecasts itself (or related object pointer) to the interface type, and stores the result into the
this
pointer at the time of method call. This is due to the polymorphic type implementation in any object-oriented languages and environments.
Known inspections
The SmartCGMS recognizes a handful of inspection interfaces to serve a specific purposes (list contains also the GUID aliases):
scgms::ISignal_Error_Inspection
(scgms::IID_Signal_Error_Inspection
) - the entity is able to calculate a metric from given signalsscgms::IDrawing_Filter_Inspection
(legacy) (scgms::IID_Drawing_Filter_Inspection
) - the entity is able to draw a selected graphs from data- this interface is kept for legacy reasons, any new implementations should be based on the
scgms::IDrawing_Filter_Inspection_v2
interface
- this interface is kept for legacy reasons, any new implementations should be based on the
scgms::IDrawing_Filter_Inspection_v2
(scgms::IID_Drawing_Filter_Inspection_v2
) - the entity implements a generic drawing mechanisms and viewsscgms::ILog_Filter_Inspection
(scgms::IID_Log_Filter_Inspection
) - the entity records a list of strings and exports them (e.g a log filter)scgms::IEvent_Export_Filter_Inspection
(deprecated) (scgms::IID_Event_Export_Filter_Inspection
) - this entity exports device events (all or selected), by invoking a previously registered callbackscgms::IFilter_Feedback_Receiver
(scgms::IID_Filter_Feedback_Receiver
) - this entity serves as a feedback receiver and could be linked with the matching sender entityscgms::IFilter_Feedback_Sender
(scgms::IID_Filter_Feedback_Sender
) - this entity serves as a feedback sender; the SmartCGMS links this entity with matching receiver entites upon configurationdb::IDb_Sink
(db::Db_Sink_Filter
) - this entity requires the database connection; the SmartCGMS injects the connector instance through theSet_Connector
upon configuration
Please, refer to the SmartCGMS interface code (common/iface/FilterIface.h
) for detailed documentation.
Example implementation
Let us suppose we want to define an interface, that poses a requirement to report count of device events that passes through it. This is also a very simple example, that just serves a purpose of demonstration.
At first, we need to define the interface GUID and the interface itself as a base class. This definition often resides in a shared header file, that is included by both caller and callee code. The entity is also required to implement the refcnt::IReferenced
interface, and as we want to use more complex polymorphism scheme and multiple inheritance, we
use virtual inheritance to deal with the "diamond problem":
constexpr const GUID IID_Event_Counter_Inspection = { 0xb0321b86, 0xd24f, 0x4204, { 0xb4, 0xb0, 0x16, 0xab, 0xba, 0x95, 0x31, 0x54 } }; // {B0321B86-D24F-4204-B4B0-16ABBA953154}
class IEvent_Counter_Inspection : public virtual refcnt::IReferenced {
public:
virtual HRESULT IfaceCalling Report_Count(uint64_t* const count) const = 0;
};
The general requirement would be, that the count
parameter points to a valid 8-byte long memory, where we want to store the actual count. The method returns S_OK
when the count
was filled with a valid count
of device events. It returns S_FALSE
when no events were recorded yet, and the count
was filled with a zero. It returns E_INVALIDARG
, when count
is nullptr
.
Let us have an existing entity called CArbitrary_Filter
, that serves its arbitrary purpose. We want to enhance this filter to support our defined interface. The first step is to implement the interface itself in the filter class.
An example of the implementation follows, excluding the unimportant parts:
class CArbitrary_Filter : public scgms::CBase_Filter, public IEvent_Counter_Inspection {
private:
uint64_t mEvent_Counter = 0;
...
protected:
...
virtual HRESULT Do_Execute(scgms::UDevice_Event event) override final {
...
mEvent_Counter++;
...
}
public:
...
virtual HRESULT IfaceCalling Report_Count(uint64_t* const count) const {
if (!count)
return E_INVALIDARG;
*count = mEvent_Counter;
return (mEvent_Counter > 0) ? S_OK : S_FALSE;
}
};
The final step in the implementing class definition and implementation is to declare the interface support using the QueryInterface
method. An example of such method implemented using the SmartCGMS SDK follows. Note that it is a
public member function of CArbitrary_Filter
:
public:
HRESULT IfaceCalling QueryInterface(const GUID* riid, void **ppvObj) {
if (Internal_Query_Interface<IEvent_Counter_Inspection>(IID_Event_Counter_Inspection, *riid, ppvObj))
return S_OK;
return E_NOINTERFACE;
}
Alternatively, when not using the C++ SDK, you may want to implement the full requirements on the method yourself. An example in C++ follows (the implementation is would be very similar in other languages):
public:
HRESULT IfaceCalling QueryInterface(const GUID* riid, void **ppvObj) {
if (*riid == IID_Event_Counter_Inspection) {
*ppvObj = dynamic_cast<IEvent_Counter_Inspection>(this);
AddRef();
return S_OK;
}
return E_NOINTERFACE;
}
The callee part is now finished. To obtain the interface reference in the caller code, let us use the QueryInterface
interface method to probe the support for the interface on such entity.
scgms::IFilter* referenced_filter = ...; // you would obtain this depending on the context
IEvent_Counter_Inspection* inspection = nullptr;
if (referenced_filter->QueryInterface(&IID_Event_Counter_Inspection, &inspection) == S_OK) {
uint64_t count;
HRESULT res = inspection->Report_Count(&count);
if (res == S_OK)
std::cout << "Total of " << count << " events passed through the filter" << std::endl;
else if (res == S_FALSE)
std::cout << "No events passed through the filter" << std::endl;
else
std::cout << "Invalid call to the Report_Count method" << std::endl;
// if we successfully obtained the inspection instance, we officially own one reference; we need to release it as soon, as we don't use it to avoid memory leaks
inspection->Release();
}
else
std::cout << "The filter does not support the event counter interface" << std::endl;
To avoid the need of manual release, you can use the reference counting SDK:
using SEvent_Counter_Inspection = std::shared_ptr<IEvent_Counter_Inspection>;
SEvent_Counter_Inspection sptr = refcnt::make_shared_reference_ext<SEvent_Counter_Inspection, IEvent_Counter_Inspection>(inspection, false);
This creates a shared pointer with a custom deletor, that calls the Release
method automatically during the destructor call. Note that the false
parameter instructs the constructor not to add a reference counter value, as the QueryInterface
already did that.