2009-11-25 5 views
15

Est-il possible de connaître le nom/le nom de la variable de l'objet à partir d'une méthode de classe? Par exemple:Un constructeur de classe C++ peut-il connaître son nom d'instance?

#include <iostream> 

using namespace std; 

class Foo { 
    public: 
      void Print(); 
}; 

void Foo::Print() { 
    // what should be ????????? below ? 
    // cout << "Instance name = " << ?????????; 
} 

int main() { 
    Foo a, b; 
    a.Print(); 
    b.Print(); 
    return 0; 
} 
+0

curieux de savoir si ce n'est que pour le débogage/la journalisation? Je l'espère –

+0

Oui, je me demandais simplement s'il était possible d'aider à affiner un problème que j'avais. –

+1

Ce que vous entendez par nom d'instance n'est pas clair. Considérons 'Foo bar [200]'. Quel nom d'instance devrait être pour 'bar [13]'? Ou même prosaïque, laissez 'new Foo()' retourné '0xbadbad' pointeur. Quel est le nom de l'instance de '* (0xbadbad)'? – ivaigult

Répondre

25

pas avec la langue elle-même, mais vous pouvez coder quelque chose comme:

#include <iostream> 
#include <string> 

class Foo 
{ 
public: 
    Foo(const std::string& name) { m_name = name;} 
    void Print() { std::cout << "Instance name = " << m_name << std::endl; } 

    private: 
    std::string m_name; 
}; 

int main() 
{ 
    Foo a("a"); 
    Foo b("b"); 

    a.Print(); 
    b.Print(); 

    return 0; 
} 
27

Les noms de variable sont pour le programmeur, le compilateur voit des adresses.

D'autres langues qui fournissent des méta-données/réflexion sur leur programme pourraient fournir cette fonctionnalité, C++ n'est pas un de ces langages.

+0

D'accord. La question en l'état a peu ou pas de sens. –

16

Les noms de variable n'existent pas dans le code compilé.

Toutefois, vous pouvez utiliser #define pour obtenir le nom en cours de pré-traitement et laisser les noms être remplis avant la compilation.

Quelque chose comme ceci:

#define SHOW(a) std::cout << #a << ": " << (a) << std::endl 
// ... 
int i = 2; 
SHOW (i); 
+1

'Les noms de variables n'existent pas dans le code compilé. Est-ce vrai même pour les binaires avec des symboles de débogage. – TheMeaningfulEngineer

8

Les noms de variables ne survivent pas à la compilation. Le mieux que vous puissiez faire est de passer le nom de la variable dans le constructeur de l'objet et de le stocker dans l'objet en utilisant une macro. Ce dernier conduira à un code vraiment laid donc vous ne voudriez que cela en dernier recours.

0

Ce n'est pas possible. C++ n'a pas de concept de "réflexion" comme la plate-forme .NET. Mais la bibliothèque MFC a la classe CRunTime - vous pouvez le voir par exemple.

+2

même avec une réflexion vous ne pouvez pas voir les noms de variable locale - juste propriétés, méthodes et champs –

9

Qu'est-ce que cela signifie?

void f(T const& p) { 
    cout << p.name(); 
} 

T r() { 
    T c; 
    return c; 
} 

void g() { 
    T a; 
    cout << a.name(); 
    T & b = a; 
    cout << b.name(); 
    T * ptr = &b; 
    cout << ptr->name(); 

    T d = r(); 
    cout << d.name(); 
} 

Qu'attendez-vous? "A" chaque fois? Et qu'en est-il de c/d?

0

Les symboles de débogage n'existent pas en ce qui concerne C++. Par conséquent, aucun mécanisme C++ ne vous permet de faire quoi que ce soit avec eux. Serait-il possible de créer une solution spécifique à une plate-forme qui fonctionnerait? Possible. Il faudrait analyser l'image du processus en mémoire, l'analyser selon un format spécifique, trouver des symboles (et des cadres) de débogage et les mapper aux adresses. Essentiellement, déboguez-vous. Cela vaut-il la peine? Pas à mon avis.

6

Pour la générosité: C'est l'un des plus grands et les plus hacks dégoûtant que j'ai jamais créé, mais son assez bon pour des raisons de débogage à mon avis

#include <iostream> 

