Developpez.com

Club des développeurs et IT pro
Plus de 4 millions de visiteurs uniques par mois

CAMP, nouvelle bibliothèque libre de réflexion pour C++.
La réflexion est-elle un besoin dans vos développements ?

Le , par Laurent Gomila, Rédacteur
Bonjour à tous

Tegesoft vient de publier la première version publique de CAMP, une bibliothèque libre permettant d'étendre les classes C++ pour leur donner des capacités d'introspection.

CAMP permet de binder classes, propriétés, fonctions et objets de manière complètement non-intrusive, afin de pouvoir les manipuler de manière homogène en runtime. Cela permet par exemple d'utiliser ses propres classes dans des scripts (Python, Lua, ...), de les sérialiser automatiquement via des formats textuels ou binaires, de les envoyer sur le réseau, de construire des éditeurs de propriétés, etc.

Le système est fortement inspiré de boost.python ou encore de luabind, sauf qu'il est plus abstrait et peut ensuite être exploité pour n'importe quelle utilisation, pas seulement dans un cadre particulier (un langage de script pour les exemples cités).

CAMP est distribué sous licence LGPL v3.

Un petit exemple d'utilisation sera plus parlant qu'un long discours :
Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
 #include <camp/camptype.hpp> 
 #include <camp/class.hpp> 
 #include <string> 
 #include <iostream> 
  
 // Let's define a class for handling persons 
 class Person 
 { 
 public: 
  
     // Construct a person from its name 
     Person(const std::string& name) : m_name(name), m_age(0) 
     { 
     } 
  
     // Retrieve the name of a person 
     std::string name() const 
     { 
         return m_name; 
     } 
  
     // Retrieve the age of a person 
     unsigned int age() const 
     { 
         return m_age; 
     } 
  
     // Set the age of a person 
     void setAge(unsigned int age) 
    { 
         m_age = age; 
     } 
  
     // Make a person speak (tell about its name and age) 
     void speak() 
     { 
         std::cout << "Hi! My name is " << m_name << " and I'm " << m_age << " years old." << std::endl; 
     } 
  
 private: 
  
     std::string m_name; 
     unsigned int m_age; 
 }; 
  
 // Make the Person type available to CAMP 
 CAMP_TYPE(Person); 
  
  
 int main() 
 { 
     // Bind our Person class to CAMP 
     camp::Class::declare<Person>("Person") 
         .constructor1<std::string>() 
         .property("name", &Person::name) 
         .property("age", &Person::age, &Person::setAge) 
         .function("speak", &Person::speak); 
  
     // Retrieve it by its name 
     const camp::Class& metaclass = camp::classByName("Person"); 
  
     // Construct a new person named John 
     Person* john = metaclass.construct<Person>(camp::Args("John")); 
  
     // Print its name 
     std::string name = metaclass.property("name").get(john); 
     std::cout << "John's name is: " << name << std::endl; 
  
     // Set its age to 24 
     metaclass.property("age").set(john, 24); 
  
     // Make John say something 
     metaclass.function("speak").call(john); 
  
     // Kill John 
     metaclass.destroy(john); 
  
     return 0; 
 }


Vous avez aimé cette actualité ? Alors partagez-la avec vos amis en cliquant sur les boutons ci-dessous :


 Poster une réponse

Avatar de Laurent Gomila Laurent Gomila - Rédacteur https://www.developpez.com
le 29/06/2010 à 8:39
Je pense tout simplement qu'elle n'est pas prête pour ça, mais j'y penserai dans quelques mois/années

Je connaissais mirror, qui a une approche assez différente de CAMP, mais pas intro. Je vais jeter un coup d'oeil à ça

J'avais aussi entendu parler d'un projet boost pour abstraire boost.python et en faire un moteur de binding script générique, mais apparemment le projet a été abandonné faute de temps. Me rappelle plus le nom.

EDIT : j'ai retrouvé, c'est boost.langbinding
Avatar de epsilon68 epsilon68 - Membre éprouvé https://www.developpez.com
le 29/06/2010 à 12:40
en tous les cas j'aime vraiment bien ta lib,
si je compare le code (utilisateur) avec mirror,
c'est beaucoup plus clean avec la tienne.

en plus on peut controler quel attribut (ou autre) exactement nous voulons inclure ou non.

je pense que je vais (dois) m'y mettre tres prochainement.

cependant, peux-tu juste me dire si on peut faire 2 meta classes pour une meme classe? mon utilisation serait que parfois je dois faire une metaclasse pour un certain type de serialisation, puis j'en ai besoin d'une autre pour un autre contexte.

encore felicitations, le code est vraiment clean.
Avatar de Laurent Gomila Laurent Gomila - Rédacteur https://www.developpez.com
le 29/06/2010 à 18:04
en tous les cas j'aime vraiment bien ta lib,
si je compare le code (utilisateur) avec mirror,
c'est beaucoup plus clean avec la tienne.

