Langages de script intégrables dans un logiciel en C++

L'auteur de ce sujet a trouvé une solution à son problème.
Auteur du sujet

Bonjour,

Ça fait longtemps que je m'amuse déjà avec lua, mais j'aimerais bien savoir s'il existe d'autres langages de script comparables. Je parle bien de langages de script intégrables dans un programe en C++ et donc avec une API C/C++, et non pas de langage forcément autonome.

Il y a principalement deux choses qui commencent à me gêner avec lua en fait :

  • La non-gestion d'unicode. Mon application est sous windows et toutes les chaînes sont des LPWSTR / std::wstring. C'est un peu dommage de systématiquement faire des conversions de et vers UTF-8, ou pire, ANSI, quand on passe de lua à C++ et vice versa. Si la gestion d'UTF-8 était transparente dans lua ça pourrait encore aller mais ce n'est pas le cas (typiquement l'opérateur # qui ne fonctionne pas comme prévu puisqu'il mesure la taille en octets et non la longueur effective) ET comme mon logiciel est un bloc-notes amélioré et pas un jeu, on manipule surtout des chaînes plus que des chiffres en fait
  • Le multithreading. Ça c'est le point assez bloquant en lua, si on veut faire du vrai multithreading alors il faut créer un contexte (lua_State*) différent par thread et réenregistrer toutes les API fournies par le logiciel à chaque fois; ou alors on fait du faux multithreading et on la joue aux locks. et le partage de données entre contextes peut devenir rapidement problématique (va passer une table tout en conservant les métaméthodes associées)

Donc, je me demandais s'il existait pas des langages de script similaires, qui comblent ces défauts et qui :

  • Restent assez légers (pas un machin qui prend direct des dizaines de Mo)
  • Conservent les concepts clés de closure et de fonctions d'ordre supérieur, parce que c'est bien pratique pour appeler des callback quand certains évènements se produisent
  • Pas non plus trop compliqué à mettre en place
  • Et qui peut se compiler avec MinGW

D'abord je suis tombé sur [AngelScript] (http://www.angelcode.com/). Ça a l'air assez sympa, mais sans fonctions d'ordre supérieur, je me suis demandé comment faire pour avoir un système d'évènements avec callback, du genre window.onClose = myScriptFunction. Ça a pas l'air pratique pour faire quelque chose de flexible dans le genre ou alors j'ai raté quelque chose.

Après je me suis demandé s'il y avait moyen d'embarquer du JavaScript, mais j'ai vite laissé tomber. Ça a l'air ultra compliqué… ou alors je suis un noob. V8 semble super bien foutu ceci dit. Le gros avantage du js c'est quand même que tout le monde un temps soit peu intéressé le connaît au moins un minimum étant donné la place qu'il a aujourd'hui dans nos navigateurs web.

