I've often seen that you can replace all handwritten/raw loops with stl algorithms. Just to improve my C++ knowledge I've been trying just that.
To populate a std::vector with data I use a for loop and the loops index.
unsigned int buffer_size = (format.getBytesPerSecond() * playlen) / 1000;
// pcm data stored in a 'short type' vector
vector<short> pcm_data;
for (unsigned int i = 0; i < buffer_size; ++i)
{
pcm_data.push_back( static_cast<short>(amplitude * sin((2 * M_PI * i * frequency) / format.SampleRate)) );
}
The above code works fine, as you can see I use the for loops index 'i' for the algorithm to be correct.
How can someone replace that for loop with something from the standard?
The only functions i've seen that almost allow me to do it are std::transform and std::generate, but both of those wouldn't work because I require an index value to increment for the code.
EG:
generate_n(begin(pcm_data), buffer_size, [] ()
{
return static_cast<short>(amplitude * sin((2 * M_PI * i * frequency) / format.SampleRate)); //what is i??
});
transform(begin(pcm_data), end(pcm_data), begin(pcm_data) [] (???)
{
return static_cast<short>(amplitude * sin((2 * M_PI * i * frequency) / format.SampleRate)); //what is i??
});
Or am I simply going too far into the idea of "no raw loops"?
The real solution here would be to define an appropriate iterator, something like:
class PcmIter : public std::iterator<std::forward_iterator_tag, short>
{
int myIndex;
double myAmplitude;
double myFrequency;
short myValue;
void calculate()
{
myValue = myAmplitude * std::sin( 2 * M_PI * myIndex * frequency );
}
public:
PcmIter( int index, amplitude = 0.0, frequency = 0.0 )
: myIndex( index )
, myAmplitude( amplitude )
, myFrequency( frequency )
{
calculate();
}
bool operator==( PcmIter const& other ) const
{
return myIndex == other.myIndex;
}
bool operator!=( PcmIter const& other ) const
{
return myIndex != other.myIndex;
}
const short& operator*() const
{
return myValue;
}
PcmIter& operator++()
{
++ myIndex;
calculate();
}
PcmIter operator++( int )
{
PcmIter results( *this );
operator++();
return results;
}
};
In practice, I suspect that you could get by with having
operator*
return a value, which you calculate at that point,
and not having a myValue
member.
To use:
std::vector<short> pcmData(
PcmIter( 0, amplitude, frequency),
PcmIter( buffer_size ) );
(The amplitude and the frequency are irrelevant for the end iterator, since it will never be dereferenced.)
Ideally, this would be a random_access_iterator, so that the constructor to vector will calculate the number of elements, and pre-allocate them. This involves implementing a lot more functions, however.
If you're courageous, and have to do similar things a lot, you could consider making the iterator a template, to be instantiated over the function you're interested in.
And while I've not had a chance to play with them lately, if
you're using Boost, you might consider chaining
a transform_iterator
and a counting_iterator
. It's still
a bit wordy, but the people who did the iterators at Boost did
the best they could, given the somewhat broken design of STL
iterators.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With