2016-08-31 1 views
-4

Je veux stocker plusieurs types d'objets dans une carte, et je suis venu avec cette solution. Je dois connaître le type de chaque objet, donc je ne peux pas utiliser boost :: any. Y a-t-il un meilleur moyen de le faire, ou est-ce une solution acceptable?plusieurs types Stockage sur la carte

enum eType 
{ 
    TYPE_STRING, 
    TYPE_NUMBER, 
}; 

class CType 
{ 
public: 
    int GetType() { return m_Type; } 

protected: 
    int m_Type; 
}; 

template <typename T> 
class CData : public CType 
{ 
public: 
    CData(const T & rData, int iType) 
    { 
     m_Type = iType; 
     m_Data = rData; 
    } 

    T & GetData() { return m_Data; } 

private: 
    T m_Data; 
}; 

std::map<unsigned long, CType *> map_Data; 

void main() 
{ 
    // Create a new data with TYPE_NUMBER 
    CData<short> data(32767, TYPE_NUMBER); 

    // Add it to the map 
    map_Data[0] = &data; 

    // Get the type 
    switch (map_Data[0]->GetType()) 
    { 
     case TYPE_NUMBER: 
     { 
      // Cast the first element to CData 
      CData<short> * pField = (CData<short> *)map_Data[0]; 

      // Print the data 
      printf("Data: %d\n", pField->GetData()); 
     } 
     break; 

     case TYPE_STRING: 
     { 
      // Cast the first element to CData 
      CData<std::string> * pField = (CData<std::string> *)map_Data[0]; 

      // Print the data 
      printf("Data: %s\n", pField->GetData().c_str()); 
     } 
     break; 
    } 
} 
+1

avez-vous essayé de l'exécuter? aussi 'void main' n'existe pas – TemplateRex

+0

Bien sur j'ai essayé, ça marche en studio visuel. – JOkle

+0

Vous demandez une opinion. Si vous l'avez exécuté, le compilateur l'a accepté. Est-ce suffisant? – CAB

Répondre

1

Utilisez boost::any et boost::any_cast.

un contrôle pour voir si l'une quelconque est un entier;

bool is_int(const boost::any & operand) 
{ 
    return operand.type() == typeid(int); 
} 

Un chèque pour voir si le tout est un const char *

bool is_char_ptr(const boost::any & operand) 
{ 
    try 
    { 
     any_cast<const char *>(operand); 
     return true; 
    } 
    catch(const boost::bad_any_cast &) 
    { 
     return false; 
    } 
} 
0

Si vous ne voulez pas polluer vos classes avec des fonctions telles que getType, une solution basique et facile à utiliser utilise un taggés union comme celui dans l'exemple suivant:

#include <map> 

struct StructA { }; 
struct StructB { }; 

struct TaggedUnion { 
    enum { A, B } tag; 
    union { 
     StructA a; 
     StructB b; 
    }; 
}; 

int main() { 
    std::map<std::size_t, TaggedUnion> map; 
    map[0] = TaggedUnion{ TaggedUnion::A, StructA{} }; 

    switch(map[0].tag) { 
    case TaggedUnion::A: 
     // do whatever you want with map[0].a; 
     // ... 
     break; 
    case TaggedUnion::B: 
     // do whatever you want with map[0].b 
     // ... 
     break; 
    } 
} 
0

Votre question est intéressante et je ne comprends pas pourquoi tant downvoted. Malheureusement ce que vous essayez de faire est impossible en C++ (je me demande si c'est faisable en Java ou C#)

Je mets de nouveau le lien de ce que je pense est le noyau de votre question.

creating-an-interface-for-an-abstract-class-template-in-c++

Sur cette implémentation est intéressante. Je viens de mettre un peu réusinage ici que je pense que vous pouvez épargner quelques lignes sans cas méchant interrupteur:

#include <iostream> 
#include <string> 
#include <sstream> 
#include <List> 

using namespace std; 

struct CType 
{ 
    int GetType() { return m_Type; } 
    string GetStringVal() { return m_string_val; } 
    enum eType {  TYPE_STRING, 
        TYPE_NUMBER }; 
protected: 
    int m_Type; 
    string m_string_val; 
}; 

template <typename T> 
class CData : public CType 
{ 
public: 
    CData(const T & rData):m_Data(rData) 
    { 
     stringstream strs; 
     m_Type = GetType(); 
     //Mingw bug 
     //m_string_val = std::to_string(m_Data); //c++11 
     strs << m_Data; 
     m_string_val = strs.str(); 
    } 

    T & GetData() { return m_Data; } 

private: 
    T m_Data; 
    CType::eType GetType(); 
}; 

template<> CType::eType CData<int>::GetType() { return TYPE_NUMBER; }; 
template<> CType::eType CData<string>::GetType() { return TYPE_STRING; }; 
//More specialization here 

int main() 
{ 
    cout << "Hello world!" << endl; 

    CData<int> cd_int(5); 
    CData<string> cd_str("SO contribution"); 

    list<CType> my_list = { cd_int, cd_str }; 

    for (auto & elem : my_list) 
     cout << elem.GetStringVal() << endl; 

    return 0; 
} 

Les résultats sont naturellement:

Bonjour tout le monde!

SO contribution

----- ADD-ON 2016-Sept-05 -----

Une autre possibilité est de stocker une fonction (lambda avec " cette "capture" renvoyant le résultat au lieu du résultat lui-même. Il évite d'effectuer des mises à jour lorsque le champ m_Data change

#include <iostream> 
#include <string> 
#include <sstream> 
#include <List> 
#include <functional> 

using namespace std; 

struct CType 
{ 
    int GetType() { return m_Type; } 
    string GetStringVal() { return m_string_func(); } 
    enum eType {  TYPE_STRING, 
        TYPE_NUMBER }; 
protected: 
    int m_Type; 
    function<string()> m_string_func ; 
}; 

template <typename T> 
class CData : public CType 
{ 
    public: 
    CData(const T & rData):m_Data(rData) 
    { 
     m_Type = GetType(); 
     m_string_func = [this](){ //MinGW bug,otherwise to_string() 
            stringstream strs; 
            strs << m_Data; 
            return strs.str();}; 
    } 

private: 
    T m_Data; 
    CType::eType GetType(); 
}; 

template<> CType::eType CData<int>::GetType() { return TYPE_NUMBER; }; 
template<> CType::eType CData<string>::GetType() { return TYPE_STRING; }; 

int main() 
{ 
    cout << "Hello world!" << endl; 

    CData<int> cd_int(5); 
    CData<string> cd_str("SO contribution"); 

    list<CType> my_list = { cd_int, cd_str }; 

    for (auto & elem : my_list) 
     cout << elem.GetStringVal() << endl; 

    return 0; 
}