Je ne suis pas trop fan de python, et ça a l'air d'être quand même assez lourd (la distribution 2.7 autonome que j'ai pèse quand même 60 Mo)

Connaissez-vous quelque chose d'autre ou avez-vous des idées ?

Merci pour vos réponses !

Ma plateforme avec 23 jeux de société classiques en 6 langues et 13000 joueurs: http://qcsalon.net/ | Apprenez à faire des sites web accessibles http://www.openweb.eu.org/

+0 -0

Si la programmation fonctionnelle ne te fait pas peur, tu peut tenter d'intégrer une variante de Lisp dans ton programme. Je crois qu'il y en a des très légères, elles ont toutes des callback, et ça reste assez léger. Il y a aussi des implémentations en C, donc pas de problème pour mingw.

Mon Github — Tuto Homebrew — Article Julia

+0 -0
Auteur du sujet

Si la programmation fonctionnelle ne te fait pas peur, tu peut tenter d'intégrer une variante de Lisp dans ton programme.

Si ça ne tenait qu'à moi pourquoi pas, mais là par contre c'est surtout les futurs utilisateurs de mon programme qui vont me haïr. Déjà qu'on m'accuse de faire des logiciels que je suis le seul à comprendre…

Lua tout comme JavaScript et python ont l'énorme avantage d'être des langages plutôt simples pour le scripteur, outre leur notoriété qui n'est plus à faire.

Quelqu'un a déjà essayé avec ruby ?

Ma plateforme avec 23 jeux de société classiques en 6 langues et 13000 joueurs: http://qcsalon.net/ | Apprenez à faire des sites web accessibles http://www.openweb.eu.org/

+0 -0

Je dirais que l'interpreteur ruby doit faire la même taille que CPython, non ?

Sinon, avec une version de Python non-standard : PyPy ou micro-python (qui est justement fait pour de l'embarqué). Et PyPy n'a pas le GIL je crois, donc la parallelisation est plus simple.

Mon Github — Tuto Homebrew — Article Julia

+0 -0
Staff

Et PyPy n'a pas le GIL je crois, donc la parallelisation est plus simple.

Si. La version de base de PyPy a le GIL. Il y a une version parallèle, PyPy STM, qui le vire mais pour l'instant rien n'est dispo en version stable. En particulier la compatibilité avec les libs natives posent problème mais ça avance. Cependant je ne suis pas surs que PyPy soit beaucoup plus leger que CPython. Dans tous les cas, ce qu'il veut c'est du multi-thread : ça marche tres bien en Python, c'est juste limité à un seul coeur (et le multi-process n'est pas bien compliqué non plus).

A titre personnel, dans un tel cas, je mettrais Python, ou lua si je voulais un truc vraiment léger.

+0 -0
Auteur du sujet

L'mplémentations Jim de de Tcl/TK a les critères que tu recherches et y'a des libs pour faire une intégration en C++ Tcl/TK en général.

Davidbrcz

Tiens, voilà un langage dont je n'ai absolument jamais entendu parler et qui a l'air à priori plutôt atypique. Je vais y jeter un oeil, merci!

Ma plateforme avec 23 jeux de société classiques en 6 langues et 13000 joueurs: http://qcsalon.net/ | Apprenez à faire des sites web accessibles http://www.openweb.eu.org/

+0 -0
Auteur du sujet

Bon, ben finalement je vais quand même prendre python. J'avais pas tilté mais python 3 fonctionne en unicode en interne. La version que j'ai téléchargée pour windows, python34.dll, fait 1 Mo une fois upxée; ça va encore, je pensais que c'était beaucoup plus gros que ça.

J'ai jeté un coup d'oeil à TCL mais ça ne m'a pas convaincu, sur la forme un peu atypique de la syntaxe surtout en fait.

Merci pour vos réponses.

Ma plateforme avec 23 jeux de société classiques en 6 langues et 13000 joueurs: http://qcsalon.net/ | Apprenez à faire des sites web accessibles http://www.openweb.eu.org/

+0 -0
Auteur du sujet

J'ai juste un problème à la con, je ne peux pas utiliser PyRun_SimpleFile et toutes les autres fonctions de la C/API qui prennent des FILE* en paramètre. Le moindre appel fait crasher mon programme. Par contre PyRun_SimpleString fonctionne nickel et on peut import sans problème (et donc indirectement exécuter un fichier par ce biais)

La doc indique qu'on peut avoir effectivement des problèmes notamment sous windows parce qu'il y a plusieurs versions différentes et incompatibles de FILE*, parce qu'il y a plusieurs versions de fopen définies dans plusieurs DLL (Plusieurs version de la MSVCRT probablement).

Comment je fais pour savoir quelle version utiliser pour que ça marche ? Comment forcer GCC 4.x à linker la bonne version ?

Je n'ai pas envie d'utiliser Visual Studio, ni m'amuser à essayer de recompiler python moi-même. J'ai pris le python34.dll et libpython34.a qu'on trouve dans la distribution par défaut qu'on installe avec le .msi; le reste fonctionne très bien.

Merci.

Ma plateforme avec 23 jeux de société classiques en 6 langues et 13000 joueurs: http://qcsalon.net/ | Apprenez à faire des sites web accessibles http://www.openweb.eu.org/

+0 -0
Staff

Je pense également que Python3 est un bon choix. Cela dit il faut bien prendre en compte que tu n'auras qu'un seul thread (au sens système) de disponible pour faire tourner la VM, quitte à scinder l'exécution de Python en plusieurs pseudo-threads.

En tout cas, concernant la gestion d'Unicode, tu trouveras difficilement mieux dans cette famille de langages (mention spéciale à Perl dans lequel il est tout bonnement impossible de comprendre ce qu'on fait avec l'encodage des chaînes de caractères).

PS : recompiler Python 3.4, ça m'est arrivé au boulot pas plus tard qu'il y a 2 semaines sur une Red Hat antédiluvienne, ça prend 10 minutes montre en main. Faut juste pas oublier d'installer la libssl (c'est la seule dépendance un peu chiante).