Disons que ce n'est pas la même approche, voire le même but. boost.mirror fournit un vrai mapping 1:1 entre code C++ et informations réflechies. Les macros sont plus verbeuses, mais du coup tout est "pré-câblé" directement, les infos sont même exploitables par le compilo pour écrire des meta-programmes.

cependant, peux-tu juste me dire si on peut faire 2 meta classes pour une meme classe? mon utilisation serait que parfois je dois faire une metaclasse pour un certain type de serialisation, puis j'en ai besoin d'une autre pour un autre contexte.

Nop (du moins pas en même temps). Ca créerait trop d'inconsistences en interne, car CAMP doit pouvoir déterminer de manière unique et automatique quelle meta-classe est associée à quel type C++.
Avatar de epsilon68 epsilon68 - Membre éprouvé https://www.developpez.com
le 15/07/2010 à 10:50
J'ai vu qu'un module json existait...
mais je pense que le json devrait etre écrit sans utiliser de structure intermediaire... je pense que je vais l'implementer tres prochainement!

CAMP est vraiment cool :-)
Avatar de epsilon68 epsilon68 - Membre éprouvé https://www.developpez.com
le 19/07/2010 à 9:53
Hello,

j'ai compilé camp pour macosx, et je tourne autour avant de m'attaquer à mon probleme. J'utilise aussi Qt et la je regarde pour QString.
J'aurais bien une question, dans le source code suivant:

Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
class MyClass 
{ 
public: 
 
    int func(QString s, int i) { return i*2; } 
    QString prop; 
}; 
 
CAMP_TYPE(MyClass) 
 
int main() 
{ 
    camp::Class::declare<MyClass>("MyClass") 
        .function("func", &MyClass::func) 
        .property("prop", &MyClass::prop) 
        ; 
         
    MyClass o; 
    o.prop = "coucou"; 
 
    std::cout << "prop: " << o.prop.toLatin1().constData() << std::endl; 
 
    camp::UserObject object = o; 
    object.set("prop", "hello"); 
    std::string s = object.get("prop"); 
 
    std::cout << "prop (camp): " << o.prop.toLatin1().constData() << std::endl; 
 
    int i = object.call("func", camp::Args("hello", 20)); 
 
    std::cout << "func (camp): " << i << std::endl; 
 
    return 0; 
}
quand on fait:
Code : Sélectionner tout
std::string s = object.get("prop");
pareil pour:
Code : Sélectionner tout
object.set("prop", "hello");
comment se fait la conversion entre unicode et std::string?

EDIT: trouvé: par fromStdString et toStdString
mais alors me suis-je dis... que faire avec unicode?
bein en fait assigner et recuperer des QString marche aussi
c'est génial!
Avatar de epsilon68 epsilon68 - Membre éprouvé https://www.developpez.com
le 19/07/2010 à 13:45
Citation Envoyé par Laurent Gomila  Voir le message
Nop (du moins pas en même temps). Ca créerait trop d'inconsistences en interne, car CAMP doit pouvoir déterminer de manière unique et automatique quelle meta-classe est associée à quel type C++.

en fait, on peut se servir des tags sur les propriétés donc c'est bon :-)
Avatar de vikki vikki - Membre averti https://www.developpez.com
le 30/08/2010 à 18:08
Bonjour à tous,
Puisque ce post est dédié à CAMP, je permet d'écrire ici plutôt que de créer une nouvelle discussion.
Je me demandais s'il était possible d'enregistrer une classe auprès de CAMP de manière dynamique, donc sans passer par la macro CAMP_TYPE et la spécialisation de classe sous-jacente. J'ai réussi à la faire en définissant une méthode statique CAMP_TYPE_NOT_REGISTERED() dans ma classe à enregistrer de cette manière (l'exemple est un peu bidon mais fonctionne):

Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 
struct cstring : public std::string 
{ 
	std::string str; 
	static const char* CAMP_TYPE_NOT_REGISTERED(){return "cstring";} 
}; 
 
void register_cstring() 
{ 
        camp::Class::declare<cstring>("CTestString") 
       .constructor0(); 
} 
 
//dans main(): 
const camp::Class& metaclass = camp::classByName("CTestString"); 
std::string* str= metaclass.construct<std::string>();
Ça fonctionne avec la version 0.6.0 (pas testé avec la 0.7.1), mais ca ressemble plus à un hack qu'autre chose...
Avatar de Laurent Gomila Laurent Gomila - Rédacteur https://www.developpez.com
le 30/08/2010 à 21:04
Ca compile, mais ça ne fonctionnera pas. Là tout ce que tu fais c'est éviter le système mis en place pour avoir un message d'erreur compréhensible lorsque la classe n'est pas déclarée avec CAMP_TYPE. Mais ce n'est pas juste pour faire beau qu'on a mis en place ce système

