6
votes

Détecter une fonction en C ++ au moment de la compilation

Y a-t-il un moyen, probablement à l'aide de modèles, de macros ou d'une combinaison des deux, que je peux appliquer génériquement une fonction à différentes classes d'objets, mais faites-leur de répondre de différentes manières si elles n'ont pas de fonction spécifique?

Je souhaite spécifiquement appliquer une fonction qui émettra la taille de l'objet (c.-à-d. Le nombre d'objets dans une collection) si l'objet a cette fonction mais émettra un simple remplacement (tel que "N / A") si l'objet ne le fait pas. IE P>

NO_OF_ELEMENTS( mySTLMap ) -----> [ calls mySTLMap.size() to give ] ------>  10
NO_OF_ELEMENTS( myNoSizeObj ) --> [ applies compile time logic to give ] -> "N/A"


3 commentaires

Duplicaté possible: Stackoverflow.com/Questtions/257288/...


@AlessandroCezzato: Avec C ++ 11 frappant à la porte, il y a de nouvelles opportunités qui n'ont pas été explorées dans la réponse liée.


Merci pour toutes les réponses et conseils. Malheureusement, je ne peux pas utiliser C ++ 11 ou booster dans ma solution en raison des limitations des clients, qui est dommage. Néanmoins, avec les informations que vous avez toutes fournies, j'ai pu obtenir cela aller et l'étendre pour fournir une fonction de diffusion de la taille () ou de "" à l'aide d'une classe de modèle privé et intégré, partiellement Spécialisé pour la condition «vraie». Je suis maintenant très heureux !! Merci à tous!


6 Réponses :


3
votes

Vous pouvez faire quelque chose comme xxx

essentiellement, la mise en œuvre la plus générique est toujours renvoyée -1 ou "na" mais pour le vecteur et les cartes, il retournera la taille. Comme le plus général correspond toujours, il n'y a jamais une panne de temps de construction


0 commentaires

13
votes

De ce que je comprends, vous voulez avoir un test générique pour voir si une classe a une fonction de certaines membres. Cela peut être accompli en C ++ en utilisant Sfinae . En C ++ 11, il est assez simple, car vous pouvez utiliser déclinger : xxx

si vous utilisez c ++ 03, il est un peu plus difficile en raison du manque de déclinger , vous devez donc abuser Tailleof à la place: xxx

bien sûr cela utilise boost.enable_if < / code>, qui pourrait être une dépendance indésirable (et inutile). Toutefois, l'écriture activer_if vous est morte simple: xxx

dans les deux cas la signature de la méthode test (int) est uniquement visible, si u a une méthode de taille , car sinon évaluant soit le déclinger ou la taille de (selon sur quelle version que vous utilisez) échouera, qui supprimera ensuite la méthode de la considération (en raison de sfinae . Les longues expressions std :: déclval (). Taille (), Void (), std :: true_type () est un abus d'opérateur de virgule C ++, qui retournera la dernière expression de la liste séparée par des virgules. Cela garantit donc que le type est appelé std :: true_type pour la variante C ++ 11 (et la taille de évalue int pour la variante C ++ 03). Le void () Au milieu n'est là que là-bas pour s'assurer qu'il n'y a pas d'étranges surcharges de l'opérateur de la virgule interférant avec l'évaluation.

Bien sûr, cela retournera true si t h En tant que méthode Taille appelable sans arguments, mais ne donne aucune garantie sur la valeur de retour. Je suppose que tu veux probablement détecter que ces méthodes qui ne retournent pas nulle. Cela peut être facilement accompli avec une légère modification du test (int) méthode: xxx


3 commentaires

Intéressant ... mais quel est le void () pour?


@Matthieum.: Le void () n'est pas strictement nécessaire. Cependant, il est là de veiller à ce qu'aucune chose étrange ne se passe avec des opérateurs de virgules surchargés (peu probable dans ce cas, mais c'est la manière générale que j'utilise pour vérifier toutes les méthodes qui, donc je pensais que je le garderais).


Je pensais brièvement que cela pourrait être le cas, mais cela semble être plus semblable à ce que c'était un croft oublié lorsque vous avez ajouté true_type :) comme pour aller le déclenlette way, Ça fait Les choses si faciles pour les fonctions, qu'il est plus facile de définir des traits tels que consexpr ou simplement des traits de contournement et utilisez-le directement, voir ma réponse :)



