2013-09-23 3 views
1

Je ne sais pas si c'est possible mais il y a peut-être d'autres solutions à ce que je veux. J'essaie d'obtenir les paramètres d'un fichier de paramètres. Ils peuvent être des chaînes (comme des noms), des entiers ou des booléens. Bien sûr, ils sont stockés en tant que texte à l'intérieur du fichier mais je vais créer une classe pour l'ouverture et le retour des paramètres, mais pas en tant que chaîne mais en tant que ce que chacun d'eux est en fait. Le constructeur chargera le fichier, analysera les paramètres et les stockera sous forme de carte, par exemple. Maintenant, quand j'appelle la fonction de membre de réglage je veux qu'il identifie quel type est la valeur du paramètre demandé (s'il est numérique, un entier, si est "vrai" ou "faux" un booléen, si est alphanumérique une chaîne) et renvoie une valeur de ce type. Un exempleC++ - Retourne différents types de variables

Settings UserPreferences("Preferences.cfg"); 
bool AutoLogin = UserPreferences.Setting("autologin"); // return bool 
string UserName = UserPreferences.Setting("username"); // return string or char* 

J'ai eu un coup d'œil sur les modèles, mais il semble que je dois préciser quelle variable je pense lors de la création des paramètres objet, mais ce n'est pas le point. Je suis heureux de déclarer le type de la variable à retourner comme ceci:

mais je ne sais pas si c'est possible. Qu'est-ce que tu penses?

+3

Avez-vous _tried_le? –

+1

J'ai une telle classe qui fonctionne bien. Donc oui, je * pense * c'est possible. ;) – syam

+0

J'ai été très surpris que cela fonctionne, étant donné que vous ne pouvez pas surcharger sur le type de retour. – BoBTFish

Répondre

2

Ceci est certainement possible, bien que vous ayez une garantie de pouvoir lancer sur le type donné. Ceci est vu beaucoup dans ContentLoader de XNA (quoique un système très différent). Vous pouvez utiliser cette approche pour simplifier et résumer comment les objets sont stockés et récupérés. Considérez:

class Loader 
{ 
private: 
    vector<void*> _items; 
public: 
    template <typename Type> 
    Type GetItem(int index) { return (Type)(_items[ index ]); } 
}; 

L'idée est que tant que vous pouvez jeter les données internes à la fiabilité du type requis (plus fiable que l'exemple) que c'est une opération parfaitement légale. Comment en faire un succès garanti est une autre question entièrement, mais vous pouvez certainement avoir des méthodes dont le type de retour est celui de leurs types de modèles. Prenons l'exemple suivant (je c'est un projet collégial pour un chargeur de ressources):

header.h

class BasicResource 
{ 
public: 
    static const int ResourceID; 
    const int ID; 
    BasicResource() 
     : ID(ResourceID) 
    { 
    } 
}; 

class Loader 
{ 
private: 
    vector<BasicResource*> _items; 
public: 
    template <typename Type> 
    Type GetItem(int index); 
}; 

#include "inline.inl" 

Inline.inl

template <typename Type> 
Type Loader::GetItem(int index) 
{ 
    auto item = _items[ index ]; 
    if(item != nullptr && item->ID == Type::ResourceID) 
    { 
     return (Type)_item; 
    } 
    else 
    { 
     // Handle the fail case somehow 
    } 
} 

fichiers Inline vous permettent de séparer votre logique comme vous le feriez normalement, mais incluez-le dans l'en-tête qui permet l'exportation des méthodes de modèle.

+0

Merci, William. Votre exemple fonctionne mais j'aimerais l'avoir dans une DLL. Le problème est que je ne peux pas (ou ne sais pas comment) scinder la classe entre le .h (interface) et .cpp (implémentation). Je ne sais même pas si c'est possible. Est-ce? – ali

+0

@ali Le code de modèle doit se trouver dans un fichier d'en-tête (généralement). Vous ne pouvez donc déplacer les implémentations non basées sur des modèles, comme le constructeur, dans un .cpp – leemes

+0

Donc, je ne peux pas déplacer le code de la fonction de modèle dans le .cpp et laisser une déclaration dans l'en-tête? Les membres modélisés ne peuvent-ils pas être exportés à partir d'une bibliothèque partagée/dynamique? – ali

2

Oui, c'est certainement possible. J'ai écrit le bit suivant du code complet pour prouver le point:

#include <iostream> 
#include <map> 
#include <string> 
#include <sstream> 
#include <stdexcept> 


struct Settings 
{ 
    typedef std::map<std::string, std::string> SettingsMap; 
    template <class T> T as(const std::string& name) const 
    { 
    std::istringstream is(getEntry(name)); 
    T value; 
    if(is) 
    { 
     if((is >> value) || (is.eof() && !is.fail())) 
     { 
     return value; 
     } 
    } 
    //Exception handling not in scope of question 
    throw std::runtime_error("..."); 
}; 

const std::string& getEntry(const std::string& name) const 
{ 
    SettingsMap::const_iterator pos(settingsMap_.find(name)); 
    if(pos != settingsMap_.end()) 
    { 
    return pos->second; 
    } 
    //Not part of the scope of this answer.... 
    throw std::invalid_argument("No such setting..."); 
} 

Settings() 
{ 
    settingsMap_["mybool"] = "1"; 
    settingsMap_["myint"] = "5"; 
    settingsMap_["myfloat"] = "43.2"; 
} 

SettingsMap settingsMap_; 
}; 

int main() 
{ 
    Settings s; 
    std::cout << s.as<bool>("mybool") << " " 
    << s.as<int>("myint") << " " 
    << s.as<float>("myfloat"); 

    return 0; 
} 

J'ai mis quelque chose de semblable à cela, mais je l'ai utilisé boost :: tout comme mon type cartographié, et je l'ai lu le type réel pendant la première analyse, garantissant ainsi que le type stocké est correct. J'ai également utilisé boost :: lexical_cast au lieu de istringstream natif, mais j'ai omis cela dans le but de prouver le point.

Questions connexes