2

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.

3
  • 1
    They want use fFunctions = functions; or fFunctions = {create, destroy}; somewhere. Commented Sep 1, 2022 at 15:16
  • 1
    You do this if you want to implement your own version of runtime polymorphism. It allows you to represent the entirety of an instance's behavior as a single value. Commented Sep 1, 2022 at 15:17
  • 1
    This is hand made variant of inheritance and virtual functions. Commented Sep 1, 2022 at 15:18

1 Answer 1

2

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);
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.