2017-04-24 6 views
0

Ich muss einen modularen Serialisierer erstellen. Ich benutze msgpack.Vorlage Parameter Pack Serialisierung

So ist eine einfache Form ist wie folgt aufgebaut:

enum class FieldId 
{ 
    Time, 
    Pressure 
}; 

struct TimeFieldConfig 
{ 
    typedef long long DataType; 
    const static FieldId id = FieldId::Time; 
} 

struct PressureFieldConfig 
{ 
    typedef double DataType; 
    const static FieldId id = FieldId::Pressure; 
} 

struct BaseField 
{ 
    virtual void dump(std::ofstream &buffer) const = 0; 
}; 

template<class T> 
struct Field : BaseField 
{ 
    void dump(std::ofstream &buffer) 
    { 
    msgpack::pack(buffer, values); 
    } 
    std::vector<typename T::DataType> values; 
} 

struct Recorder 
{ 
    template <class T> 
    void insertField() 
    { 
     data.insert_or_assign(T::id, new Field<T>); 
    } 

    template <class T> 
    void add(const typename T::DataType &v) 
    { 
     if (data.find(T::id) != data.cend()) 
      reinterpret_cast<Field<T> *>(data[T::id])->add(v); 
    } 

    void dump(const std::string fpath) 
    { 
     std::ofstream outFile; 
     outFile.open(fpath, std::ios::binary); 

     // headers 
     std::vector<int> keys; 
     for (const auto &k : data) 
      keys.push_back(static_cast<int>(k.first)); 
     msgpack::pack(outFile, keys); 

     // values 
     for (const auto &k : data) 
      k.second->dump(outFile); 

     outFile.close(); 
    } 

    std::map<FieldId, BaseField *> data; 
} 

int main(int arc, char* argv[]) 
{ 
    Recorder r; 
    r.insertField<TimeFieldConfig>(); 
    r.insertField<PressureFieldConfig>(); 

    /* add data ... */ 
    r.dump("data.dat"); 
} 

Dump funktioniert gut, alle Daten und Header vorhanden sind. Jetzt möchte ich aufgenommene Daten zurückladen.

Meine Frage ist, wie erstelle ich eine Instanz meiner Recorder die Felder dynamisch einfügen benötigt?

+0

Dies ist zu breit - was hast du schon versucht? Wo bist du stecken geblieben? –

Antwort

0

Ich antworte mir selbst, falls es helfen könnte.

Was ich erstellen wollte, heißt Registrierungsmuster. So habe ich es gemacht.

// may be a singleton 
class FieldFactory 
{ 
public: 
    FieldFactory() 
    { 
    } 
    ~FieldFactory() 
    { 
    } 

    template <class T> 
    void registerField() 
    { 
     creators.insert_or_assign(T::id, &Field<T>::creator); 
    } 

    BaseField *create(FieldId fid) 
    { 
     return creators[fid](); 
    } 

private: 
    typedef std::function<BaseField *()> FieldCreator; 
    std::map<FieldId, FieldCreator> creators; 
}; 

Dann in Haupt, nennt es Klassen registrieren

FieldFactory ff; 
ff.registerField<TimeFieldConfig>(); 
ff.registerField<PressureFieldConfig>(); 

Im Feld Template-Klasse hinzu:

static BaseField *creator() 
{ 
    return new Field<T>(); 
} 

und eine Ladefunktion in Recorder Klasse erstellen:

void reload(const std::string &filePath) 
{ 
    std::ifstream inFile; 
    inFile.open(filePath, std::ios::binary); 

    std::vector<char> buffer((std::istreambuf_iterator<char>(inFile)), (std::istreambuf_iterator<char>())); 
    inFile.close(); 

    // headers 
    std::size_t off = 0; 
    std::vector<int> keys; 

    msgpack::object_handle result; 
    msgpack::unpack(result, buffer.data(), buffer.size(), off); 
    result.get().convert(keys); 

    // create fields 
    for (auto k : keys) 
    { 
     FieldId fid = static_cast<FieldId>(k); 
     data.insert_or_assign(fid, m_ff->create(fid)); 
    } 

    // values 
    for (auto k : keys) 
    { 
     msgpack::object_handle oh; 
     msgpack::unpack(oh, buffer.data(), buffer.size(), off); 

     FieldId fid = static_cast<FieldId>(k); 
     if (data.find(fid) != data.cend()) 
      data[fid]->reload(oh); 
    } 
} 

Vielleicht impr Ich denke, aber das ist die Idee.

Verwandte Themen