2010-02-14 2 views
4

Je me demande s'il existe un moyen de savoir si une classe est une base directe d'une autre classe, c'est-à-dire, en termes de trait de type Boost, une fonction is_direct_base_of. Pour autant que je puisse le voir, Boost ne semble pas supporter ce genre de fonctionnalité, ce qui m'amène à penser que c'est impossible avec le standard C++ actuel.Existe-t-il un moyen de savoir si une classe est une base directe d'une autre classe?

La raison pour laquelle je le souhaite est de faire une vérification de validation sur deux macros utilisées pour un système de réflexion afin de spécifier qu'une classe est dérivée d'une autre, comme dans l'exemple de code ci-dessous.

header.h:

#define BASE  A 
#define DERIVED B 

class A {}; 
class B : public A 
{ 
    #include <rtti.h> 
}; 

rtti.h:

// I want to check that the two macro's are correct with a compile time assert 
Rtti<BASE, DERIVED> m_rtti; 

Bien que les macros semblent inutiles dans cet exemple simple, dans mon vrai scénario monde rtti.h est beaucoup plus complexe. Une voie possible serait de comparer la taille de ce pointeur avec la taille d'un pointeur de ce pointeur au type de base et en essayant en quelque sorte de déterminer si c'est la taille de la classe de base elle-même ou quelque chose. (Ouais, vous avez raison, je ne sais pas comment ça marcherait non plus!)

Répondre

11

Je me suis demandé, "Qu'est-ce que les constructions C++ différencient entre l'héritage direct et l'indirect?" Cela vient à l'esprit que les constructeurs C++ de types dérivés appellent directement les constructeurs pour leur (s) base (s) directe (s). Alors code comme ceci:

Derived::Derived() : Base() {} 

est valable que si Base est est une base directe de Derived. Et puisque vous injectez le code rtti.h dans le corps de Derived, vous pouvez tolérer la restriction que cette technique n'est directement visible que dans la classe dérivée elle-même (c'est-à-dire qu'elle n'est pas aussi générale qu'un hypothétique type_traits::is_direct_base_of, mais n'a pas besoin d'être).

Donc, puisque nous ne voulons probablement pas déranger avec les constructeurs par défaut en soi, que diriez-vous d'en ajouter quelques-uns à des fins spéciales?

#define BASE  A 
#define DERIVED B 

struct rtti_tag {}; // empty type 

class A 
{ 
protected: 
    A(rtti_tag) { assert(false); } // never actually called 
}; 

#include <rtti.h> 
class B : public A 
{ 
    IS_DIRECT_BASE_OF(DERIVED, BASE); // fails to compile if not true 
}; 

rtti.h:

#define IS_DIRECT_BASE_OF(_B_, _A_) _B_(rtti_tag tag) : _A_(tag) \ 
    { assert(false); } // never actually called 

Ce code est compilé pour moi avec g ++ 4.2; si insertion d'une nouvelle classe dans la hiérarchie d'héritage, les pauses affirmation et la compilation échoue avec ce que je pense est un diagnostic raisonnablement descriptif:

In constructor ‘B::B(rtti_tag)’: 
error: type ‘A’ is not a direct base of ‘B’ 
... 
+1

'Derived :: dérivé(): Base() {}' est aussi valide si 'Base' est une classe de base virtuelle non-directe - ce cas pourrait-il être éliminé avec (encore un autre) test? +1 de toute façon. –

+0

Bon point, j_random_hacker, c'est assez intelligent (ou mal!). Je ne sais pas comment résoudre ce "problème", mais j'espère que c'est assez d'une chose pathologique qui n'a pas vraiment besoin d'être traitée (C++ étant un langage où les mal intentionnés peuvent faire n'importe quoi) . –

+0

Je suis d'accord, c'est un cas pathologique que vous n'avez pas besoin de perdre votre sommeil. J'y ai pensé parce que j'ai déjà vu une astuce pour empêcher une dérivation supplémentaire d'une classe (c'est-à-dire la forcer à être une "feuille") qui a également été subvertie par l'héritage virtuel. VI est vraiment un peu un hack de conception de langage - par exemple. Une classe de base virtuelle 'operator =()' sera appelée un nombre indéterminé de fois où un objet de la classe dérivée est assigné. –

Questions connexes