1

We are trying to namespace the versions of our API with namespaces, although we figured that we will be getting some problems with virtual functions :

namespace v1 {
    class someParam {
    public:
        someParam() {};
        virtual ~someParam() {};
    };

    class someClass {
    public:
        someClass() {};
        virtual ~someClass() {};
        virtual bool doSomething(someParam a);
    };


    bool someClass::doSomething(someParam a)
    {
        return true;
    }
}

namespace v2 {

    class someParam : public v1::someParam {
    public:
        bool doParamStuff();
    };
    bool someParam::doParamStuff()
    {
        return true;
    }
}

// Type Aliasing for v2 API
using someClass = v1::someClass;
using someParam = v2::someParam;

// SOME OTHER PROGRAM
class plugin : public someClass
{
public:
    plugin() {};
    virtual ~plugin() {};
    bool doSomething(someParam a) override;

};

In this specific case, we are creating extension of existing classes to allow binary compatibility. Although, we get a compilation error for plugin::doSomething because of the override keyword as it is not overriding someClass::doSomething because:

plugin::doSomething(v2::someParam) vs someClass::doSomething(v1::someParam).

Is there any way to fix up the plugin without explicitely using v1 for someParam in plugin class ? Ideally, nothing should be done on the plugin side, and without having to create v2::someClass

user925890
  • 11
  • 2

1 Answers1

0

This:

virtual bool doSomething(::v1::someParam a)

specifies a binary (and C++) interface. You cannot override it with

virtual bool doSomething(::v2::someParam a)

as that is a different type. They are not compatible. These signatures are unrelated.

When you update someParam, you must also update every interface that uses someParam, and then every interface that uses those interfaces, etc.

So, in namespace v2:

class someClass: ::v1::someClass {
public:
    virtual bool doSomething(::v1::someParam a) override final;
    virtual bool doSomething(someParam a);
};

and in doSomething(v1::someParam) describe how to generate a v2::someParam and pass it to the new doSomething.

If you cannot do this, you instead have to do this:

class someClass {
public:
    virtual bool doSomething(someParam a);
};

and make v2::someClass a type unrelated to v1::someClass.

Regardless, you do

using someClass = v2::someClass;

Now, instead of using using declarations, you can instead conditually use inline namespaces.

When you update a version, make the current version the inline namespace. The others are normal namespaces.

Code will now silently start using the inline namespace that is "current".

You can import types from previous namespaces by using symbol = ::library_ns::v1::symbol; This should only be done when that type is unchanged, as well as all of its parameters.


Now, if your ::v2::someParam is only a helper, you can split someParamArg from someParamInstance types.

someParamArg would then be the argument type of the root of the someParam heirarchy (::v1::someParam), while someParamInstance would be ::v2::someParam; what people should create when they want to use it.

In this case, someParamArg needs to be able to consider every state of someParamInstance, even those from later versions. Hence this only works if ::v2::someParam is essentially a helper, or if it supports internal value-type polymorphism.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524