#include <typeinfo> 
#define DEBUG_INSTANCE(classtype, name) class _ ## classtype ## _INSTANCE_ ## name ## _ : public classtype \ 
    { \ 
     public: \ 
      _ ## classtype ## _INSTANCE_ ## name ## _(){ } \ 
    }; \ 
_ ## classtype ## _INSTANCE_ ## name ## _ name 

class Foo { 
public: 
    virtual void _MakeTypeIDRunTime() { } 
    // A virtual method in the class forces typeid(*this) to be used runtime rather than compiled 
    // See: https://stackoverflow.com/a/6747130/1924602 

    void Print(); 
}; 

void Foo::Print() { 
    std::cout << "Instance name = " << typeid(*this).name() << std::endl; 
} 

int main() 
{ 
    DEBUG_INSTANCE(Foo, a); 
    DEBUG_INSTANCE(Foo, b); 

    a.Print(); 
    b.Print(); 

    system("PAUSE"); 
    return 0; 
} 

Sortie:

Instance name = [email protected][email protected] 
Instance name = [email protected][email protected] 
Press any key to continue . . . 

Cette macro va créer une classe qui hérite de Foo et le nom contient le nom de l'instance. La seule limitation est que Foo a un constructeur par défaut et doit contenir une méthode virtuelle pour que typeid accepte les classes héritées et soit appelé utilisé lors de l'exécution. Voir https://stackoverflow.com/a/6747130/1924602 pour une meilleure explication

est peut-être possible de soutenir les constructeurs si vous utilisez la __VA_ARGS__ macro

1

Il est impossible avec la langue elle-même. Cependant, vous pouvez utiliser le préprocesseur pour l'obtenir.Mais cela ne va pas être clair et vous devrez faire attention si vos classes ont des constructeurs différents. Aussi, vous devrez le faire dans chaque classe.

Je réutilise l'exemple de Keith Steven en combinaison avec #define préprocesseur:

#include <iostream> 
#include <string> 

using namespace std; 

class Foo 
{ 
public: 
    Foo(const string& name) : m_name(name) {} 
    void Print() { cout << "Instance name = " << m_name << endl; } 

private: 
    string m_name; 
}; 

#define DRESSED_Foo(var) Foo var(#var) 

int main() 
{ 
    DRESSED_Foo(a); 
    DRESSED_Foo(b); 

    a.Print(); 
    b.Print(); 

    return 0; 
} 
5

Il est certainement possible pour une instance de connaître son nom à partir de la méthode de classe:

#include <iostream> 

class Foo { 
    public: 
    void Print() { std::cout << "Instance name = " << this << std::endl; } 
}; 

int main() { 
    Foo a, b; 
    a.Print(); 
    b.Print(); 
    return 0; 
} 

produira la sortie semblable à ceci:

Instance name = 0x7fff502b8b48 
Instance name = 0x7fff502b8b40 

comme fo En connaissant le nom de la variable, ce n'est certainement pas possible. L'existence de l'objet ne signifie pas l'existence de la variable - cette instance:

new Foo(); 

existera pour la durée restante du processus, mais ne sera jamais associée à une variable. Le concept de langage des variables n'est pas reflété dans le contenu desdites variables, et toute relation potentielle entre la variable de langage et un objet est exprimée uniquement dans le code généré et non dans les données générées ou les métadonnées. Sauf évidemment l'accès à l'information de débogage qui, comme déjà souligné, ne fait pas partie du langage.

+0

Je ne suis pas d'accord, vous pouvez appeler 'this' un nom (étant donné que c'est l'adresse de l'objet), mais peut passer comme un identificateur uniqe . Vous donnera la prime si vous ajoutez un exemple comment obtenir les noms de variables à partir d'adresses de mémoire à partir d'un binaire construit de débogage. Pas au moment de l'exécution, mais en tant qu'étape séparée (en respectant le binaire ou quelque chose). S'il vous plaît, laissez l'exemple inclure la commande de compilation avec tous les drapeaux requis. – TheMeaningfulEngineer

+0

