2009-09-11 15 views
4

problème de base: Je veux être en mesure de prendre une instance d'une classe basé sur un modèle, disent:Affectation des identifiants numériques uniques aux instances d'une classe Templated

template<class a, class b, class c> class foo; 

foo<int, float, double>; 

et faire quelque chose comme:

foo<int, float, double>::value; //Evaluates to a unique number 
foo<long, float, double>::value; //Evaluates to a different unique number 
foo<int, float, double>::value; //Evaulates to the same unique number 

Sauf, vraiment, il est:

template<class a, class b, class c> 
int getUniqueIdentifier() 
{ 
    return foo<a, b, c>::value; 
} 

Solution actuelle tentative:Je pense que je veux utiliser "Extensible Associative Sequence" de Boost :: MPL, puisque chaque élément obtient son propre identifiant unique, mais je pense que je dois pouvoir modifier la séquence en place, qui "insert" doesn ' t faire.
Il est possible que j'aboie le mauvais arbre. (Du côté positif, dayum, mais MPL!)

Objet:
Réinventer la roue sur un système de signaux & Sockets. Les composants créent et enregistrent des canaux avec un "standard", qui utiliserait les identifiants uniques pour placer les canaux dans une carte, permettant une polyvalence d'exécution. J'ai essayé de regarder la bibliothèque Qt comme un exemple, mais je ne peux pas analyser leurs abréviations, et je pense que je manque un savoir-faire formel.

Merci!

+1

Le problème fondamental ici est que les modèles peuvent être instanciés dans différentes unités de traduction. Il est donc impossible d'attribuer des numéros uniques au moment de la compilation - deux TU pourraient être compilées en même temps sur des ordinateurs différents! – MSalters

+0

Oh, et le problème principal ne semble pas être le problème principal - il y a un but sous-jacent qui est en réalité mieux résolu par d'autres moyens. – MSalters

Répondre

4

Si vous voulez mettre les choses sur une carte, et avez besoin d'une solution appropriée consiste à utiliser la touche par type, std::type_info::before(). Il peut être utile de dériver une classe de sorte que vous pouvez fournir operator<, ou envelopper std::type_info::before() dans un prédicat binaire.

+0

Bon, compréhension vérification: Lorsque le compilateur compile une classe modèle, il construit un ensemble d'instructions (les différentes fonctions), un nom lisible par l'homme, et un nom lisible par une machine. Chacune de ces choses existe en mémoire, et possède donc une adresse mémoire, qui est par définition unique par "instance" du modèle, et identique pour toutes les "instances" identiques du modèle. Std :: type_info, quand vous en héritez, permet d'accéder à cette information?Std :: typeid (classe c) est alors le moyen facile d'obtenir un identifiant unique? – Narfanator

+0

Soyez prudent avec la revendication "par définition unique". Par exemple, les fonctions de gabarit peuvent être intégrées, et donc plusieurs copies de ces fonctions peuvent exister, intégrées dans d'autres fonctions. Ce n'est pas vraiment pertinent ici. Le point principal est que tous les types, y compris les instances de modèles de classe (mais pas les modèles eux-mêmes) ont type_info disponible, et ceux-ci ont un ordre fourni par le compilateur. Cet ordre est accessible via 'type_info :: before()'. Le point de dériver une classe de 'type_info' est simplement d'adapter son interface pour l'utiliser comme clé dans une' std :: map' – MSalters

0

Il y a une bonne chance de collisions, mais pour la simplicité, vous ne pouvez pas battre:

template<class a, class b, class c> 
static int getUniqueIdentifier() 
{ 
    return sizeof(a) + sizeof(b) + sizeof(c); 
} 
0

Si vous faites tout cela dans un seul fichier d'en-tête, vous pouvez utiliser la __LINE__ macro pour obtenir un nombre unique. Cependant, dès que cela est réparti sur plus d'un fichier, ils ne sont plus uniques. IIRC, VC a une macro qui évalue à un nombre qui est incrémenté chaque fois que la macro est utilisée. Donc, si vous êtes sur VC seulement, vous pouvez probablement l'utiliser. Cependant, cela aussi ne fonctionnera que dans une unité de traduction.

À part cela, je n'ai aucune idée de comment obtenir des nombres uniques de types.

Cependant, pourquoi n'utilisez-vous pas un typeid comme type de clé de la carte? Quelque chose comme ceci:

typedef std::map< std::type_info, whatever > my_map; 

template< typename a, typename b, typename c> 
void register_foo(my_map& the_map, const foo<a,b,c>& some_bar, whatever the_whatever) 
{ 
    the_map.insert(typeid(foo<a,b,c>), the_whatever); 
} 
0

