Gigognes C ++ définitions de modèle
Question
Je suis abusant de C ++ des modèles un peu et je vais avoir du mal à comprendre quelque chose. Disons que j'ai deux types qui devrait vraiment être hérité d'un type de base, mais pour des raisons de vitesse, je ne peux pas se permettre d'avoir la surcharge de fonction virtuelle (je l'ai benchmarkée, et les appels virtuels ruine des choses pour moi!).
Tout d'abord, voici les deux classes je
template<class DataType> class Class1
{
//Lots of stuff here
}
template<Class DataType> class Class2
{
//The same stuff as in Class1, but implemented differently
}
Dans une conception typique oo, et Class1
hériterait de Class2
et je pourrais IInterface
avoir une fonction qui ressemble à ceci
DoStuff(IInterface& MyInterface)
{
}
Mais je ne peux pas le faire, alors je l'ai fait
template <class C>
DoStuff(C& c)
{
}
Je sais que ce n'est pas assez, car il n'y a rien (au niveau du compilateur) pour appliquer que et DoStuff
mettre en œuvre la même case
interface, mais pour des raisons de vitesse, je suis briser certaines règles.
Ce que j'aimerais faire est de créer un rappel sur la fonction if
, mais je ne peux pas comprendre comment le faire fonctionner avec les modèles (d'autant plus qu'il est le caché là-dedans.
Par exemple, cela fonctionne en ce moment
DoStuff(char* filename)
{
switch (//figure out the type i need to make)
{
case 1: return DoStuff(Class1<int>(filename));
case 2: return DoStuff(Class1<double>(filename));
}
}
template<class DataType>
DoStuff(DataType* pdata)
{
return DoStuff(Class2<DataType>(pdata));
}
template<class C>
DoStuff(C c)
{
c.Print();
}
Maintenant, je sais que vous vous demandez, pourquoi utiliser et #defines
()
? Eh bien la différence sous-jacente entre traitement d'un dossier et le traitement de la mémoire est si grand, qu'il est logique d'avoir différentes classes pour les différents types d'entrée (plutôt que la surcharge juste le constructeur et l'avoir se comportent différemment pour les différentes entrées). Encore une fois, je l'ai fait référence cela et il est beaucoup plus rapide d'avoir des cas particuliers traités dans leurs propres classes plutôt que d'avoir s <class C>
/ <class DataType>
s dans toutes les fonctions.
Alors, ce que je voudrais faire est de cacher beaucoup de cette mise en œuvre par les développeurs juniors, je ne veux pas avoir à créer trois différents surchargé s pour template <class C<DataType>>
gérer les différentes entrées. Idéalement, je viens de mettre en place un certain type de rappel avec et tout ce qu'ils template<template< class DataType> class C>
avaient besoin de faire quelque chose comme créer une classe appelée et surcharger le BigSwitch
opérateur et ont MemArray
foncteur faire le travail.
Le problème que je vais avoir est que la fonction qui fait le FileArray
travail que par GetX
mais sans canevas C est lui-même par X
et sans canevas tout ce que je ne peux pas comprendre comment passer tout autour dans d'une manière générique. Par exemple, je ne peux pas utiliser ou template<class ArrayType>
ArrayType<DataType>
. Il ne sera pas seulement la compilation.
Quelqu'un at-il un bon truc pour avoir un appel générique retour, une fonction ou un foncteur (je ne me soucie pas), avec cette classe imbriquée basé sur un modèle? Fondamentalement, je veux quelque chose où je peux écrire une fonction générique qui ne se soucie pas de la classe qui est le stockage des données et ont appelé par une que la plupart du temps la fonction commune qui détermine quelle classe à utiliser.
BigSwitch(CallBack,Inputs)
{
switch(//something)
{
case 1: return CallBack(Class1<Type>(Inputs))
case 2: return CallBack(Class2<Type>(Inputs))
}
}
De cette façon, je peux écrire une fonction et ont <=> d'autres gens écrivent les fonctions callback.
Des idées?
EDIT de clarification pour Jalf:
J'ai deux classes très similaires, et <=> qui représentent essentiellement <=> le même type de données, mais le magasin de données est très différent. Pour le rendre plus concret, je vais utiliser un exemple simple: un tableau est <=> simple et ressemble à un <=> tableau mais plutôt que de stocker dans la mémoire est stocke dans un fichier (parce qu'il est trop grand pour tenir dans la mémoire) . Donc, je vais les appeler et <=> en ce moment <=>. Alors disons que je voulais la somme des tableaux. Je peux faire quelque chose comme ça
template <class ArrayType, class ReturnType>
ReturnType Sum(ArrayType A)
{
ReturnType S=0;
for (int i=A.begin();i<A.end();++i)
{
S+=A[i];
}
return S;
}
Mais maintenant, je besoin d'un moyen de charger des données réelles dans le tableau. Si c'est un tableau à base de mémoire, je fais
MemArray<DataType> M(pData);
et si son fichier-baaed, je ferais ceci
FileArray<DataType> F(filename);
et ces deux appels sont valides (car le compilateur génère à la fois les chemins de code au moment de la compilation)
double MS=Sum<MemArray<DataType>,double>(M);
double FS=Sum<FileArray<DataType>,double>(F);
Tout cela suppose que je sais ce que le type de données est, mais pour un tableau à base de fichiers, je sais pas le type de données jusqu'à ce que j'ouvre le fichier et d'interroger l'en-tête pour savoir quel type de données dans le tableau.
double GetSum(char* filename)
{
int DataTypeCode=GetDataTypeCode(filename);
switch (DataTypeCode)
{
case 1: return Sum<FileArray<int>,double>(FileArray<int>(filename));
case 2: return Sum<FileArray<double>,double>(FileArray<double>(filename));
}
}
template <class DataType>
double GetSum(DataType* pData)
{
return Sum<MemArray<DataType>,double>(MemArray<DataType>(pData));
}
Tout cela fonctionne, mais il faut écrire deux fonctions surchargées et une <=> fonction pour tout ce que <=> je veux faire. les fonctions sont essentiellement <=> le même code à chaque fois, sauf pour le <=> qu'il appelle. Donc, j'aimerais pouvoir écrire quelque chose comme
double GetX(CallBackType X, char* filename)
{
int DataTypeCode=GetDataTypeCode(filename);
switch (DataTypeCode)
{
case 1: return X<FileArray<int>,double>(FileArray<int>(filename));
case 2: return X<FileArray<double>,double>(FileArray<double>(filename));
}
}
template <class DataType>
double GetX(CallBackType, DataType* pData)
{
return X<MemArray<DataType>,double>(MemArray<DataType>(pData));
}
pour que je puisse appeler
GetX(Sum,filename)
puis la finr quand quelqu'un d'autre veut ajouter une nouvelle fonction, tout ce qu'ils doivent faire est d'écrire la fonction et appelez
GetX(NewFunction,filename)
Je suis à la recherche d'une façon d'écrire mes fonctions surchargées et mes <=> fonctions afin que <=> je peux abstraitement l'entrée / stockage des algorithmes réels. Normalement, ce n'est pas un problème difficile, il est juste que je vais avoir du mal parce que la fonction contient un argument <=> de modèle qui est lui-même basé sur un modèle. Le dispose également d'une <=> implicite caché là-dedans <=>. Le compilateur est malheureux à ce sujet.
La solution
En se concentrant sur la première partie de votre question (pourquoi vous n'êtes pas seulement en utilisant l'héritage):
Une façon courante de faire polymorphisme de compilation et de donner accès à la classe dérivée membres par la classe de base est par la CRTP motif.
template <typename T>
class IInterface {
void DoStuff() {
void static_cast<T*>(this)->DoStuff()
}
};
class Class1 : IInterface<Class1> {
void DoStuff(){...}
}
Est-ce que résoudre votre problème?
Edit: Soit dit en passant, je suis heureux d'avoir pu aider, mais la prochaine fois s'il vous plaît essayer de structurer votre question un peu plus.
Je n'avais vraiment aucune idée de ce que vous demandiez, donc il était tout simplement un coup de poignard dans le noir, sur la base des 3 premières lignes de votre question. ;)
Vous n'expliquer vraiment ce que vous essayez d'atteindre, que ce que votre solution de contournement qui ne fonctionne ressemble. Commencez poser le problème, puisque c'est ce dont nous avons vraiment besoin de savoir. Ensuite, vous pouvez fournir des détails sur vos solutions de contournement actuelles. Et lorsque le code affichant, ajouter un peu de contexte. Où sont DoStuff () appelé à partir, et pourquoi les développeurs juniors ont besoin de les définir? (Vous avez déjà fait, non?)
Que serait dit les développeurs juniors faire avec ce code en premier lieu?
Et il est source de confusion que vous fournissez les cas spécifiques (1 et 2), mais pas l'instruction switch lui-même (// quelque chose)
Vous obtiendrez beaucoup plus (et mieux et plus vite) répond à la prochaine fois si vous essayez de le rendre facile pour la personne qui répond. :)
Autres conseils
Quant à votre question sur un "rappel généralisé", vous pouvez utiliser un boost :: fonction mais qui utilise essentiellement des fonctions virtuelles sous les couvertures (il ne peut pas - mais au moins un concept similaire) de sorte que la différence de performance que vous recherchez ne sera pas là (en augmentation de fait: :. la fonction sera probablement plus lente en raison de l'allocation de tas)