Édité par nohar

I was a llama before it was cool

+0 -0
Auteur du sujet

Cela dit il faut bien prendre en compte que tu n'auras qu'un seul thread (au sens système) de disponible pour faire tourner la VM, quitte à scinder l'exécution de Python en plusieurs pseudo-threads.

J'y ai pensé, ça ne change rien par rapport à lua avec lequel je m'y prenais mal à l'époque; je vais probablement lancer python ailleurs que sur le thread principal pour éviter des blocages. Il faut juste que je retrouve un moyen efficace de remonter les évènements de l'UI-thread… pour le moment je pense à une seconde boucle de messages et du polling en appelant une fonction C++ de mon API, mais le problème c'est que pour le scripteur c'est pas simple, et c'est pas sûr que ce soit très efficace.

Quoi qu'il en soit si je le laisse dans le thread principal alors je m'expose à des problèmes, genre si l'utilisateur démarre un pseudo-thread alors j'ai des risques de ne jamais reprendre la main je pense.

PS : recompiler Python 3.4, ça m'est arrivé au boulot pas plus tard qu'il y a 2 semaines sur une Red Hat antédiluvienne, ça prend 10 minutes montre en main. Faut juste pas oublier d'installer la libssl (c'est la seule dépendance un peu chiante).

Ouais, mais je suis sous windows. ET sous windows, autoconf c'est chiant, cMake c'est chiant, en fait les dépendances c'est toujours le bordel. Donc non, j'oublie. Déjà que boost met 2h alors laisse tomber.

J'ai déjà une partie de la réponse à ma question ci-dessus, python34.dll est linké à msvcr100.dll. Il faut juste que je trouve comment forcer GCC 4.x à se linker aussi à msvcr100.dll au lieu de msvcrt.dll; c'est un link implicite mais ça doit bien pouvoir se changer non?

Ma plateforme avec 23 jeux de société classiques en 6 langues et 13000 joueurs: http://qcsalon.net/ | Apprenez à faire des sites web accessibles http://www.openweb.eu.org/

+0 -0
Staff

Quoi qu'il en soit si je le laisse dans le thread principal alors je m'expose à des problèmes, genre si l'utilisateur démarre un pseudo-thread alors j'ai des risques de ne jamais reprendre la main je pense.

Il suffit de récupérer le GIL pour t'assurer que le code python de l'utilisateur s'arrête et te laisse travailler. C'est pas bien méchant.

I was a llama before it was cool

+0 -0
Staff

A noter que Python fait facilement du multi-thread, simplement tout est bloqué sur le même coeur. Mais en général ça suffit largement pour éviter qu'un process bloque l'execution du thread de la boucle principale.

+0 -0

J'ai déjà une partie de la réponse à ma question ci-dessus, python34.dll est linké à msvcr100.dll. Il faut juste que je trouve comment forcer GCC 4.x à se linker aussi à msvcr100.dll au lieu de msvcrt.dll; c'est un link implicite mais ça doit bien pouvoir se changer non?

QuentinC

le workaround qui va bien est PyFile_AsFile

1
PyRun_AnyFile(PyFile_AsFile(pfo), fname)
+0 -0
Auteur du sujet

