Home > Software engineering >  Why create a struct of function pointers inside a class?
Why create a struct of function pointers inside a class?

Time:09-02

I was digging around in the Vulkan backend for the Skia graphics API, found here, and I don't understand a piece of code.

Here's the smallest code example:

struct VulkanInterface : public SkRefCnt {

public:
    VulkanInterface(VulkanGetProc getProc,
                    VkInstance instance,
                    VkDevice device,
                    uint32_t instanceVersion,
                    uint32_t physicalDeviceVersion,
                    const VulkanExtensions*);

    /**
     * The function pointers are in a struct so that we can have a compiler generated assignment
     * operator.
     */
    struct Functions {
        VkPtr<PFN_vkCreateInstance> fCreateInstance;
        VkPtr<PFN_vkDestroyInstance> fDestroyInstance;

        // a ton more functions here
    } fFunctions;
};

Why would you create a struct of function pointers in a class?

Why this extra layer of abstraction where you have to add fFunctions-> everywhere?

I know there's a comment with an explanation and I know what those words mean, but I don't understand the comment as a whole. I just need it broken down a little more. Thanks.

CodePudding user response:

With regular polymorphic inheritance

struct Base
{
    virtual ~Base() = default;
    virtual void foo();
    // ...
};

struct D1 : Base
{
    void foo() override;
    // ...
};

struct D2 : Base
{
    void foo() override;
    // ...
};

You cannot assign to base class without slicing:

D1 d1;
Base b = d1;
b.foo(); // call Base::Foo

or treat object with value semantic:

D1 d1;
D2 d2;
d2 = d1; // Illegal

you have to use (smart) pointer instead.

In addition, you cannot mix (at runtime) from different virtual functions

Base base;
base.foo = &D2::foo; // imaginary syntax, Illegal
base.bar = &D1::bar; // imaginary syntax, Illegal

Having those function pointers inside the class allow the above (at the price of bigger object).

struct VulkanInterface
{
    void (*foo) ();
    void (*bar) (VulkanInterface& self);
    // ...
};

VulkanInterface makeVulkanInterface1() { return {my_foo, my_bar}; }
VulkanInterface makeVulkanInterface2() { return {my_foo2, my_bar2}; }
VulkanInterface v1 = makeVulkanInterface1();
VulkanInterface v2 = makeVulkanInterface2();
VulkanInterface v = v1;
v = v2;
v.foo = v1.foo;
v.bar(v);
  • Related