Je ne comprends pas pourquoi tu veux éviter cette macro, elle ne déclare rien, elle associe juste un identificateur à la classe pendant la compilation. La déclaration de la meta-classe intervient avec camp::Class::declare, et ça c'est bien dynamique.
Avatar de vikki vikki - Membre averti https://www.developpez.com
le 31/08/2010 à 9:54
Hello,

J'ai fait un petit programme test qui me semble fonctionner correctement.
Code : Sélectionner tout
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
 
//// main.cpp //// 
 
#include <camp/camptype.hpp> 
#include <camp/class.hpp> 
#include <iostream> 
 
struct A 
{ 
	virtual void print() {std::cout<<"A"<<std::endl;} 
}; 
 
struct B : public A 
{ 
	virtual void print() {std::cout<<"B"<<std::endl;} 
	static const char* CAMP_TYPE_NOT_REGISTERED(){return "B";} 
}; 
 
CAMP_TYPE(A); 
 
int main() 
{ 
	camp::Class::declare<A>("A") 
		.constructor0(); 
 
	camp::Class::declare<B>("B") 
		.base<A>() 
		.constructor0(); 
 
 
     const camp::Class& metaclass = camp::classByName("B"); 
     A* a = metaclass.construct<A>(); 
	 a->print(); 
 
	 std::system("pause"); 
}
Bon, après je ne suis pas sûr à 100% de l'utilité. Mon but est de créer un système de plugins (probablement basé sur l'API de Qt), chaque plugin contenant une classe fille de A et une fonction permettant le bind de cette classe vers camp (je m'en sers pour la génération automatique d'éléments d'IHM). Du coup je ne sais pas trop ce qui ce passe en utilisant CAMP_TYPE à l'intérieur de mon plugin, l'appli principale va-t-elle vraiment reconnaitre le nouveau type enregistré (vu que tout semble se faire à la compilation), ou alors faudra-t-il que j'exporte également la spécialisation de classe implémentée par CAMP_TYPE? ou alors j'ai rien compris ?
Avatar de Laurent Gomila Laurent Gomila - Rédacteur https://www.developpez.com
le 31/08/2010 à 11:44
En fait, étant donné la façon dont CAMP_TYPE_NOT_REGISTERED est utilisé pour générer une erreur, la fonction que tu définis joue effectivement le même rôle qu'un CAMP_TYPE. Mais du coup autant utiliser CAMP_TYPE...

Ensuite effectivement CAMP_TYPE n'est pas toujours nécessaire, en particulier dans ton cas. Tout comme en C++ tu n'aurais pas besoin d'inclure l'en-tête de la classe B pour l'utiliser via un A*, avec CAMP tu n'as pas besoin de CAMP_TYPE(B). En fait, CAMP_TYPE n'est requis que pour les opérations statiques (à la compilation) impliquant le type en question. C'est pour cela qu'on le met en général juste après la définition de la classe : si tu peux te passer de celle-ci, tu peux aussi te passer du CAMP_TYPE correspondant (et inversement).
Tu aurais eu une erreur de compilation si par exemple tu avais écrit camp::classByType<B>().

Donc pour résumer, CAMP_TYPE est bien nécessaire mais uniquement pour déclarer la metaclasse, donc dans le plugin. Dans ton programme principal tu n'en auras pas besoin.
Avatar de vikki vikki - Membre averti https://www.developpez.com
le 31/08/2010 à 14:45
Ensuite effectivement CAMP_TYPE n'est pas toujours nécessaire, en particulier dans ton cas. Tout comme en C++ tu n'aurais pas besoin d'inclure l'en-tête de la classe B pour l'utiliser via un A*, avec CAMP tu n'as pas besoin de CAMP_TYPE(B). En fait, CAMP_TYPE n'est requis que pour les opérations statiques (à la compilation) impliquant le type en question. C'est pour cela qu'on le met en général juste après la définition de la classe : si tu peux te passer de celle-ci, tu peux aussi te passer du CAMP_TYPE correspondant (et inversement).
Tu aurais eu une erreur de compilation si par exemple tu avais écrit camp::classByType<B>().

Ah oui, je vois mieux comment ca fonctionne et j'étais bien à coté de mes pompes J'ai fait un petit test de plugin avec boost.python et camp et ca marche nickel, merci beaucoup !
Offres d'emploi IT
Ingénieur analyste programmeur (H/F)
Safran - Auvergne - Montluçon (03100)
Ingénieur H/F
Safran - Ile de France - Moissy-Cramayel (77550)
Ingénieur conception en électronique de puissance H/F
Safran - Ile de France - Moissy-Cramayel (77550)

Voir plus d'offres Voir la carte des offres IT
Contacter le responsable de la rubrique C++