Archives pour la catégorie «Libraries»

Comment éviter des erreurs « bêtes » en manipulant la pile lua

Lua est globalement très facile à utiliser. Ceci dit la manipulation de la pile (stack) n’est pas forcément évidente sur de longues manipulations. En effet selon la fonction employée, la fonction va dépiler (pop) un certain nombre d’éléments, voire en pusher un certain nombre. Dès lors comment s’assurer que la pile est équilibrée (balanced), c’est-à-dire que la taille de la pile avant et après appel de la fonction est identique ?

Imaginons par exemple cette fonction (tirée & modifiée  de luabind) :

 int create_cpp_class_metatable(lua_State* L)
 {
//Empile un tableau
lua_newtable(L);

// Empile une chaîne de caractère
 lua_pushstring(L, "__luabind_classrep");
// Empile un booléen
 lua_pushboolean(L, 1);
/* Jusque là c'était simple, maintenant il faut être capable de retenir que cette fonction assigne au tableau à la position en paramètre (soit ici "-3", c'est-à-dire 3 emplacements en dessous du haut de la pile => le tableau créé par lua_newtable(L)), la valeur à l'index -1 pour l'indice à l'index -2. Cet appel équivaut donc à monNouveauTableau["__luabind_classrep"] = 1.
lua_rawset dépile les 2 paramètres du haut de la pile ("__luabind_classrep" et "1") mais ne touche pas au tableau...
*/
 lua_rawset(L, -3);

// du coup ici on quitte la fonction en laissant le tableau sur le haut de la pile. PAS BIEN !!
}

Pour faciliter le débogage de ce type d’erreur, certaines bibliothèques dont luabind propose une classe avec le prototype suivant:

// Copyright (c) 2003 Daniel Wallin and Arvid Norberg

// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:

// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.

// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
// ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
// TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
// PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
// SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR
// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
// OR OTHER DEALINGS IN THE SOFTWARE.

#ifndef LUABIND_DEBUG_HPP_INCLUDED
#define LUABIND_DEBUG_HPP_INCLUDED

#ifndef NDEBUG

#include <luabind/lua_include.hpp>
#include <cassert>

namespace luabind { namespace detail
{

    /** \struct  stack_checker_type
     *  Registers stack size on creation. Destructor checks size is the same than that registered during creation (otherwise asserts).
     */

	class stack_checker_type
	{
	    public:
            stack_checker_type(lua_State* L,const int& diff)
                : m_L(L)
                , m_stack(lua_gettop(m_L) + diff)
            {}

            ~stack_checker_type()
            {
                assert(m_stack == lua_gettop(m_L));
            }
        private:
            lua_State* m_L;
            int m_stack;
	};

}}

    /** \def LUABIND_CHECK_STACK
     *   This macro checks if stack is balanced between its construction and destruction (within its C++ scope).
     * Available only with NDEBUG
     * \param A lua_State
     * creates an object registering current size of stack. When getting out of scope, destruct
     */
    #define LUABIND_CHECK_STACK(L) luabind::detail::stack_checker_type stack_checker_object(L,0)


    /** \def LUABIND_CHECK_STACK_DIFF check that the difference in size between construction and destruction of the structure is equal to diff
    * \param diff Differnece of stack size (either positive or negative)
    **/
    #define LUABIND_CHECK_STACK_DIFF(L,diff) luabind::detail::stack_checker_type stack_checker_object(L, (diff) )
#else
    #define LUABIND_CHECK_STACK(L) do {} while (0)
    #define LUABIND_CHECK_STACK_DIFF(L,diff) do {} while (0)
#endif

#endif // LUABIND_DEBUG_HPP_INCLUDED

Cette classe enregistre la taille de la pile lors de la construction d’une de cette instance. Quand l’instance sort de sa portée (scope), typiquement en fin de fonction, le destructeur est appelé. Le destructeur vérifie alors que la taille est identique à celle enregistrée lors de son instanciation. Si la taille de la pile après diffère de celle à l’instanciation alors un assert est lancé. Si l’on reprend notre exemple précédent, le code devient alors.


        int create_cpp_class_metatable(lua_State* L)
        {

            LUABIND_CHECK_STACK(L);//!< Crée une instance de la classe définie dans le fichier précédent

            lua_newtable(L);

            // mark the table with our (hopefully) unique tag
            // that says that the user data that has this
            // metatable is a class_rep
            lua_pushstring(L, "__luabind_classrep");
            lua_pushboolean(L, 1);
            lua_rawset(L, -3);

        }/* On sort du scope, le destructeur est appelé et détecte que la taille diffère (le tableau est restée sur la pile. La pile n'est pas "balanced", un assert va être lancé */

