0

Nous essayons de namespace les versions de notre API avec les espaces de noms, bien que nous avons pensé que nous allons recevoir quelques problèmes avec des fonctions virtuelles:C++ en utilisant les espaces de noms pour le versionnage peut causer des problèmes d'héritage avec les remplacements

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; 

}; 

Dans ce Dans un cas spécifique, nous créons l'extension de classes existantes pour permettre la compatibilité binaire. Bien, nous obtenons une erreur de compilation pour plugin :: doSomething en raison du mot-clé override car il ne surcharge pas someClass :: doSomething parce que:

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

Y at-il un moyen de réparer le plugin sans utiliser explicitement v1 pour someParam dans la classe plugin? , Rien ne devrait idéalement se faire du côté du plug-in, et sans avoir à créer v2 :: someClass

Répondre

0

Ce:

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

spécifie une interface binaire (et C++). Vous ne pouvez pas le remplacer par

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

car c'est un type différent. Ils ne sont pas compatibles. Ces signatures ne sont pas liées.

Lorsque vous mettez à jour someParam, vous devez également mettre à jour toutes les interfaces qui utilise someParam, et chaque interface qui utilise ces interfaces, etc.

Ainsi, namespace v2:

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

et doSomething(v1::someParam) décrivent comment générer un v2::someParam et le transmettre au nouveau doSomething.

Si vous ne pouvez pas faire cela, vous devez plutôt faire:

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

et faire v2::someClass un type sans rapport avec v1::someClass.

Peu importe, vous

using someClass = v2::someClass; 

Maintenant, au lieu d'utiliser using déclarations, vous pouvez utiliser à la place conditually inline namespace s.

Lorsque vous mettez à jour une version, définissez l'espace de noms inline dans la version actuelle. Les autres sont des espaces de noms normaux.

Le code commencera maintenant silencieusement en utilisant le inline namespace qui est "actuel".

Vous pouvez importer les types de namespaces précédents par using symbol = ::library_ns::v1::symbol; Cela ne devrait être fait lorsque ce type est inchangé, ainsi que tous ses paramètres.


Maintenant, si votre ::v2::someParam est seulement une aide, vous pouvez diviser someParamArg de someParamInstance types.

someParamArg serait alors le type d'argument de la racine du someParam heirarchy (::v1::someParam), tandis que someParamInstance serait ::v2::someParam; ce que les gens devraient créer quand ils veulent l'utiliser.

Dans ce cas, someParamArg doit être en mesure d'examiner chaque état de someParamInstance, même celles des versions ultérieures. Par conséquent, cela ne fonctionne que si ::v2::someParam est essentiellement un assistant, ou s'il prend en charge le polymorphisme de type valeur interne.