Herzlich Willkommen, lieber Gast!
  Sie befinden sich hier:

  Forum » C / C++ (ANSI-Standard) » Datentyp anhand Argument zuweisen

Forum | Hilfe | Team | Links | Impressum | > Suche < | Mitglieder | Registrieren | Einloggen
  Quicklinks: MSDN-Online || STL || clib Reference Grundlagen || Literatur || E-Books || Zubehör || > F.A.Q. < || Downloads   

Autor Thread - Seiten: > 1 <
000
16.07.2019, 10:07 Uhr
pauledd



Hi

Vorhaben:
Ich schreibe gerade eine Konsolenanwendung die mir eine Binärdatei mit Daten in eine CSV-Datei konvertiert. Das funktioniert auch bis dahin.

Problem:
Die Datenblöcke der Binärdatei können 8bit oder 10bit sein. Ich möchte die Bittiefe als Commandline Argument übergeben. Das funktioniert soweit.

Das Problem ist nun nachdem ich die Bittiefe aus dem Argument ermittelt habe muss ich dann irgendwie dem ifstream.read() Befehlen mit dem passenden buffer Datentyp füttern.

Das sind die beiden Buffer Typen:

C++:
block16_t buf_16;
block8_t buf_8;


Wie kann ich jedem "ifile.read(reinterpret_cast..." mitteilen das ich buf_8 oder buf_16 verwenden will.

Das letzte was mir einfällt ist den ganzen Leseprozess in zwei getrennte Funktionen zu packen die jeweils block16_t oder block8_t verwendet, das würde aber den Code fast verdoppeln. Gibt es eine elegantere Lösung dem "ifile.read" sozusagen dynamisch mitzuteilen welchen buffer er verwenden soll?


Hier mein ganzer Code:
dataTypes:

C++:
struct metadata_t{                    // Byte Nr. in file
    unsigned int adcFrequency;        // 0-3
    unsigned int cpuFrequency;        // 4-7
    unsigned int sampleInterval;    // 8-11
    unsigned int recordEightBits;    // 12-15
    unsigned int pinCount;
    unsigned int pinNumber[123];    // 4B * 123 = 492 Byte
};


// 10bit data
struct block16_t{
    unsigned short count;            // 2B
    unsigned short overrun;
    unsigned short data[254];        // 2B * 254 = 508 Byte
};

// 8bit data
struct block8_t{
    unsigned short count;
    unsigned short overrun;
    unsigned char data[508];
};



main.cpp

C++:
#include <iostream>
#include <fstream>
#include "dataTypes.h"
#include <cstring>

using namespace std;

block16_t buf_16;
block8_t buf_8;
metadata_t* pm;


int main(int argc, char **argv) {
    
    // check arguments -------------------------------------------
    
    if(argc == 1) {
        cerr << "no input file specified! Use:" << endl;
        cerr << "bin2csv" << " infile outfile [8,10](bits)" << endl;
    } else if(argc == 2) {
        cerr << "no output file specified! Use:" << endl;
        cerr << "bin2csv" << " infile outfile [8,10](bits)" << endl;
    } else if(argc == 3) {
        cerr << "no bitdepth  specified! Use:" << endl;
        cerr << "bin2csv" << " infile outfile [8,10](bits)" << endl;
    } else if(argc > 4) {
        cerr << "too much arguments specified, must be three!  Use:" << endl;
        cerr << "bin2csv" << " infile outfile [8,10](bits)" << endl;
    } else if(!strcmp(argv[3], "10") || !strcmp(argv[3], "8")) {
        const string inFile = argv[1];
        const string outFile = argv[2];
        
        // input data ---------------------------------------------
        
        ifstream ifile(inFile, ios::binary);
        if(!ifile) {
            cerr << "ERROR: cannot read input file!" << endl;
        } else {

            // read header metadata -----------------------------------------
            
            ifile.read(reinterpret_cast<char*>(&buf_16), sizeof(buf_16));
            pm = reinterpret_cast<metadata_t*>(&buf_16);
            int PIN_COUNT = pm->pinCount;
            
            
            // write header csv  -------------------------------------------------
            
            ofstream ofile(outFile, ios::trunc);
            if(!ofile) {
                cerr << "ERROR: cannot open output file!" << endl;
            } else {
                ofile << "# adcFrequency:\t\t" << pm->adcFrequency <<
                        "\n# cpuFrequency:\t\t" << pm->cpuFrequency <<
                        "\n# sampleInterval:\t" << pm->sampleInterval <<
                        "\n# eightBits:\t\t" << pm->recordEightBits <<
                        "\n# pinCount:\t\t" << pm->pinCount << endl;
        
                for(int i=0;i<PIN_COUNT;i++){
                    ofile << "PIN" << pm->pinNumber[i];
                    if(i < PIN_COUNT-1){
                        ofile << ',';
                    }else{
                        ofile << endl;
                    }
                }
                // read data blocks --------------------------------------------------
                
                while(ifile.read(reinterpret_cast<char*>(&buf_16), sizeof(buf_16))) {
                    if(buf_16.count == 0) {
                        break;
                    }
                    
                    // write data blocks to csv --------------------------------------
                    for(int j=0; j<buf_16.count; j+=PIN_COUNT) {
                        for(int i=0; i<PIN_COUNT; i++) {
                            if(i) {
                                ofile << ',';
                            }
                            ofile << buf_16.data[i+j];
                            if(i) {
                            }
                        }
                        ofile << '\n';
                    }
                }
            }
             ofile.close();
        }
        ifile.close();
    }else{
        cerr << "bitdepth is not 8 or 10! USE:" << endl;
        cerr << "bin2csv" << " infile outfile [8,10](bits)" << endl;
    }
return 0;
}