La version précédente de create_cpp_class_metatable va donc lancer un assert. Voici la version corrigée:


        int create_cpp_class_metatable(lua_State* L)
        {

            LUABIND_CHECK_STACK(L);//!< Crée une instance de la classe définie dans le fichier précédent

            lua_newtable(L);

            lua_pushstring(L, "__luabind_classrep");
            lua_pushboolean(L, 1);
            lua_rawset(L, -3);

            return luaL_ref(L, LUA_REGISTRYINDEX);//!< Dépile ce qu'il y a sur le dessus de la pile. Ici il s'agit du tableau
/* On sort du scope, le destructeur est appelé et détecte que la taille diffère (le tableau est restée sur la pile. La pile n'est pas "balanced", un assert va être lancé */
        }

On peut perfectionner la classe stack_checker_type en ajoutant une fonction membre check() pour vérifier la taille avant l’appel du destructeur, ou bien même passer un delta pour vérifier que la pile à la fin est égale à la taille de la pile lors de l’instanciation + delta (fonctionnalité que j’ai implémentée dans ma version de luabind). Par ailleurs on remarque que ce système de vérification peut-être désactivée selon si NDEBUG est défini ou pas. Les performances de votre application finale ne seront donc pas perturbées par cette astuce !

Luabind 0.9.1 adapted to lua 5.2 beta

As you may know, lua 5.2 is going to be released very soon with new interesting features. To be able to use these features and to keep using luabind at the same time, I’ve decided to hack luabind 0.9.1 so that it supports lua 5.2 beta. Here is the link to download:

http://downloads.tuxfamily.org/bluecosmos/utils/luabind-perso.zip

(I’ve suppressed some « super » related code as I don’t need it and it’s deprecated anyway)
It works fine with me but I may have introduced bugs.

I did it for fun and I don’t intend to fork luabind or whatever. I might consider adding __index/__newindex override or help another binding library. There are a few interesting around like oolua and sbl (Simple Binding).

Utiliser une chaîne de caractère comme paramètre template C++

Le jeu repose sur une machine à état où les différents états sont identifiés par une chaîne de caractère (« Options », »Menu », »Scores » etc…). Je passais ce nom via le constructeur de la class parente ( par exemple

 COptionsState() : IGameState("Options") { } 

). Pour certaines raisons (pour le fun aussi), j’ai voulu passer ce paramètre non plus au runtime mais à la compilation via un paramètre template type template <const char* ID> class IGameState {}. J’ai alors – naïvement tenté – de faire dériver l’état des options de la classe template IGameState précédente comme suit:

class COptionsState() : public IGameState<"Options"> { ... }

Avec les erreurs qui vont bien. Ceci n’est pas possible car la variable passée en paramètre doit être linké de manière  externe et non locale et ce pour s’assurer que 2 classes distinctes ne seront pas définies à partir du même template pour le même paramètre.

On doit donc déclarer une variable linkable de manière externe. Pour ceci on peut utiliser le mot clé « extern » => extern const char* Options_Str = « options »; et définir ainsi

class COptionsState() : public IGameState<Options_Str> { ... }

. Aussi apparemment il existe une différence entre const char* Str considérée comme une variable et non comme une adresse mémoire par gcc quand passée en argument. Ainsi il vaut mieux définir Options_Str sous forme de tableau:

 extern const char Options_Str[] = "options";

qui chez moi (mingw) a fait disparaitre tous les messages d’erreurs.

Note: Boost MPL a ajouté desoptions pour les strings apparemment.

Autre lien intéressant sur le sujet, la FAQ comeau

How to safely convert a wide string into a narrow string (and vice-versa) ?

Irrlicht relies on wide strings (irr::core::stringw / std::wstring) for the GUI system but I had to convert some of them into narrow strings (std::string for instance) for the game.
So I looked on the internet for various solutions but there didn’t seem to be a general and clean one. Snippets did always call a fixed-size buffer which I din’t want to resort to.
That’s when I discovered this very neat C++ project (just a few headers). It makes the conversion through iterators and seems robust (until now I haven’t met any problem).

Here is the code I use :

inline void
convertIntoNarrowString(const std::wstring& original, std::string& converted)
{
utf8::utf16to8(original.begin(),original.end(), std::back_inserter(converted) );
}

inline void
convertIntoWideString(const std::string& original, std::wstring& converted)
{
utf8::utf8to16(original.begin(),original.end(), std::back_inserter(converted) );
}

Comment rediriger la sortie de la SDL vers la console

…ou comment forcer l’affichage de printf en console et empêcher SDL de l’enregistrer dans stdout.txt .

Cette question est traitée dans la FAQ officielle. On peut trouver d’autres solutions en parcourant le net ( qques sujets sur le forum de gamedev.net notamment ) mais aussi ici .

En résumé, il faut ajouter quelques lignes lors de l’initialisation de la SDL.Le code suivant fonctionne chez moi:

freopen( "CON", "w", stdout );
freopen( "con", "w", stderr );

SDL_Init(SDL_INIT_VIDEO);
FILE* ctt = fopen("CON", "w" );

ecran = SDL_SetVideoMode(676, 640, 32, SDL_HWSURFACE | SDL_DOUBLEBUF);    //On crée la fenetre.
fclose( ctt );