Si vous avez un RTTI activé, vous pouvez concaténer les noms des classes, votre modèle est instancié avec, puis retourner un hachage pour cette chaîne. En choisissant une bonne fonction de hachage, vous réduisez au minimum le risque de collision (et peut-être vous n'avez pas besoin RTTI aussi bien, vous pouvez introduire quelques RTTI de votre propre):

// basic template for gaining names of the classes: 
template<typename T> 
class Rtti 
{ 
static std::string GetRTTIName() { return std::string(); } 
}; 

// specialization for the classes, you expect to use: 
template<> 
class Rtti<float> 
{ 
static std::string GetRTTIName() { return std::string("float"); } 
}; 
+0

Quel est le problème avec l'utilisation de 'std :: type_info'? – sbi

+0

std :: type_info est parfaitement ok. Mais (j'ai pensé) il pourrait être désactivé par les paramètres du projet ... – SadSido

+0

Il peut être désactivé sur certains compilateurs. Mais c'est plutôt stupide, en désactivant une fonctionnalité C++ standard que vous utilisez. – MSalters

0

Ou que diriez-vous:

template<class a, class b, class c, int uniqueId> 
class Test 
{ 
public: 
    int uid() { 
     return uniqueId; 
    } 
}; 

int main(int argc, char* argv[]) 
{ 
    Test<int, int, int, 5> test1; 

    std::cout << test1.uid() << std::endl; 

    return 0; 
} 

Si vous souhaitez que toutes les instances avec les mêmes paramètres de modèle possèdent le même ID que vous pouvez utiliser;

template<class a, class b, class c> 
class TestId5 : public Test<a, b, c, 5>{}; 
0

Voulez-vous par instance ou par instanciation?Pour la suite

template <...> 
class foo 
{ 
public: 
    static unsigned id; 
}; 

extern unsigned fooCounter; 

template <...> 
unsigned foo::id = ++fooCounter; 

ou même

template <...> 
unsigned foo::id = reinterpret_cast<unsigned>(&id); 
+0

Je suis assez certain que ce dernier serait indéfini - par l'ODR l'initialiseur constant devrait avoir la même valeur dans toutes les unités de traduction. – MSalters

+0

@ MSalters, l'initialiseur dans le dernier n'est plus une expression constante que dans le premier. Et je viens juste de relire 3.2 et surtout 3.2/5 et de ne rien voir qui empêche que le dernier soit bien défini. (Le reinterpret_cast échouera à compiler à l'implémentation où un non signé n'est pas assez grand pour contenir un pointeur, mais c'est un autre problème). – AProgrammer

+0

Semble que vous pourriez avoir raison - 3.2/5 mentions (avant il va dans les exceptions pour les noms d'objets const avec interne ou pas de liaison) la possibilité que le nom se réfère à la même entité partout. Maintenant, le problème est, cela invoque la logique cyclique: Si l'ODR s'applique, '& id' est le même nom partout, et l'ODR s'applique. Mais si l'ODR ne s'applique pas, '& id' n'est pas le même nom partout, et l'ODR ne s'applique pas. Hmmm ... DR? – MSalters

2

Je me suis trouvé justement avoir ce coin de hackery qui traîne dans mes libs (les uintxx sont mes typedefs avec des significations évidentes) - fonctionne sans rtti. 32/64 compat. les premiers gabarits sont pour définir un pointeur_uint qui contient un vide *

namespace pgast{ 

template <size_t size> 
struct _pointer_uint{ 
}; 

template <> 
struct _pointer_uint<2>{ 
    typedef uint16 intrinsic; 
}; 

template <> 
struct _pointer_uint<3>{ 
    typedef uint32 intrinsic; 
}; 

template <> 
struct _pointer_uint<4>{ 
    typedef uint32 intrinsic; 
}; 

template <> 
struct _pointer_uint<5>{ 
    typedef uint64 intrinsic; 
}; 

template <> 
struct _pointer_uint<6>{ 
    typedef uint64 intrinsic; 
}; 

template <> 
struct _pointer_uint<7>{ 
    typedef uint64 intrinsic; 
}; 

template <> 
struct _pointer_uint<8>{ 
    typedef uint64 intrinsic; 
}; 

typedef _pointer_uint< sizeof(void*) >::intrinsic pointer_uint; 

template <class c> 
struct Class_Identifier{ 
    static pointer_uint id(){ 
     static char _id; 
     return reinterpret_cast<pointer_uint>(&_id); 
    } 
    template <class c2> 
    bool operator==(const Class_Identifier<c2>& rhs)const{ 
     return id() == Class_Identifier<c2>::id(); 
    } 
    template <class c2> 
    bool operator<(const Class_Identifier<c2>& rhs)const{ 
     return id() < Class_Identifier<c2>::id(); 
    } 
}; 

}//namespace pgast 

/* 
Copyright (c)1993,2001 J. E. Pendergast Jr. 
*/ 
0

Revoir ce problème; il me semble que si je peux garantir que certaines fonctions dans l'instanciation du template sont uniques à cette instanciation, je peux simplement utiliser l'adresse de cette fonction comme ID.

Je pensais à cela quand j'ai réalisé que je n'avais pas besoin d'utiliser la chaîne de caractères de type_id, je pourrais simplement utiliser l'emplacement de la chaîne.

Ainsi, par exemple;

template<class a> 
foo 
{ 
    void id(){} 
    ... 
} 

aurait

&foo<int>::id != &foo<float>::id //? 

Si cela est vrai, alors je peux l'utiliser comme mon unique, par-spécialisation numéro d'identification de la carte, et ne pas compter sur RTTI.

Questions connexes