Džuris
Džuris

Reputation: 2234

Declare variable with unknown type

I am reading .wav files in a c++ program.

The .wav format is such that a samples are either uint8_t or int16_t. Is it possible to declare the variable based on the format of the specific file?

One solution would be:

int16_t sample;
if (bits==8)
{
  uint8_t temp8;
  in.read((char*)&temp8,bits);
  sample = (int16_t) temp8;
}
else
{
  in.read((char*)&sample,bits);
}

and I can use sample from now on. However that doesn't seem very handy and in the 8bit case the variable sample is bigger then it's needed.

I believe this could also be done with union but it still would make the variable take up 2 bytes regardless of anything.

What I would actually want is:

if(bits==8)
  uint8_t sample;
else
  int16_t sample;

But with a scope that exceeds the conditional statement.

Upvotes: 4

Views: 6337

Answers (1)

Flovdis
Flovdis

Reputation: 3095

You can use an abstract base class and a template class to provide different implementations of sample reader. Using the abstract interface, later code doesn't need to know if it handles 8 bit or 16 bit samples.

#include <fstream>
#include <vector>
#include <exception>

class SampleReader
{
public:
    virtual void readSample( std::ifstream &in ) = 0;
};

template<class T>
class SampleReaderT : public SampleReader
{
public:
    virtual void readSample( std::ifstream &in )
    {
        while (!in.eof()) {
            const T value = readOneValue(in);
            if (in) {
                _sample.push_back();
            } else {
                break;
            }
        }
    }

private:
    T readOneValue( std::ifstream &in )
    {
        T inputUnit;
        in.read((char*)&inputUnit,sizeof(T));
    }

private:
    std::vector<T> _sample;
};

typedef SampleReaderT<int8_t> SampleReader8Bit;
typedef SampleReaderT<int16_t> SampleReader16Bit;

int main(int argc, char** argv)
{
    int sampleBits = 8;

    SampleReader *sampleReader = nullptr;
    if (sampleBits == 8) {
        sampleReader = new SampleReader8Bit();
    } else if (sampleBits == 16) {
        sampleReader = new SampleReader16Bit();
    } else {
        throw std::out_of_range("unknown bit number");
    }

    std::ifstream in("test.dat");
    sampleReader->readSample(in);
}

Note the following things in this example:

  • This is just an example, you certainly need to find a better design for your project.
  • There is a base class with pure virtual methods. This base class is a great help to make your code abstract. You just can use a pointer to SampleReader and don't have to know if this is a 8bit or 16bit sample reader.
  • The shown way to read the values is not platform independent. There are platforms with a different byte order.
  • The error handling is missing in this example. Reading a stream can raise errors at any time.

Upvotes: 3

Related Questions