I'm trying to combine three signal waveforms into a single, interleaved waveform. I need to know the best way to do it in C++ STL. Better solutions would use as much C++ STL style as possible, avoid redundant code, etc. Is there some STL "tuple" type class that would do this for me? I need contiguous storage at all times for backward compatibility with other code (therefore, vector). The best solutio开发者_JAVA技巧n would be correct and easy to understand. Space and speed are not as high of a priority as correctness and ease of understanding.
The output waveform must be ordered like this: first sample from first channel, first sample from second channel, first sample from third channel, then continue with the second sample from each channel and repeat for all samples. I know that all three input waveforms have the same number of samples.
In Matlab, I would have done it like this:
function outputWaveform=Interleave3(a, b, c) outputWaveform=zeros([1 3*length(a)]); outputWaveform(1:3:end)=a(:); outputWaveform(2:3:end)=b(:); outputWaveform(3:3:end)=c(:);
This is my first C++ STL attempt:
typedef vector<double> dVector; typedef vector<double>::iterator dVectorIT; dVector Interleave3(dVector a, dVector b, dVector c) { dVector result(0, 3*a.size()); dVectorIT aIT=a.begin(), bIT=b.begin(), cIT=c.begin(), rIT=result.begin(); for(; aIT != a.end(); ++aIT, ++bIT, ++cIT) { *rIT++=*aIT; *rIT++=*bIT; *rIT++=*cIT; } return result; }
It works, but is there a better way to do this? I hoped there might be some clever way to do it in one line with transform(). Can you append b to a, then c to a, then transform the temporary "a1a2a3...-b1b2b3...-c1c2c3..." vector into "a1b1c1a2b2c2a3b3c3..."?
Bonus question: I also need the inverse operation (to split an output waveform of 3*N samples into 3 vectors of N samples each). The Matlab solution is quite easy:
function [a, b, c]=Deinterleave3(outputWaveform) a=outputWaveform(1:3:end); b=outputWaveform(2:3:end); c=outputWaveform(3:3:end);
C++ STL seems fairly awkward, and I bet there's a better way to do it than this:
typedef vector<double> dVector; typedef vector<double>::iterator dVectorIT; void Deinterleave3(dVector outputWaveform, dVector &a, dVector &b, dVector &c) { ASSERT( !(outputWaveform.size()%3) ); a.clear(); b.clear(); c.clear(); dVectorIT oIT=outputWaveform.begin(); for(; oIT != outputWaveform.end(); ) { a.push_back( *oIT++ ); b.push_back( *oIT++ ); c.push_back( *oIT++ ); } }
Is there some clever combination of transform() and back_inserter() which would do the inverse operation? Again, using a temporary vector would be acceptable.
Boost has a "zip iterator," but I can't figure out if it would perform either the interleaving or deinterleaving operation.
EDIT: fixed the missing angle brackets (<>). The HTML filter ate them! Also, I have a new idea about how to fix this with a custom iterator.
If you're willing to use Boost, you can use slices to get syntax very close to your code in Matlab. Here's a C++ version of your Interleave3 function:
template<typename V>
V Interleave3(V const& a, V const& b, V const& c)
{
using namespace boost::numeric::ublas;
V v(a.size() + b.size() + c.size());
vector_slice<V>(v, slice(0, 3, a.size())) = a;
vector_slice<V>(v, slice(1, 3, b.size())) = b;
vector_slice<V>(v, slice(2, 3, c.size())) = c;
return v;
}
The catch is that instead of using std::vector you have to use boost::numeric::ublas::vector.
I'm not sure about interleaving but you can use for_each
to deinterleave. You would still want to add additional error checking (that the source size is correct etc):
#include <iostream>
#include <vector>
typedef std::vector<double> dVector;
typedef dVector::iterator dVectorIT;
class DeInterleave
{
public:
DeInterleave(std::vector<dVector>& output) : output_(output), currentIndex_(0) { }
void operator()(const dVector::value_type& item)
{
output_[currentIndex_].push_back(item);
currentIndex_ = (currentIndex_ + 1) % output_.size();
}
private:
std::vector<dVector>& output_;
int currentIndex_;
};
int main()
{
dVector source;
source.push_back(5.0);
source.push_back(6.0);
source.push_back(8.0);
source.push_back(2.0);
source.push_back(2.0);
source.push_back(1.0);
std::vector<dVector> dest(3);
std::for_each(source.begin(), source.end(), DeInterleave(dest));
std::cout << dest[0].size() << " " << dest[0][0] << ":" << dest[0][1] << std::endl;
std::cout << dest[1].size() << " " << dest[1][0] << ":" << dest[1][1] << std::endl;
std::cout << dest[2].size() << " " << dest[2][0] << ":" << dest[2][1] << std::endl;
return 0;
}
精彩评论