Pour le problème particulier de msvcr100.dll, finalement, j'ai capitulé et utilisé un cheat. C'est pas très glorieux mais ça marche:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
static HINSTANCE msvcr = NULL;

extern "C" FILE* msvcfopen (const char* name, const char* ax) {
typedef FILE*(*FOPEN)(const char*, const char*);
static FOPEN fopenfunc = NULL;
if (!fopenfunc) {
if (!msvcr) msvcr = LoadLibraryA("msvcr100.dll");
fopenfunc = (FOPEN)GetProcAddress(msvcr, "fopen");
}
if (fopenfunc) return fopenfunc(name, ax);
else return NULL;
}

extern "C" void msvcfclose (FILE* fp) {
typedef void(*FCLOSE)(FILE*);
static FCLOSE fclosefunc = NULL;
if (!fclosefunc) {
if (!msvcr) msvcr = LoadLibraryA("msr100.dll");
fclosefunc = (FCLOSE)GetProcAddress(msvcr, "fclose");
}
if (fclosefunc) fclosefunc(fp);
}

La question suivante que j'ai maintenant est autrement plus intéressante: j'aimerais créer une classe que l'utilisateur du script python peut utiliser mais ne peut pas instancier directement. En clair, l'obliger à passer par des fonctions bien précises pour récupérer des instances.

C'est pas expliqué dans la doc officielle, ou alors j'ai raté un truc. Je n'ai qu'à utiliser PyType_GenericNew moi-même côté C++ pour créer un objet quand je veux, et laisser le champ tp_new du type à NULL ou bien c'est plus compliqué que ça ?

Entre temps je me suis amusé à coder un truc C++11 dopé aux variadict templates pour ne pas être obligé d'écrire à la mano tous les wrappers PyObject*(PyObject* self, PyObject args*) dont je vais avoir besoin. Je suis content mais en même temps j'ai l'impression d'être un gros taré pour avoir osé pondre un truc pareil… c'est pas forcément long (150 lignes) mais c'est très intense.

Merci.

Ma plateforme avec 23 jeux de société classiques en 6 langues et 13000 joueurs: http://qcsalon.net/ | Apprenez à faire des sites web accessibles http://www.openweb.eu.org/

+0 -0
Auteur du sujet

Bonjour,

J'ai trouvé ce qu'il faut faire pour forcer MinGW à se linker à une autre MSVCRT.

IL faut appliquer à la lettre ce que le mec explique dans son premier commentaire sur cette page : http://www.mingw.org/wiki/HOWTO_Use_the_GCC_specs_file

J'insiste: le commentaire, pas l'article lui-même qui ne sert quasiment à rien.

Dans le cas de msvcr100.dll il y a encore un bug bonus. La CRT incluse implicitement par MinGW fait appel aux fonctions _findfirst et _findnext qui n'existent plus. IL faut les rediriger vers _findfirst32 et _findnext32. J'ai essayé de bidouiller des .def compilés en .a avec dlltool pour faire la redirection mais je n'ai pas réussi, alors je l'ai fait à la main :

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
#include<windows.h>
// This fixes a bug with MinGW and the msvcr100 variant of the MSVCRT
// _findfirst and _findnext are absent from msvcr100.dll and libmsvcr100.a
// and should be redirected to _findfirst32 and _findnext32

typedef long intptr_t;

extern "C" intptr_t __declspec(dllimport) _findfirst32 (const char*, struct _finddata_t *);
extern "C" int __declspec(dllimport) _findnext32 (intptr_t, struct _finddata_t *);

extern "C" intptr_t __declspec(dllexport) _findfirst (const char* a, struct _finddata_t *b) {
return _findfirst32(a,b);
}

extern "C" int __declspec(dllexport) _findnext (intptr_t h, struct _finddata_t *b) {
return _findnext32(h,b);
}

Ma plateforme avec 23 jeux de société classiques en 6 langues et 13000 joueurs: http://qcsalon.net/ | Apprenez à faire des sites web accessibles http://www.openweb.eu.org/

+0 -0
Vous devez être connecté pour pouvoir poster un message.
Connexion

Pas encore inscrit ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte