Generic Memory Pointers/Access

P

Patchwork

Hi Everyone,

I have a design-/implementation-related question for you all.

In practical usage of C++, especially when processing image or audio data
with 3rd party libraries, there often arises the situation in which a memory
location has to be interpreted as a collection of values of a specific data
type. For example, imagine an audio API that returns void* in a request to
GetSamples(). A simple loop outputting each sample may be:

if (BytesPerSample == 1)
{
// Single byte case (1 - 8 bits per sample)
ty1ByteType* pAudioBuffer =
reinterpret_cast<ty1ByteType*>(AudioData.GetSamples());

for (int SampleNo = 0; SampleNo < NumberOfSamples; SampleNo++)
{
cout << SampleNo << ": " << *(pAudioBuffer++) << endl;
}
}
else if (BytesPerSample == 2)
{
// Double byte case (9 - 16 bits per sample)
ty2ByteType* pAudioBuffer =
reinterpret_cast<ty2ByteType*>(AudioData.GetSamples());

for (int SampleNo = 0; SampleNo < NumberOfSamples; SampleNo++)
{
cout << SampleNo << ": " << *(pAudioBuffer++) << endl;
}
}
else
{
throw ... // Unsupported byte depth
}

Obviously the repeated code is thoroughly undesirable. The code could be
modified so that the processing loop occurs on the outside and the pointer
is cast at each iteration, but this isn't desirable either. As further
depths are supported, the problem snowballs.

I am wondering how other people have addressed this oft occurring problem. I
would like to write the processing code only once and, preferably,
initialise the pointer once also. Initially I thought of writing a
template-based hierarchy, that would allow the following approach:

CMyPointer* pAudio = CMyPointer::CreateInstance(Audio.GetSamples(),
ByteDepth);
CMyPointer& pAudioBuffer = *pAudio;

for (int SampleNo = 0; ...)
{
cout << ... << *(pAudioBuffer++) ...
}

delete pAudio;

To do this, CMyPointer would need to be an abstract non-template-based class
presenting the 'named constructor' CreateInstance and pure virtual pointer
functions. CreateInstance would return the correct typed instance of the
subclass CMyTypedPointer<DATA_TYPE> pointing to the given address using a
datatype of the appropriate byte depth. The problem here arises when
considering the operator*() function in the base class - because this is not
a template-based class, a specific return type has be given which would
differ in instances of the base classes.

What solutions to this scenario have people employed? Of course, it would be
further preferable to cast the memory to any arbitrary type, including
objects.

Many thanks.
Lucy x
 
B

Bernd Fuhrmann

What solutions to this scenario have people employed? Of course, it would be
further preferable to cast the memory to any arbitrary type, including
objects.
I'm not that familiar with your problem, as I don't use pointer classes
in general. However, I tend to solve similar problems this way:

template <int size, class type>
SomeFunctionheader(...)//insert neccessary parameters here
{
type* pAudioBuffer = reinterpret_cast<type*>(AudioData.GetSamples());

for (int SampleNo = 0; SampleNo < NumberOfSamples; SampleNo++)
{
cout << SampleNo << ": " << *(pAudioBuffer++) << endl;
}
}

//...

if (BytesPerSample == 1)
{
SomeFunction<1,ty1ByteType>(...);
}
else if (BytesPerSample == 2)
{
SomeFunction<2,ty2ByteType>(...);
}
else
{
throw ... // Unsupported byte depth
}

The resulting executable should be almost equivalent.

Hope that helps,
Bernd Fuhrmann
 
C

Cy Edmunds

Patchwork said:
Hi Everyone,

I have a design-/implementation-related question for you all.

In practical usage of C++, especially when processing image or audio data
with 3rd party libraries, there often arises the situation in which a memory
location has to be interpreted as a collection of values of a specific data
type. For example, imagine an audio API that returns void* in a request to
GetSamples(). A simple loop outputting each sample may be:

