Reputation: 8924
I'm writing some funky audio code and trying to use operator overloading to create a very clean and simple API. It's become of a bit of a C++ brainteaser...
What I want would be solved immediately by an "assign to indexed" sort-of-compound operator which I'm pretty sure doesn't exist. Might anyone have any insights on whether the following is possible?
I have 2 object types....
Frames frames; // audio data, contains 1 buffer (float *) for each channel
Sample s; // a single sample, 1 float for each channel
So Sample
is kind of an orthogonal slice of Frames, ie Frames is NOT an array of Sample
's. If you know audio, Frames
is "non-interleaved", and Sample
is.
The Holy Grail...
s = frames[1]; // statement 1. gets a copy of values in frame 1
frames[1] = s; // statement 2. replace values in frame 1 with those in Sample s
The first one is no problem:
// in class Frames...
Sample& operator[](size_t idx) const {
Sample s;
s.left = _leftCh[idx];
s.right = _rightCh[idx];
return s;
}
But the second assignment is tricky since the above function creates a copy of the data rather than a reference.
I've tried defining Sample with references...
class Sample {
public:
float& L;
float& R;
Sample(float& lt, float& rt) : L(lt), R(rt) {};
}
But then you can't do something as simple as...
Sample s(0.0, 0.0);
s.left = 0.2;
Another potential solution would be to have the two statements call two different operator overloads. Then enforce that statement 2 calls this [] overload which returns a new Frames object pointing to the values rather than a Sample
object:
Frames& operator[](size_t idx) {
// Construct an new Frames object whose which
// points to value idx in each channel
Frames f(_size - idx);
f._leftCh = &_leftCh[idx];
f._rightCh = &_rightCh[idx];
return f;
}
And then add an assignment operator to Frames
which just replaces the first value...
Frames& operator=(const Sample& s) {
_leftCh[0] = s.left;
_rightCh[0] = s.right;
return *this;
}
The compiler informs me that methods must differ by more than just the return type, but this is resolved by having const
after the method name for one of the operator[]
overloads. Might there be a clue here? Is there a way to have statement 1 call Sample& operator[]...
and statment 2 call Frames& operator[]...
. Or is there a much better way of accomplishing this??
Thanks for your patience if you've made it this far! Much appreciated...
Upvotes: 3
Views: 195
Reputation: 2027
Have you tried without overloading just to work out the details? e.g. sample = frame.getSample(); frame.setSample(sample);
Once the details are worked out to your satisfaction, then you can add the syntactic sugar and overload the []
and =
operators.
It looks like you want to maintain a reference to the original Sample, so that for example:
sample.right = oldR;
sample.left = oldL;
f[x] = sample;
sample.right = newR;
sample.left = newL;
newSample = f[x];
assert(sample.right == newSample.right && sample.left == newSample.left);
Is this correct? If so, I don't think you can do that, because you "break" up your sample to insert it into the frame, so you lose the original connection.
Upvotes: 0
Reputation: 171167
How about this:
class SampleRef {
float &left_, &right_;
public:
SampleRef(float& left, float& right)
: left_(left), right_(right)
{}
operator Sample () {
return Sample(left_, right_);
}
SampleRef& operator= (const Sample &arg) {
left_ = arg.left;
right_ = arg.right;
return *this
}
};
SampleRef Frames::operator[] (size_t idx) {
return SampleRef(_leftCh[idx], _rightCh[idx]);
}
You can of course also add a const
overload of operator[]
which will simply return a Sample
:
Sample Frames::operator[] (size_t idx) const {
return Sample(_leftCh[idx], _rightCh[idx]);
}
Upvotes: 5