jeudi 17 décembre 2020

Is a visitor pattern an appropriate solution here for derivied classes

Overview

The program I am trying to write takes a file type and converts it to another file. The program will know what files it will accept for example only .foo, .bar, .csv; The data in the files have clear similarities in their content them that allow them to be read after decoding the bytes from one file type and encoding the bytes to another after applying some logic. They are proprietary file types from old programs that no longer have support.

I have a derived class for each file type that has a struct called 'definition' whose purpose is to define how the bytes in the file are laid out and the values they're associated with.

A file interface is responsible for loading the correct encoder/decoder, reading the file, and comparing the bytes against the encoder/decoder for validation.

Right now the code will not work as shown because the creation of the HeaderDefinition and DataDefinition are not polymorphic. I would like the derived class to be responsible for creating the associated struct definition. I suppose I can wrap the struct into another series of classes such as Base definition with FileA Definition(.foo), FileB Definition (.bar), and FileC Definition(.csv) as derived classes and leave it the decoder class to create the one it needs. But that seems redundant, I was wondering if there is a design pattern here that can solve all my issues.

My goal is to keep my implementation clean and testable so I can add future file types, and features, such as, a UI, and the ability to do analysis after decoding.

Main.cpp

int main(int argc, char** argv)
{
    InputParser input(argc, argv);

    const std::string& filepath = input.getCmdOption("-f");

    if (!filepath.empty())
    {
        FileInterface file_interface;
        // Extract values from file
        auto values = file_interface.decode_from(filepath);

    }

    return 0;
}

BaseDecoder.h

class BaseDecoder
{
public:
    
    virtual BaseHeaderDefinition getHeaderDefinition () = 0;
    virtual BaseDataDefinition   getDataDefintion () = 0;

    virtual bool        validateMagicBytes(BaseHeaderDefinition &definition) = 0;
    virtual uint32_t    extractTotalPoints(BaseHeaderDefinition& definition) = 0;
    virtual double      extractStartTime(BaseHeaderDefinition& definition)   = 0;
    virtual double      extractEndTime(BaseHeaderDefinition& definition)     = 0;
    virtual float       extractDataPoint(BaseDataDefinition& data)           = 0;

}

FooDecoder.h

#pragma pack(push, 1)
struct  FooHeaderDefinition {
    std::array<uint8_t, 4>      MAGIC;      
    std::array<uint8_t, 4>      totalpoints;    
    std::array<uint8_t, 4>      start_time; 
    std::array<uint8_t, 4>      end_time;       
    uint8_t                     endl;       // End header 
};
#pragma pack(pop)

#pragma pack(push, 1)
struct FooDataDefinition {
    uint8_t     data;
};
#pragma pack(pop)

class FooDecoder
{
public:
FooDecoder();

FooHeaderDefinition getHeaderDefinition() override { FooHeaderDefinition new_def = {};  return new_def; };
FooDataDefinition getDataDefinition() override { FooDataDefinition new_def = {}; return new_def;};

bool        validateMagicBytes(HeaderDefinition &definition) override;
uint32_t    extractTotalPoints(HeaderDefinition& definition) override;
double      extractStartTime(HeaderDefinition& definition) override;
double      extractEndTime(HeaderDefinition& definition)override;
float       extractDataPoint(DataDefinition& data) override;

private:

// Class specific functions
double decodeStartFreq(FooHeaderDefinition & definition);
uint32_t convertArray2int(std::array<uint8_t, 4>& arr);
std::array<uint8_t, 20> magic_bytes = { 0x00, 0x01, 0x02, 0x03 };
};

FileInterface.h

class FileInterface
{
public:
    FileInterface();
    ~FileInterface();

    extracted_values decode_from(const std::string& filepath);

private:
    //
    // Private member functions
    //
    void extract_header();
    void extract_data();

    //
    // Current loaded file
    std::ifstream file_;

    //
    // cast to derived encoder based on file type provided.
    BaseDecoder encoder_;
    
    //
    // The values extracted from a file stored in standard units.
    extracted_values values_ = {};
};

Aucun commentaire:

Enregistrer un commentaire