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.

Était-ce utile?

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)

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