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