Anteru's blog
  • Consulting
  • Research
    • Assisted environment probe placement
    • Assisted texture assignment
    • Edge-Friend: Fast and Deterministic Catmull-Clark Subdivision Surfaces
    • Error Metrics for Smart Image Refinement
    • High-Quality Shadows for Streaming Terrain Rendering
    • Hybrid Sample-based Surface Rendering
    • Interactive rendering of Giga-Particle Fluid Simulations
    • Quantitative Analysis of Voxel Raytracing Acceleration Structures
    • Real-time Hybrid Hair Rendering
    • Real-Time Procedural Generation with GPU Work Graphs
    • Scalable rendering for very large meshes
    • Spatiotemporal Variance-Guided Filtering for Motion Blur
    • Subpixel Reconstruction Antialiasing
    • Tiled light trees
    • Towards Practical Meshlet Compression
  • About
  • Archive

Breaking dependencies with COM-style interfaces

March 21, 2007
  • Programming
approximately 10 minutes to read

Ever had the problem that you have a base class, and a few of the derived classes share a certain interface and you didn’t want to pull it up to the main class because it drags a whole lot of dependencies with it? Then read on for how to write your own COM-like interface query. The main idea is to have a single function in your base class (in this example, IBaseClass) which allows you to get various interfaces to it. You have to consider how to identify an interface, in this example, I use GUIDs which are reasonably simple to implement yet well suited by being … um, unique :).

So here we go: We create a base interface class (IInterface, or IUnknown) from which we derive our interfaces. Actually, you could skip that, too, but I think it can be useful for compile-time checks later. Next, we have a function getInterface (id, target) which returns us a pointer to where the interface is. In this example, we won’t do reference counting and thread-safe interfaces but a quick hack: The object creates an interface instance on load and always gives away a pointer to it. This works nicely if the interface is not too heavy and is reasonable unless you want to have more than one interface ready - but then, you should rather think about your class hierarchy :).

Here’s the example, pretty self-explaining:

#include <iostream>
#include <string>

// Simple POD Guid
struct GUID
{
    bool operator==(const GUID& rhs) const
    {
        return a == rhs.a && b == rhs.b && c == rhs.c && d == rhs.d;
    }

    const unsigned int a, b, c, d;
};

// The base class for all interfaces
struct IInterface
{
    static GUID guid;

    virtual ~IInterface ()
    {
    }
};

// The GUID instance for IInterface
GUID IInterface::guid = {
    // {94BE63E1-3B3A-4553-8591-823FBCD58F8A}
    0x94be63e1, 0x3b3a4553, 0x8591823f, 0xbcd58f8a
};

// An example interface
struct IExample : public IInterface
{
    static GUID guid;

    virtual void sayHello () = 0;
};

// The GUID instance for IExample
GUID IExample::guid = {
    // {C6B4D9CA-7F28-4e7c-870F-DC8F56AD7DC8}
    0xc6b4d9ca, 0x7f284e7c, 0x870fdc8f, 0x56ad7dc8
};

// ---

#include <stdexcept> // for std::invalid_argument

// Our base class
struct IBaseClass
{
    void getInterface (const GUID& iid, void** target)
    {
        if (target == 0)    // can't write to 0 ...
            throw std::invalid_argument ("target must not be null");
        else                // call through to the implementation
            getInterfaceImpl (iid, target);
    }

    virtual ~IBaseClass ()
    {
    }

protected:
    // Each derived class will implement this
    virtual void getInterfaceImpl (const GUID& iid, void** target) = 0;
};

// Non-member call so you don't have to cast to void** on your own.
// Optionally, add a compile-time-check if T is derived from
// IInterface here!
template <typename T>
void getInterface (IBaseClass& object, const GUID& iid, T** target)
{
    object.getInterface (iid, reinterpret_cast<void**>(target));
}

// ---

// Simple test class
class Test : public IBaseClass
{
public:
    Test ()
        : interface_ (this)
    {
    }

    void say (const std::string& s)
    {
        std::cout << s << std::endl;
    }

private:
    // The interface, note that it is private
    class TestInterface : public IExample
    {
    public:
        TestInterface (Test* parent)
            : parent_ (parent)
        {
        }

        void sayHello ()
        {
            parent_->say ("Hello");
        }

    private:
        Test* parent_;
    };

    TestInterface interface_;

    // Our own implementation for getInterface
    // Only knows IExample
    void getInterfaceImpl (const GUID& iid, void** target)
    {
        if (iid == IExample::guid)
            *target = &interface_;
        else
            throw std::runtime_error ("Unsupported interface");
    }
};

// ---

int main ()
{
    Test t;
    IExample* e = 0;

    getInterface (t, IExample::guid, &e);
    e->sayHello ();
}
Previous post
Next post

Recent posts

  • Data formats: Why CSV and JSON aren't the best
    Posted on 2024-12-29
  • Replacing cron with systemd-timers
    Posted on 2024-04-21
  • Open Source Maintenance
    Posted on 2024-04-02
  • Angular, Caddy, Gunicorn and Django
    Posted on 2023-10-21
  • Effective meetings
    Posted on 2022-09-12
  • Older posts

Find me on the web

  • GitHub
  • GPU database
  • Projects

Follow me

Anteru NIV_Anteru
Contents © 2005-2025
Anteru
Imprint/Impressum
Privacy policy/Datenschutz
Made with Liara
Last updated February 21, 2019