if (BytesPerSample == 1)
{
// Single byte case (1 - 8 bits per sample)
ty1ByteType* pAudioBuffer =
reinterpret_cast<ty1ByteType*>(AudioData.GetSamples());

for (int SampleNo = 0; SampleNo < NumberOfSamples; SampleNo++)
{
cout << SampleNo << ": " << *(pAudioBuffer++) << endl;
}
}
else if (BytesPerSample == 2)
{
// Double byte case (9 - 16 bits per sample)
ty2ByteType* pAudioBuffer =
reinterpret_cast<ty2ByteType*>(AudioData.GetSamples());

for (int SampleNo = 0; SampleNo < NumberOfSamples; SampleNo++)
{
cout << SampleNo << ": " << *(pAudioBuffer++) << endl;
}
}
else
{
throw ... // Unsupported byte depth
}

Obviously the repeated code is thoroughly undesirable. The code could be
modified so that the processing loop occurs on the outside and the pointer
is cast at each iteration, but this isn't desirable either. As further
depths are supported, the problem snowballs.

I am wondering how other people have addressed this oft occurring problem. I
would like to write the processing code only once and, preferably,
initialise the pointer once also. Initially I thought of writing a
template-based hierarchy, that would allow the following approach:

CMyPointer* pAudio = CMyPointer::CreateInstance(Audio.GetSamples(),
ByteDepth);
CMyPointer& pAudioBuffer = *pAudio;

for (int SampleNo = 0; ...)
{
cout << ... << *(pAudioBuffer++) ...
}

delete pAudio;

To do this, CMyPointer would need to be an abstract non-template-based class
presenting the 'named constructor' CreateInstance and pure virtual pointer
functions. CreateInstance would return the correct typed instance of the
subclass CMyTypedPointer<DATA_TYPE> pointing to the given address using a
datatype of the appropriate byte depth. The problem here arises when
considering the operator*() function in the base class - because this is not
a template-based class, a specific return type has be given which would
differ in instances of the base classes.

What solutions to this scenario have people employed? Of course, it would be
further preferable to cast the memory to any arbitrary type, including
objects.

Many thanks.
Lucy x

First of all, try to refrain from using reinterpret_cast -- it is the most
dangerous of the C++ cast types. A static_cast should be enough to convert a
void pointer.

Next, isolate the casting to a couple of functions:

ty1ByteType *ty1(AudioDataType &AudioData)
{
if (AudioData.BytesPerSample != 1)
throw a_fit();
return static_cast<ty1ByteType*>(AudioData.GetSamples());
}

ty2ByteType *ty2(AudioDataType &AudioData)
{
if (AudioData.BytesPerSample != 2)
throw a_fit();
return static_cast<ty2ByteType*>(AudioData.GetSamples());
}

I assumed that the bytes per sample was available from AudioData's type. I
sure hope so, because not checking here would be a big mistake. It would be
far too easy to type ty2 when you meant ty1 and get the wrong cast.

Then write your processing code template style:

template <typename BYTETYPE> // BYTETYPE is ty1ByteType or ty2ByteType
display_audio(BYTETYPE *ptr, int NumberOfSamples)
{
for (int SampleNo = 0; SampleNo < NumberOfSamples; SampleNo++)
cout << SampleNo << ": " << *(ptr++) << endl;
}

You might call this function using another function as follows:

display_audio(AudioDataType &AudioData)
{
if (AudioData.BytesPerSample == 1)
display_audio(ty1(AudioData), NumberOfSamples);
else if (AudioData.BytesPerSample == 2)
display_audio(ty2(AudioData), NumberOfSamples);
else
bomb_city();
}

It still isn't the prettiest thing in the world, but when dealing with
legacy libraries you do what ya gotta do. If you look at the gamut of
operations you want to do with your audio data you can probably write a very
clean wrapper that hides these ugly details, perhaps using polymorphism to
avoid the if/then/else ladders. Then test, test, and test some more. Good
luck.

Warning: untested code above. And I make LOTS of mistakes. :)
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top