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++.
//---------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) };
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
git is great because Linus did it, mercurial is better because he didn't.
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;
}
}
}
@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.
× Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
× Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.
Mon site web de jeux SDL2 entre autres : https://www.ant01.fr
git is great because Linus did it, mercurial is better because he didn't.
Mon site web de jeux SDL2 entre autres : https://www.ant01.fr