Dieser Post wurde am 16.07.2019 um 10:19 Uhr von pauledd editiert.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
001
17.07.2019, 08:31 Uhr
FloSoft
Medialer Over-Flow
(Administrator)


Hi,

warum drehst du die verantwortlichkeit nicht um, d.h du machst echtes c++, kein C mit C++ funktionen.

- abstrakte Basisklasse für "Block", mit einer virtuellen Funktion "read" die einen (i)stream bekommt.


C++:
class BlockBase
{
public:
virtual ~BlockPufferTyp();

virtual void read(istream& buf) = 0;
};



- abgeleitete Klassen Block8 und Block16, die diese Funktion passend implementieren.


C++:
class Block8 : public BlockBase
{
public:
   virtual void read(ifstream& buf) override
   {
     buf.read(this, sizeof(this)); // achtung, sowas kann auch gefährlich sein. besser ist hier eigentlich das einzelne einlesen in die einzelnen membervariablen
   }

    unsigned char count;
    unsigned char overrun;
    unsigned char data[254];
}

class Block16 ...




- deine main instanziiert dann je nach Parameter eine Block8 oder Block16 als Basistyp und benutzt die virtuelle methode um das passende objekt einzulesen


C++:
// ...

unique_ptr<BlockBase> buffer;

if(option8)
{
    buffer(new Block8);
}
else
{
   buffer(new Block16);
}

// ...

buffer->read(ifile);

// ...




Prinzipiell kann dann alles, was die beiden Sachen gemeinsam tun, in die Basisklasse, und alles was nicht in die abgeleiteten jeweils.

das gleiche gilt dann auch für deine metadaten.

wenn man einen reinterpret_cast benutzen muss, ist es meistens ein Zeichen das man kein "echtes" modernes C++ schreibt
--
class God : public ChuckNorris { };

Dieser Post wurde am 17.07.2019 um 08:34 Uhr von FloSoft editiert.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
002
18.07.2019, 13:10 Uhr
pauledd



Danke dir, ich versuche deine Ratschläge mal umzusetzen... hab bloß gerade ein bisschen viel um die Ohren ()
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
003
19.07.2019, 09:39 Uhr
pauledd



Also ich habe mich jetzt mal an die abstrakte Klasse getraut und davon dann eine abgeleitete Klasse erstellt, erstmal nur für den 8bit Fall, nur um zu sehen ob ich ordentlich einlesen kann. Mir ist noch nicht wirklich klar wie das mit "new" und unique_ptr funktioniert.
Also habe ich dann in main Block_8t mit new erstellt und lösche es am ende wieder.

Was ich jetzt nicht verstehe ist warum du mein 8bit...


C++:
    unsigned short count;
    unsigned short overrun;
    unsigned char data[508];



struct zu "unsigned char" geandert hast? Die Blöcke in der Binary sind ja alle 512 Byte groß.
Bei den 8bit Daten bestehen die Blöcke alle aus dem count (2 Byte), overrun (2 Byte) und die eigentlichen Daten data (508 Byte), ergibt 512 Byte.

Du hast daraus:


C++:
    unsigned char count;
    unsigned char overrun;
    unsigned char data[254];



count = 1, overrun = 1, data = 254, zusammen 256 Byte gemacht.

Oder war das nur ein Tippfehler bei dir?

Dann noch eine andere Frage bezüglich:

Zitat:

wenn man einen reinterpret_cast benutzen muss, ist es meistens ein Zeichen das man kein "echtes" modernes C++ schreibt



