2017-08-09 4 views
0

J'écris une bibliothèque C++ avec toutes les classes utilisant le modèle d'usine , c'est-à-dire ayant un constructeur privé vide et une fonction privée void init() , qui fait l'initialisation réelle d'une instance. Les classes qui peuvent être instanciés par l'utilisateur bibliothèque ont une fonction publique qui create() statique effectue les opérations suivantes:Utilisation d'une macro pour remplacer les fonctions "create()" dans un style de modèle d'usine API

static Class* create(int arg1, int arg2) { 
    Class* ret = new Class(); 
    ret->init(arg1, arg2); 
    return ret; 
} 

Il y a aussi plusieurs classes qui ne doivent pas être instancié à partir de l'extérieur de la bibliothèque, donc ils ne le font pas fournir un public create().

Ces "classes internes" ont des relations friend entre elles, de sorte qu'elles peuvent s'instancier entre elles. Supposons que je veux écrire le moins de code possible, donc je voudrais éviter de déclarer et de définir create() privés fonctions pour toutes les classes internes, mais je voudrais aussi éviter d'écrire

InternalClass* foo = new InternalClass(); 
foo->init(); 

tout le temps un interne La classe est instanciée. Au lieu de cela, il serait bien d'avoir une seule fonction (modèle) ou une macro qui peut créer une classe interne avec une seule ligne de code, par ex.

template<class T, typename ...Args> inline T* create(Args... args) { 
    T* ret = new T(); 
    ret->init(args...); 
    return ret; 
} 

Le problème avec la fonction ci-dessus (qui serait défini en fonction autonome dans un en-tête global partagé, le mot-clé inline ainsi) est qu'il contourne les déclarations friend entre les classes et ne serait donc pas compiler. D'autre part, le problème avec une macro est qu'elle ne peut pas "renvoyer" un pointeur vers l'instance tant qu'il y a l'instruction ret->init() dans la macro, donc l'utilisation de la macro comme suit ne serait pas possible:

InternalClass* foo = CREATE(InternalClass, arg1, arg2); 

Enfin, ma question est:

est-il en quelque sorte possible de créer une macro qui peut être appelée de la manière ci-dessus (et sans changer le type de retour de toutes les init() fonctions à une classe type pointeur, qui permettrait en effet ce qui suit: #define CREATE(T, ...) (new T())->init(__VA_ARGS__))?

(S'il vous plaît noter que ceci est plus un académique qu'une question pratique comme je l'ai déjà mis en œuvre la solution avec les fonctions create() privées, qui fonctionne parfaitement bien et semble être la solution « propre ». I se demandait s'il y aurait pu une solution avec des macros, aussi ...)

+2

Qu'est-ce qui se passe avec init? Pourquoi ne pas l'initialiser dans le constructeur? En outre, le type de retour de votre méthode d'usine est en fait le même que votre classe, donc cette usine est inutile. – SergeyA

+0

Si vous voulez * vraiment * utiliser des macros (vous ne le faites certainement pas), vous aurez probablement besoin de quelque chose comme les [expressions d'expression] de GCC (https://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs. html): '#define CREATE (T, ...) ({auto * ret = nouveau T; ret-> init (__ VA_ARGS__); ret;})' – Justin

Répondre

1

Vous pouvez mélanger les deux modèles et macro:

template <typename T, typename InitF, typename... Args> 
T* CreateImpl(T* t, InitF init, Args&&...args) 
{ 
    (t->*init)(std::forward<Args>(args)...); 
    return t; 
} 

et le mACRO (f choses privées privées)

// I assume no overloads of T::init 
#define CREATE(T, ...) CreateImpl(new T(), &T::init, __VA_ARGS__)