C++ Design – Hiding Implementation from Users of a Dynamic Library

cdesign

I'm building a DLL and in my public headers I have this:
(definitions are in .cpp but for clarity I show them in .hpp here)

ObjectTag.hpp:

class API_DLL ObjectTag {
public:
    ObjectTag() : mUUID(UIDGenerator::Generate()) {
    }

    uuid getUUID() {
        return mUUID;
    }

private:
    uuid mUUID;
};

Texture.hpp:

class API_DLL Texture : public ObjectTag {
public:
    Texture() : ObjectTag(){
    }

};

I have a Scene editor that creates Texture objects, and saves them in a file with their uuid. Now I want to load those Texture objects read from the file inside the library and set their previously generated and saved UUID.

My first attempt was to make this simple function void setUUID(uuid id):

class API_DLL ObjectTag {
public:
    ObjectTag() : mUUID(UIDGenerator::Generate()) {
    }

    uuid getUUID() const {
        return mUUID;
    }
    void setUUID(uuid id) {
        mUUID=id;
    }

private:
    uuid mUUID;
};

But this will allow the users of the library to modify the UUID variable. How would you design this in a way that prevents doing that from the users of the library?

Best Answer

How would you design this in a way that prevents doing that from the users of the library?

Separate Texture interface from implementation:

class API_DLL ObjectTag
{
    uuid mUUID;

public:
    uuid getUUID()
    {
        return mUUID;
    }
};

class API_DLL Texture: public ObjectTag // Texture interface visible in client code
{
public:
    virtual ~Texture() = 0;
    virtual void DoTextureThings() = 0;
};

template<typename T>
class EditableObjectTag: public T // EditableObjectTag not exposed to client code
                                  // defines save functionality
{
public:
    void setUUID(uuid id) { mUUID = id; }
};

class YourTexture: public EditableObjectTag<Texture> // YourTexture not exposed
                                                     // to client code

{
    void DoTextureThings() override { ... }
};

Your load function:

API_DLL std::vector<std::unique_ptr<Texture>> LoadTextures()
{
    std::vector<std::unique_ptr<Texture>> result;

    result.emplace_back(new YourTexture{ bla, bla, bla });

    return result;
}

Client code can now work with textures, without caring that a Texture is an abstract class, and the Texture interface doesn't mention anything about setting values into the object:

void ClientCode()
{
    auto textures = LoadTextures();
    textures[0]->DoTextureThings();

    // textures[0]->setUUID(someUUID); -> fails to compile
}

Note: the EditableObjectTag class is an aspect-based design: it adds an editable interface on it's template argument.

Related Topic