Ich komme einfach nicht drauf wie ich dem ifstream::read(char* s, streamsize n) die Daten korrekt entlocken soll ohne "reinterpret_cast" ?

Ich verstehe auch nicht wie das stattdessen mit "this" funktionieren soll.
Woher nimmt das "this" die einzulesende Größe von 512 Byte?

Und warum du die variablen in Block8 public machst? Ist das alles beabsichtigt?

Hier ist mal der Code soweit wie ich gekommen bin, ich lese mit

C++:
buf.read(reinterpret_cast<char*>(&count), sizeof(count));


erstmal nur "count" ein, das klappt auch, er liest nur die ersten zwei Byte ein.


C++:
#include <iostream>
#include <fstream>

class BlockBase{
public:
    virtual void read(std::ifstream &buf) = 0;
    virtual ~BlockBase(){};
};

class Block_8t : public BlockBase
{
private:

public:
    virtual void read(std::ifstream &buf) override {
        buf.read(reinterpret_cast<char*>(&count), sizeof(count));
        std::cout << count << '\n';
    }
    unsigned short get_Count(){
        return count;
    }
    void printSize(){
        std::cout << "size1:" << sizeof(*this) << '\n';
    }
    unsigned short count;
    unsigned short overrun;
    unsigned char data[508];
};

int main(int argc, char **argv)
{
    std::string file;
    file = argv[1];
    std::ifstream ifile(file, std::ios::binary);
    if(!ifile){
        std::cout << "file open ERROR!\n";
    }
    
    Block_8t *b = new Block_8t;
    b->printSize();
    b->read(ifile);
    ifile.close();
    
    delete b;
    return 0;
}




Zum Spaß habe ich mal versucht die Größe von this auszugeben und da sagt er mir 520 Byte, also kann das mit this schon mal nicht funktionieren, es müssten ja 512 Byte sein.

Bitte um Erleuchtung

Dieser Post wurde am 19.07.2019 um 09:40 Uhr von pauledd editiert.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
004
23.07.2019, 20:15 Uhr
pauledd



Ich habe mal nur dein Grundgerüst genommen:

C++:
#include <iostream>
#include <fstream>
#include <memory>

class BlockBase
{
public:
     BlockBase() {};
     virtual void readFile ( std::ifstream &istr ) = 0;
};

class Block8_t:public BlockBase
{
     void readFile ( std::ifstream & istr ) override
     {
          istr.read ( ( char* ) &this->count, sizeof ( this ) );
     }
     unsigned char count;
};

int main()
{
     std::unique_ptr<BlockBase> bb;
     bb ( new Block8_t );
     return 0;

}


damit meckert der Compiler aber dass

Code:
main.cpp: In function ‘int main(int, char**)’:
main.cpp:25:17: error: no match for call to ‘(std::unique_ptr<BlockBase>) (Block8_t*)’
  bb(new Block8_t);
                 ^



Wie soll ich der BlockBase denn das (new Block8_t) übergeben?

Code:
Type 'std::unique_ptr<BlockBase>' does not provide a call operator
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
005
27.07.2019, 18:17 Uhr
f.-th.



Hier mal ein Link zu https://de.cppreference.com/w/cpp/memory/unique_ptr.

Will mich da nicht reinknien - deine Fehlermeldung sollte man mit Hilfe des Links angehen können.


Könnte man dein Programm nicht auch mittels

C++:
std::bitset<N>::bitset

angehen?

MfG f.-th.

Dieser Post wurde am 27.07.2019 um 18:18 Uhr von f.-th. editiert.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
006
29.07.2019, 10:38 Uhr
pauledd



Ich habe das ganze inzwischen anders gelöst, aber diesmal tatsächlich ohne reinterpret_cast...

Ich habe eine Base Klasse in der ich eine Template Funktion erstellt habe der ich den Datentyp übergebe und die mir dann einmal die Daten ausliest und dann gleich in eine CSV schreibt. Außerdem lese ich zuerst de Metadaten aus dem File Header und lege damit gleich fest ob die Daten 8 oder 10bit sind. Als Argumente gebe ich dem Programm nur noch die Eingabe und Ausgabe Datei mit...

Ich bin mir sicher da sind noch ungereimtheiten drinn aber bis jetzt funktioniert es ganz gut.

https://github.com/pauledd/bin2csv/blob/master/main.cpp
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
Seiten: > 1 <     [ C / C++ (ANSI-Standard) ]  


ThWBoard 2.73 FloSoft-Edition
© by Paul Baecher & Felix Gonschorek (www.thwboard.de)

Anpassungen des Forums
© by Flo-Soft (www.flo-soft.de)

Sie sind Besucher: