Partage
  • Partager sur Facebook
  • Partager sur Twitter

Chargement de byte code dans des fichiers.

Langage de script

    20 novembre 2021 à 12:44:07

    Bonjour,

    Je développe un casse briques et pour les besoins de configuration des différents niveaux de jeu, je charge des données qui le plus souvent sont des chiffres que je lis dans des fichiers codés façon UTF-8 qui sont chargés dans des types primitifs ( unsigned int / int / double ) ou parfois des std::string. Il y a quelques temps je créais une nouvelle classe ou une nouvelle fonction à chaque fois que je voulais charger ce type de donnée, mais je trouve avec le recul que cela contrevient au DRY (Don't Repeat Yourself ) et je me suis demandé si cela ne pouvais pas se faire de manière générique. Voici comment je prévois de remédier au problème et je me demande si il n' y a pas de solution à laquelle je n'aurais pas pensé ou que je n'aurais pas imaginé par manque de connaissance du C++.

    Le code de la classe pour charger le byte code:

    #ifndef GENERIC_BYTE_CODE_LOADER_H
    #define GENERIC_BYTE_CODE_LOADER_H
    
    #include "byteCodeLoading/byteCodeConsts.h"
    #include <vector>
    #include <string>
    
    class ByteCodeLoader
    {
    private:
    	std::vector< ByteType > byteOrder;
    	std::vector< unsigned > unsignedIntegerByte;
    	std::vector< int > signedIntegerByte;
    	std::vector< double > doubleByte;
    	std::vector< std::string > stringByte;
    
    public:
    	ByteCodeLoader(const std::vector< ByteType >& order);
    	~ByteCodeLoader() = default;
    	
    	unsigned getUint(size_t index) const;
    	int getSint(size_t index) const;
    	double getDouble(size_t index) const;
    	const std::string& getString(size_t index) const;
    };
    
    #endif //GENERIC_BYTE_CODE_LOADER_H

    et l'entête des enums fortement typées (vu dans le livre de Koala01):

    #ifndef BYTE_CODE_CONSTS_H
    #define BYTE_CODE_CONSTS_H
    
    enum class ByteType : char{
    	isUint,
    	isSint,
    	isDouble,
    	isString
    	isMax
    };
    
    #endif //BYTE_CODE_CONSTS_H

    Et l' utilisation de la classe:

    //---------Fichier de byte code ( un unsigned int, puis un double, puis deux signed int , puis un std::string à chaque ligne
    
    4 123.456 -8 -12 herisson
    8 45.89 -2 1 marmotte
    
    
    //---------Fin fichier UTF-8 de byte code
    #include "byteCodeConsts.h"
    #include "byteCodeLoading/byteCodeLoader.h"
    
    enum class DataA_list : char{
    	ByteType::isUint,
    	ByteType::isDouble,
    	ByteType::isSint,
    	ByteType::isString,
    	Max
    };
    
    enum class DataA_uint : size_t{
    	Uint1,
    	UintMax
    };
    
    enum class DataA_sint : size_t{
    	Sint1,
    	Sint2,
    	Max
    };
    
    etc...
    
    ByteCodeLoader byteCodeA{ {ByteType::isUint, ByteType::isDouble, ByteType::isSint, ByteType::isSint, ByteType::isString} };
    
    int data{ byteCodeA.getSint(DataA_sint::Sint1) };

    Voilà. :)



    • Partager sur Facebook
    • Partager sur Twitter

    Mon site web de jeux SDL2 entre autres : https://www.ant01.fr

      20 novembre 2021 à 15:08:51

      Il n'y a pas de reflexion en C++, impossible de faire quelque chose de générique. Il y aura forcément besoin de passer par des fonctions de sérialisation / désérialisation à un moment donné.

      Ce que tu peux faire est une fonction de sérialisation template que tu spécialise pour chaque type (ou une fonction template qui appelle une fonction membre, les choix sont multiples), ainsi il te sera facile de faire une fonction générique sans savoir quel type de données tu as en entrée.

      -
      Edité par markand 20 novembre 2021 à 15:09:39

      • Partager sur Facebook
      • Partager sur Twitter

      l'azerty est aux dispositions ce que subversion est aux SCM

        20 novembre 2021 à 16:23:32

        Je crois que tu emploies le terme "ByteCode" de travers, et ça perturbe la compréhension de ce que tu veux faire.

        Un bytecode, c'est un jeu d'instructions (comme pour un processeur) qui est destiné à être exécuté par un programme (pas comme du code machine).

        Ce que tu présentes

        //---------Fichier de byte code ( un unsigned int, puis un double, puis deux signed int , puis un std::string à chaque ligne
         
        4 123.456 -8 -12 herisson
        8 45.89 -2 1 marmotte
        

        n'est pas du bytecode (où sont les instructions ???) mais une description textuelle de paramètres qui te serviront à configurer différents objets.

        Comme tu ne montres pas ce que tu fais de ces paramètres, on va pas pouvoir en dire grand chose.

        ----

        Bon, il me semble que tu t'es empêtré dans de sombres histoires d'énumeration machin truc et d'entiers signés ou pas avant de savoir vraiment où tu voulais aller.

        On va dire :

        • à partir d'un fichier de descriptions comme celui ci
        4 123.45 -8 -12 herisson
        8 45.678 -2   1 marmotte
        
        • fabriquer des objets de la classe Personnage
        class Personnage
        {
            int m_truc;
            float m_machin;
            int m_chose, m_bidule;
            std::string m_nom;
        
        public:
            Personnage(int truc, float machin, int chose, int bidule,
                       const  std::string & nom);
            ...
        }
        
        ---
        C'est parti
        1. Le main lance  lireDescriptions("descriptions.txt");
        2. la fonction lireDescriptions lit les descriptions qui sont dans le fichier, et fabrique les objets correspondants. Faute de mieux, pour cette demo, elle les fait afficher
        Pour ça :
        - lecture du fichier ligne par ligne (ifstream, getline),
        - décomposition d'une ligne de description par istringstream + >> (c'est un peu rudimentaire, un format csv serait mieux)
        - les lignes incorrectes sont signalées
        - une fois l'objet construit, on le fait afficher (fonction libre std::ostream & operator<< (std::ostream & out, const Personnage &p); laissée en exercice
        Code
        void lireDescriptions(const std::string &chemin)
        {
            std::ifstream infile(chemin);
            int numero = 0;
            std::string description;
        
            while (std::getline(infile, description)) {
                numero += 1;
                std::istringstream stream(description);
                int machin;
                float truc;
                int  chose, bidule;
                std::string nom;
                if (stream >> machin >> truc >> chose >> bidule >> nom) {
                    Personnage p{machin, truc, chose, bidule, nom};
                    std::cout << "lecture -> " << p << std::endl;
                } else {
                    std::cout << chemin
                              << ":" <<numero << " ligne ignorée : "
                              << description << std::endl;
                    continue;
                }
            }
        }
        
        Execution
        lecture -> Personnage herisson(4,123.45,-8,-12)
        lecture -> Personnage marmotte(8,45.678,-2,1)
        

        ---
        le code en entier, avec d'autres trucs
        #include <iostream>
        #include <fstream>
        #include <sstream>
        #include "Personnage.h"
        
        void lireDescriptions(const std::string &chemin)
        {
            std::ifstream infile(chemin);
            int numero = 0;
            std::string description;
        
            while (std::getline(infile, description)) {
                numero += 1;
                std::istringstream stream(description);
                int machin;
                float truc;
                int  chose, bidule;
                std::string nom;
                if (stream >> machin >> truc >> chose >> bidule >> nom) {
                    Personnage p{machin, truc, chose, bidule, nom};
                    std::cout << "lecture -> " << p << std::endl;
                } else {
                    std::cout << chemin
                              << ":" <<numero << " ligne ignorée : "
                              << description << std::endl;
                    continue;
                }
            }
        }
        
        int main()
        {
            Personnage lulu {1, 2, 3, 4, "lutin"};
            std::cout << "lulu = " << lulu << std::endl;
            
            lireDescriptions("descriptions.txt");
        }
        // Personnage.cc
        #include <sstream>
        
        #include "Personnage.h"
        
        Personnage::Personnage(int truc, float machin, int chose, int bidule,
                               const  std::string & nom)
            : m_truc{truc}, m_machin{machin}, m_chose{chose}, m_bidule{bidule},
              m_nom{nom}
        {
        
        }
        
        std::ostream & Personnage::display_on(std::ostream &out) const
        {
            out << "Personnage " << m_nom
                << "(" << m_truc << ","
                << m_machin << ","
                << m_chose  << ","
                << m_bidule << ")";
            return out;
        }
        
        
        
        std::ostream & operator<< (std::ostream & out, const Personnage &p)
        {
            return p.display_on(out);
        }
        
        // Personnage.h
        #ifndef PERSONNAGE_H_INCLUDED
        #define PERSONNAGE_H_INCLUDED
        
        #include <iostream>
        #include <string>
        
        class Personnage
        {
            int m_truc;
        	float m_machin;
        	int m_chose, m_bidule;
            std::string m_nom;
        
        public:
            Personnage(int truc, float machin, int chose, int bidule,
                       const  std::string & nom);
        			   
            std::ostream & display_on(std::ostream &out) const;
        };
        
        std::ostream & operator<< (std::ostream & out, const Personnage &p);
        
        #endif

        et le Makefile pour pas cher
        CXXFLAGS = -std=c++17
        CXXFLAGS += -Wall -Wextra -pedantic
        CXXFLAGS += -MMD
        
        EXECS = main
        
        %: %.o
        	$(LINK.cc) -o $@ $^
        
        all : $(EXECS)
        
        main: main.o Personnage.o
        
        -include $(wildcard *.d)
        
        clean:
        	$(RM) *~ *.o *.d
        
        mrproper: clean
        	$(RM) $(EXECS)
        




        -
        Edité par michelbillaud 20 novembre 2021 à 17:11:39

        • Partager sur Facebook
        • Partager sur Twitter
          20 novembre 2021 à 17:50:31

          Bonjour,

          @Markand : D'accord , je vais donc revenir à ce que je faisais avant une fonction ou classe chargée de récupérer les données écrites dans un fichier et qui les met dans une structure.

          @michelbillaud : C'est ce que je faisais auparavant, j'utilisais la combinaison std::getline( std::ifstream, std::string ) avec std::istringstream à chaque fois qu'une ligne était lue ( je ne l'ai pas précisé , désolé :( ).

          Au moins vos messages me confortent que j' utilisais la bonne méthode jusqu' à présent.:)

          Merci.

          P.S: (pour michelbillaud) , oui je me suis trompé de terme en utilisant 'bytecode' , je l'ai repris à la va-vite sur le livre 'Game programming patterns' de Robert Nystrom . Effectivement dans son livre il parle d'instructions exécutées par le processeur mais lues dans un fichier d' où ma confusion. :(

          -
          Edité par Warren79 20 novembre 2021 à 18:03:25

          • Partager sur Facebook
          • Partager sur Twitter

          Mon site web de jeux SDL2 entre autres : https://www.ant01.fr

          Chargement de byte code dans des fichiers.

          × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
          • Editeur
          • Markdown