4
votes

Ceci peut être fait en utilisant une technique appelée sfinae . Dans votre cas spécifique, vous pouvez mettre en œuvre que l'utilisation de Boost.Concept Check < / a>. Vous devriez écrire votre propre concept pour vérifier une taille -method. Sinon, vous pouvez utiliser un concept existant tel que < Code> conteneur , qui, entre autres, nécessite une taille -method.


2 commentaires

Je suis surpris que cette réponse n'a pas eu de top vote. Sfinae est la voie à suivre ici, imho.


@Azza: Même si je n'aime pas le "tu peux faire quelque chose comme" code> "style des réponses, la plupart des réponses présentent ici des pratiques Sfinae ...



1
votes

Vous pouvez essayer quelque chose comme:

#include <iostream>
#include <vector>



template<typename T>                                                                
struct has_size                                                                 
{                                                                                   
  typedef char one;                                                                 
  typedef struct { char a[2]; } two;                                                

  template<typename Sig>                                                            
  struct select                                                                     
  {                                                                                 
  };                                                                                

  template<typename U>                                                              
  static one check (U*, select<char (&)[((&U::size)!=0)]>* const = 0);     
  static two check (...);                                                           

  static bool const value = sizeof (one) == sizeof (check (static_cast<T*> (0)));   
};



struct A{ };
int main ( )
{
    std::cout << has_size<int>::value << "\n";
    std::cout << has_size<A>::value << "\n";
    std::cout << has_size<std::vector<int>>::value << "\n";
}


0 commentaires

2
votes

vous y allez. strong> remplacer std :: cout code> avec la sortie de votre sympathique.

template <typename T>
class has_size
{
    template <typename C> static char test( typeof(&C::size) ) ;
    template <typename C> static long test(...);

public:
    enum { value = sizeof(test<T>(0)) == sizeof(char) };
};

template<bool T>
struct outputter
{
    template< typename C >
    static void output( const C& object )
    {
        std::cout << object.size();
    }
};

template<>
struct outputter<false>
{
    template< typename C >
    static void output( const C& )
    {
        std::cout << "N/A";
    }
};


template<typename T>
void NO_OF_ELEMENTS( const T &object )
{
    outputter< has_size<T>::value >::output( object );
}


2 commentaires

Cela ne fonctionnera pas. objet.size () provoquera une défaillance. Vous devez déléguer l'impression à une structure Sfinae'd.


@Matthieuum. Oui! J'ai négligé ça. Merci. Corrigé maintenant.



10
votes

Il y avait une discussion sur les capacités de consexpr il y a plusieurs fois. Il est temps de l'utiliser, je pense :)

Il est facile de concevoir un trait avec consexpr et déclinger : xxx

si facile en fait que le trait perd la majeure partie de sa valeur: xxx

En action : xxx


5 commentaires

Je peux voir l'utilisation de void () dans la réponse de grizzly, mais ici? Semble superflu, car il n'y aura pas de deuxième paramètre à un opérateur de virgule "possible" de toute façon. Une gauche-over?


Je ne suis pas sûr que c'est la meilleure façon d'écrire le trait. Comparé à l'approche "commune" de la capituler dans une structure, vous n'avez évité que l'écriture de la structure autour des méthodes et la mise en place de la valeur de résultat dans une énumération ou un consexpr bool . D'autre part, has_size a une signature moins désierable de cette manière imo. Mêmes gores pour print_size .


@Xeo: the void () in print_size est utilisé pour lui donner une déclaration de retour de Void au lieu de déclnder (déclval () .Size ())


@Grizzly: Je vois ça différent. C'est certainement nouveau, alors je m'attends à une certaine résistance. J'ai donné quelques raisons pendant que je pense que les traits pourraient être déplacés vers des fonctions ici . Il y a quelques années, j'aurais adopté vos cours, mais j'essaie maintenant d'explorer C ++ 11 possibilités, en particulier pour réduire l'encombrement et obtenir de nouvelles fonctionnalités.


Notez que vous pouvez réduire encore plus l'encombrement en utilisant le type de retour de suivi: auto print_size (t const & t) -> déclinger (T.Size (), void ()) .