11
votes

DLL-Exportant des membres statiques de la classe de base de modèles

Dans une DLL, j'ai une classe non-modèle exportée avec une classe de base de gabarits. Cette classe de base de gabarit a une variable de membre statique. J'utilise l'élément de base statique dans un exécutable qui relie la DLL avec la classe non-modèles exportée.

Dans de nombreux scénarios, je reçois des symboles externes non résolus ou des plaintes sur les liaisons incohérentes. J'ai trouvé un scénario qui fonctionne, mais il semble être de Kludgey, donc je me demande s'il y a une meilleure façon et si cette meilleure façon peut également indiquer des défaillances dans le compilateur / liaison C ++ de VS2010 SP1.

C'est le scénario minimal de la DLL que je pourrai distiller - je ne pense pas pouvoir supprimer quoi que ce soit ici sans casser le scénario. xxx

alors l'utilisateur de la DLL < / p> xxx


3 commentaires

Vous pouvez écrire des DLL en C ++, mais l'interface exportée doit toujours être compatible C. Tout le reste demande des problèmes. Votre conception est fragile et peut casser à la moindre divergence dans les options du compilateur entre la bibliothèque et le client.


@Ben Voigt - pendant que je suis d'accord avec votre point lorsque vous consultez les bibliothèques générales, dans ce cas, la bibliothèque et le client sont sous mon contrôle totalement et qu'ils sont garantis pour être construits avec le même compilateur et les mêmes paramètres via divers mécanismes. Le maco _mydll_exports se développe à _declspec (Dllexport) ou _DeclSpec (Dllimport) en fonction de l'emplacement de l'inclusion - un modèle très courant avec des applications natives plus grandes C ++ Win32 de mon expérience.


Votre solution est déjà correcte. Linker élimine automatiquement les doublons en cas de modèles. De plus, VC ++ Linker devrait probablement préférer la version Dllimport de TBaseclass: G_INITIAL_VALUE, mais ce n'est pas le cas. En utilisant #IFDEF _MYDLL, vous l'aidez. Vous pouvez déposer un bogue sur MS Connect, mais ils ont tendance à fermer de tels problèmes que "ne résoudre" pas ".


4 Réponses :


6
votes

en C ++ en général, lorsqu'une classe ordinaire a un élément statique, elle doit être déclarée dans l'en-tête, mais instanciée dans un fichier source. À faire autrement entraînerait la création de trop d'instances de la classe statique.

Les modèles sont un peu de la même manière, à l'exception du compilateur a une magie pour les modèles qu'il n'a pas pour les non-modèles. Spécifiquement, il élimine par magie les instances en double d'une instanciation de gabarits lors de la phase de liaison d'une construction.

Ceci est la source de votre problème: les trucs à l'intérieur de la partie _mydll sont automatiquement instanciés par chaque fichier source comprenant ce modèle et fabrique également des objets TBaSeclass. Ensuite, le lieur élimine automatiquement les doublons.

problème est que vous avez deux liens: le lien DLL et le lien client. Les deux vont faire des instanciations à tabaseclass, et les deux vont faire ces objets g_initial_value.

Pour résoudre ce problème: déplacez le contenu dans le conditionnel _MYDLL dans le fichier CPP, le client ne recevra donc pas d'instructions pour construire l'instance elle-même.


7 commentaires

Je ne peux pas déplacer la définition de l'élément de modèle statique dans le fichier CPP, car la classe de modèle de base est également utilisée par le client à certaines fins (pour les instances / types non créés dans la DLL). Ou puis-je? Serait-ce moins krludgey de réiliter la statique spécialisée du côté du client?


Et devez le répéter une fois pour chaque spécialisation? J'aime mieux votre approche originale. Ce que vous pourriez faire est d'utiliser un conditionnel supplémentaire pour vous assurer que la définition des membres statiques n'est visible que des fichiers source où vous effectuez des instanciations explicites.


@Nicolamusatti - Je suis intrualisé par cette suggestion et jouera avec quelques idées liées à cela.