Eh bien, c'est une question de définition. En ce qui concerne le reste de votre commentaire, a et b sont des variables automatiques, donc elles n'existeraient pas dans les tables de symboles dans les fichiers binaires. Le seul moment pour faire correspondre l'adresse ('this' pointeur) avec la variable serait l'examen à l'exécution du cadre de la pile précédente et correspondant à son contenu à l'information de débogage. Fondamentalement, ce débogueur fait, juste à l'intérieur du processus. Ce n'est pas impossible, mais il faudrait un morceau de code hautement spécialisé et non utilisable qui aurait peu, voire pas, d'utilisation réelle. – Leon

1

Le mot-clé this

Je suis nouveau à la programmation, mais ayant une structure de classe étudiée, je crois que ce que vous cherchez peut-être est le mot-clé cette. Comme dans l'exemple ci-dessous (tiré de cplusplus.com), vous pouvez voir que ce est utilisé partout où la classe doit se référer.

Par conséquent, il est également possible pour un constructeur de le faire.

// example on this 
#include <iostream> 
using namespace std; 

class Dummy { 
    public: 
    bool isitme (Dummy& param); 
}; 

bool Dummy::isitme (Dummy& param) 
{ 
    if (&param == this) return true; 
    else return false; 
} 

int main() { 
    Dummy a; 
    Dummy* b = &a; 
    if (b->isitme(a)) 
    cout << "yes, &a is b\n"; 
    return 0; 
} 

http://www.cplusplus.com/doc/tutorial/templates/

1
This is not possible "Directly", consider this simple program; 

// stove.cpp 

#include <string.h> 
#include <stdio.h> 
#include <iostream> 

using namespace std; 

class Foo { 
    char* t; 
    size_t length; 

    public: 
    Foo() 
    { 
     t = new char(8); 
     t = static_cast<char*>(static_cast<void*>(this)); 

    } 
     void Print(); 
}; 

    void Foo::Print() { 
     // what should be ????????? below ? 
      cout << this << " " << strlen (t) << t << endl; 
    } 

    int main() { 
     Foo a; 
     a.Print(); 
     Foo b; 
     b.Print(); 
     return 0; 
    } 

you can check the value of t in gdb for both a and b objects, 
1) run binary in gdb and put breakpoints on lines where both objects a and b gets created. 
2) for both objects a and b , after creation (or in print function, check 
print this 
print t 
    18   } 
    (gdb) print this 
    $1 = (Foo * const) 0x7fffffffe6e0 
    (gdb) print t 
    $2 = 0x7fffffffe6e0 "\340\346\377\377\377\177" 
    ... 
    ... 
    ... 
    30   Foo b; 
    (gdb) s 
    Foo::Foo (this=0x7fffffffe6d0) at stov.cpp:15 
    15    t = new char(8); 
    (gdb) n 
    16    t = static_cast<char*>(static_cast<void*>(this)); 
    (gdb) n 
    18   } 
    (gdb) print this 
    $3 = (Foo * const) 0x7fffffffe6d0 
    (gdb) print t 
    $4 = 0x7fffffffe6d0 "\320\346\377\377\377\177" 
+0

Pouvez-vous s'il vous plaît formater votre réponse un peu, cela pourrait être juste ce que je cherchais. – TheMeaningfulEngineer

+0

juste formulé, fondamentalement je pense que vous êtes confus par la fonction d'impression et commande d'impression que j'ai utilisé dans gdb, donc juste déboguer et utiliser "p ceci" et "pt" .. – Naumann

+0

Je visait à formater la réponse visuellement et ne pas avoir inutile commentaires dans le code. Après avoir revérifié, je me suis trompé sur le fait que c'était la solution que je recherchais. À aucun moment, dans gdb, le nom de la variable n'est réellement imprimé, et l'impression 'this' pour identifier la variable peut se faire sans symboles de débogage. – TheMeaningfulEngineer

0

Vous pouvez le faire en utilisant #define. Définir une macro qui enregistrerait le nom de la variable dans la classe:

#include <iostream> 
#include <string> 

using namespace std; 

#define CREATE_FOO(f) Foo f = Foo(#f); 

class Foo { 
    public: 
      void Print() const; 
      Foo(string s): name(s) {}; 
    protected: 
      string name; 

}; 

void Foo::Print() const { 
    cout << "Instance name = " << name; 
} 


int main() { 
    CREATE_FOO(a); 
    CREATE_FOO(b); 
    a.Print(); 
    b.Print(); 
    return 0; 
} 
Questions connexes