Quelle est la conception recommandée pour les API au public pour prendre en charge plusieurs types de POD tout en maximisant la compatibilité binaire?

StackOverflow https://stackoverflow.com/questions/1563491

Question

Je suis en train de concevoir une API publique face à C ++ pour un produit qui nécessite un binaire / DLL pré-compilée (il sera multi-plateforme). Je voudrais pour l'API pour permettre à l'utilisateur d'utiliser tout POD, nous soutenons (le cas échéant), mais les exigences de base sont une flexibilité maximale et la compatibilité binaire. Je suis en train de faire quelque chose d'un peu similaire à l'API de CPLEX (il est l'un de plusieurs inspirations), mais je pense qu'il ya peut-être une meilleure façon de spécifier des informations de type que la façon dont ils l'ont fait (par rapport à IloInt, IloNum, IloAny, Ilo * Var, etc., voir lien (espérons-le) pour la branche IloExtractable) sans déconner avec la compatibilité binaire. Ai-je tort? J'ai quelque chose à l'esprit, mais je ne me rappelle pas ce qu'il est ou si elle va même travailler, je crois qu'il ressemble à quelque chose comme les motifs des visiteurs ou décorateur, mais pour les types, quelqu'un pourrait-il me éclairer sur s'il vous plaît le sujet? J'ai mon livre Design Patterns par le GoF en face de moi.

Remarque: Les erreurs de syntaxe il pourrait y avoir ici ne font pas partie du problème à portée de main

.

Des exemples de ce que je crois que je ne peux pas utiliser, et la raison:

Peut-être .. mais cela risque de rendre les choses compliquées avec l'arbre d'expression.

template<typename ValueType>
Constraint : public Expression;

Probablement aura un impact sur l'expansion future.

IntConstraint : public Expression;
LongConstraint : public Expression;
DoubleConstraint : public Expression;

Laid comme le péché, peut causer beaucoup de problèmes subtils.

union Value
{
 int AsInt,
 float AsFloat
};
class Constraint : public Expression
{
  public:
  Value GetValue(Edge);
  void SetValue(Value, Edge);
  void SetUpper(Value, Vertex);
  void SetLower(Value, Vertex);
  ...
};

Edit: En réponse à Mads Elvheim (et après avoir trouvé ce lien ) Je me rends compte maintenant que je ne ai pas besoin d'exclure des modèles de mes possibilités qui est bon, mais je suis encore assez incertain c'est la meilleure idée - au moins pour la classe de contraintes (même si elle est une conception saine), pardonnez-moi ne pas être aussi clair que je pensais.

Afin de rendre mon API facile à utiliser la grammaire je définis à l'aide bnf (ce qui est assez nouveau pour moi). Cela a conduit à un arbre de syntaxe abstraite pour l'expression, les contraintes et les autres classes qui seront inclus qui interagissent avec les contraintes. Parce que d'autres classes seront en contact avec les contraintes, je préférerais éviter la transmission d'informations de type Munch que possible jusqu'à « la dernière minute » pour ainsi dire .. Je me sens comme je manquerai un niveau d'abstraction.

L'étude CPLEX me donne l'impression qu'ils inspirent leurs types en suivant les domaines des nombres (entiers et réels), l'expression de l'équation linéaire (bien sûr) et ce qui devrait donc être possible, ce qui rend tout à fait sens, hmm ...

(Apparemment, je ne peux pas poster plus d'un lien que je suis un nouvel utilisateur.)

Edit 2: Dans un premier temps j'ai décidé de tenir un ConstraintExpressionArgument entre les classes de contrainte et expression afin que je puisse encore identifier une contrainte dans mon arbre d'expression sans être au courant du type manipule qui est bon

Un autre détail j'ai oublié de mentionner est que, contrairement à CPLEX où la classe contrainte est inutilisable par lui-même, ma classe de contrainte est actuellement une classe d'utilisateurs utilisable, mais comme dans CPLEX je veux aussi laisser la place pour l'expansion (donc la saisie question) ...

Quoi qu'il en soit, au moment où j'ai l'équivalent de

class ConstraintExpressionArgument : public Expression;
template<typename ValueType>
class Constraint : public ConstraintExpressionArgument;
Était-ce utile?

La solution 2

Bon, je pense que j'ai ce que je dois, au moins pour l'instant.

mentionné ci-dessus

class ConstraintExpressionArgument : public Expression;
template<typename ValueType>
class Constraint : public ConstraintExpressionArgument;

m'a fait sur la bonne voie pour séparer le type de contrainte-ness.

Autres conseils

La plupart des systèmes fournissent un <types.h> ou en-tête de <inttypes.h> avec des définitions de type que vous pouvez utiliser, si la plage exacte ou le nombre de bits est importante. Je ne vois pas une bonne raison de jeter l'héritage au problème. Vous pouvez également utiliser std::numeric_limits avec la macro BOOST_STATIC_ASSERT() Boost pour générer un sens de compilation affirme, si un type ou un type de modèle ne correspond pas à une exigence. L'exigence pourrait être un entier vs flotteur, plage, valeur minimale représentable, précision, etc. Par exemple:

#include <limits>
#include <inttypes.h>
#include <boost/static_assert.hpp>

template<class T, int bits> T Add(const T& a, const T& b)
{
    BOOST_STATIC_ASSERT( std::numeric_limits<T>::is_integer     );
    BOOST_STATIC_ASSERT( std::numeric_limits<T>::is_signed      );
    BOOST_STATIC_ASSERT( std::numeric_limits<T>::digits == bits );

    return a + b;
}

Si std :: numeric_limits n'a pas tous vos types, utilisez la spécialisation de modèle pour les étendre et les mettre en œuvre.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top