C ++ est une Nuissance car elle sépare la déclaration de la mise en œuvre. Les modèles C ++ se contournent que Nuissance et permettent tout dans les en-têtes (en échange d'une recompilation lente et fragile), mais les variables d'éléments statiques ne peuvent pas en tirer parti.


RE: deux instanciations de tbaseclass - Je suis d'accord que c'est ce qui se passe, mais je pense que cela ne devrait pas parce que, comme vous le direz également dans votre réponse, le compilateur devrait éliminer les instances en double du modèle, y compris la statique, mais elle pas (mais seulement pour la statique). Je commence à penser que les modèles et les DLL ne mélangent de toute façon pas, pour plusieurs autres raisons aussi.


Je suis d'accord. L'approche standard est qu'une bibliothèque de modèles n'est que des en-têtes. Il peut y avoir des classes non-modèles associées dans une DLL, mais pas des classes de modèle dans la DLL. Je regarderais la bibliothèque ATL de Microsoft pour des idées.


@ALANBALJEU - Je suis actuellement à la recherche de différentes stratégies d'exportation uniquement des modèles explicitement instanciés. Ceci est principalement à cause d'un autre problème (le compilateur détectant une instanciation implicite d'une partie d'une gabarit à l'intérieur d'une DLL, avec des erreurs de liaison sur les pièces qui n'étaient pas implicitement instanciées).



4
votes

Le fait que vous utilisiez votre classe de modèle à partir de la DLL et de l'exe rendent les choses plus déroutantes, mais toujours, cela peut travailler.

Tout d'abord, vous devez implémenter votre classe de base de modèle entièrement dans le fichier d'en-tête. . Si vous ne savez pas pourquoi, assurez-vous de lire la réponse acceptée à Cette question .

Oublions maintenant des modèles et des DLL, et envisagez un cas beaucoup plus simple. Disons que vous avez la classe C, avec un membre statique. Vous allez normalement coder cette classe de cette façon: xxx

rien d'impair ou compliqué ici. Considérons maintenant ce qui se passerait si vous déplacez la déclaration statique dans le fichier d'en-tête. S'il n'y a qu'un seul fichier source qui inclut l'en-tête, tout va bien fonctionner. Mais si deux fichiers source ou plus comprennent cet en-tête, cet élément statique sera défini plusieurs fois, et le linker ne va pas comme ça.

Le même concept s'applique à une classe de modèle. Votre #Ifdef _mydll Le pirk ne fonctionne que car à partir de la DLL, vous incluez ce fichier d'en-tête uniquement une fois. Mais au moment où vous incluez ce fichier à partir d'un autre fichier source, vous commencerez à obtenir des erreurs de liaison sur la DLL! Donc, je suis tout à fait d'accord avec vous, ce n'est pas une bonne solution.

Je pense qu'une chose qui complique votre configuration est que vous autorisez à la fois la DLL et l'EXE d'instancier cette classe de base de gabarit. Je pense que vous auriez une solution beaucoup plus propre si vous trouvez un "propriétaire" pour chaque instanciation de la classe de modèle. Après votre exemple de code, remplacons myClass avec MyDLLClass et MyExeclass. Ensuite, vous pourriez faire ce travail comme celui-ci: xxx

J'espère que cela a du sens.


2 commentaires

Vous expliquez plus clairement la racine du problème plus clairement que moi, car je vous donnerais un +1. Cependant, mon hack kludgy n'échouera pas à l'intérieur de la DLL! On dirait à l'intérieur de la DLL Le compilateur parvient à détecter correctement que le modèle de base a été instancié et ne présente pas une seconde instance de l'initialisation statique de tbaseclass .


Intéressant. Vous êtes correct, le compilateur Microsoft C ++ (VS2010) vous permet de définir le symbole statique multiple (même avec différentes valeurs attribuées à chacun!) Pourtant, le linker ne voit que l'un et l'accepte en silence. Cependant, j'ai également testé CGC, ce qui ne l'aime certainement pas et se comporte comme je l'ai décrit ci-dessus. Je vous recommande vivement de ne pas essayer de trouver une solution spécifique au compilateur, je considère que cela a un bogue VS.



0
votes

Bien que je suggère d'utiliser votre approche actuelle, il est en fait possible d'éviter #IFDEF en utilisant une syntaxe plus ancienne pour l'exportation de modèles d'une DLL. Tout cela va au fichier d'en-tête de DLL: xxx

au moment de l'exécution L'adresse de g_initial_value dans le code client réside dans l'espace d'adressage de la DLL afin qu'il semble fonctionner correctement.


1 commentaires

Cette solution ne fonctionne pas non plus - dès que vous essayez d'instancier un tbaseclass en dehors de la DLL qui n'était pas déjà explicitement (ou implicitement) instancié à l'intérieur de la DLL, vous obtenez une erreur C2491 - Définition de Dllimport Static Data Nument non autorisé.



1
votes

En fait, la classe de base de la classe exportée est également exportée si la classe de base est une classe de modèle, mais pas vraie au contraire. Veuillez vous référer à http: // www. codesynthesis.com/~boris/blog/2010/01/18/dll-export-cxx-templed/

Pour votre question spécifique, je vous suggère de définir une méthode statique dans le modèle de base qui renvoie une variable (pointeur?) d'intérêt. Ensuite, une seule définition se produira sur plusieurs DLL ou EXE qui dépend de votre bibliothèque.


1 commentaires

Intéressant - on dirait qu'il y a encore beaucoup de coins sombres avec des modèles et des dlls. À la fin, nous avons résolu le problème en supprimant les membres de la const statique des modèles à ailleurs - et évitez le plus possible de la statique.