|
Here is some code from the draw function in my custom control. I am using a vector to contain data concerning portions of the control.
This code works. You will surely agree with me, though, that using the iterator 'x' to identify vector members in called functions is probably a tad on the clunky side. I have had a go at sending pointers to vector members. I gave up with this. I think this might not be the right way to go either. What it is the correct, elegant way to do this?
vector<sliderUnit>::iterator slider;
for ( slider=sliderVector.begin() ; slider != sliderVector.end(); slider++ )
{
theSliderRect.left = theDrawRect.left+(x-2)*sliderWidth;
theSliderRect.right = theSliderRect.left + sliderWidth;
(*slider).m_TextBoxSize = m_TextBoxSize;
(*slider).sliderRect = (RECT)theSliderRect;
tempRect=theSliderRect;
tempRect.right=tempRect.right-4;
DrawBackground(&theMemDC,tempRect);
if (m_currentSlider != x-1){
(*slider).m_bFlashFocus = FALSE;
}
else{
(*slider).m_bFlashFocus = TRUE;
}
DrawButtonsByID(&theMemDC, theSliderRect, x-1);
DrawThumbSliderByID(&theMemDC, theSliderRect, x-1);
DrawSliderLabelByID(&theMemDC, theSliderRect, x-1);
x++;
}
modified 9-Oct-12 16:11pm.
|
|
|
|
|
Huh?
What type is "X"? I assume it is an int (or long) since you add it to a CRect internal value.
In what way does it identify a vector element? it is not clear.
Nihil obstat
|
|
|
|
|
Sorry, I should have clearer. The function calls I refer to are at the bottom of that block of code
DrawButtonsByID(&theMemDC, theSliderRect, x-1);
DrawThumbSliderByID(&theMemDC, theSliderRect, x-1);
DrawSliderLabelByID(&theMemDC, theSliderRect, x-1);
The 'x' becomes 'id' in the called function below which dereferences the vector member that is the same as the iterator in that first for loop block I posted. I know this is murky. That's why I want to make nicer.
void CScrollBarEx::DrawThumbSliderByID(CDC* inDC, CRect inDrawRect, short id)
{
CRect theThumbSliderRect;
COLORREF theThumbSliderColor = GetSysColor(COLOR_3DFACE);
COLORREF theThumbSliderTextColor = GetSysColor(COLOR_WINDOWTEXT);
ASSERT(inDC != NULL);
if (sliderVector[id].m_bThumbSliderDownFlag && sliderVector[m_currentSlider].m_bTrackThumbSliderFlag)
{
theThumbSliderColor = sliderVector[id].m_ThumbSliderTrackColor;
theThumbSliderTextColor = m_ThumbSliderTextColor;
}
...
|
|
|
|
|
So you're accessing a vector with an index that comes from a variable of unknown type and origin. Not a lot of information to work with. Why do you think this is a problem? Is there a reason why you think x is not the appropriate index to use?
Until you explain what x is and where you got it from, it's hard for us to understand your problem - if there is one. Since the code works, the only suggestion I can make at this point is to rename x to something more ... self-explanatory, and add a few comments to explain what's going on (while you're still able to understand it). That may be helpful if you need to fix it in a year or two.
P.S.: There's one other suggestion I can think of: instead of passing x to those functions, pass a reference to the vector element. While technically that is the same as passing a vactor index, it removes the dependency between those functions and the entire vector.
|
|
|
|
|
I have spent some time trying to write code to send a reference to the vector element. I think I might have had trouble extracting this from the iterator. I must have somehow persuaded myself that there was some reason as to why passing a reference to a vector element from an iterator was a bad idea. I was just wondering if anyone knew if there is some sort of trap here - . I would probably be happier doing it this way if it is straightforward and safe. It seems more elegant. I don't really want to get bogged down in making the changes, though, if there is some inherent problem.
The functions in the iterator block use and possibly change the data in the vector element but don't call any further functions that use the vector element. There is only a single thread accessing the vector.
|
|
|
|
|
You didn't use an actual iterator anywhere in the code that you posted, so I don't see the problem. When a function argument is declared as a reference, you can just pass a vector element, such as sliderVector[id] . If you do have an iterator, then you can just dereference it to get the vector element and pass that instead. If you wish to pass a pointer instead, just use the 'address-of' operator:
void bar_ref(double& element) {
element *= 2.0;
}
void bar_ptr(double* p_element) {
*p_element += 3.0;
}
void foo() {
std::vector<double> myvec(2); myvec[0] = 3.1415;
myvec[1] = 2.718;
for (std::size_t i = 0; i < myvec.size(); ++i) {
bar_ref(myvec[i]); bar_ptr(&(myvec[i])); }
for (auto it = myvec.begin(); it != myvec.end(); ++it) {
bar_ref(*it); bar_ptr(&(*it)); }
}
The only possible trap I see is trying to pass an iterator directly when a pointer is expected. An iterator may behave like a pointer, but, while - depending on the implementation - it may actually auto-convert to a valid pointer (I know VS 2003 did that), you shouldn't rely on that. (e. g. VS2010 doesn't accept it)
|
|
|
|
|
Thanks for this! I will certainly give this a go and let you know how I get on. It might not be for a few weeks, though...
|
|
|
|
|
Thanks very much for showing me how this can be done, Stefan. I have tried your method and it works perfectly.
I have thought some more about what I'm trying to achieve, though, and have come to the conclusion that since the vector array has class scope and that its members don't themselves know, or need to know, whether they are the current item in the 'for' block (where I've got the iterator) - the more elegant solution would, I guess, involve just sending an (integer) identifier for the required element.
Your help is very much appreciated. Your correspondence has helped my thought process a lot.
Cheers,
Ben
|
|
|
|
|
Glad I could be of help.
As for your conclusion, keep in mind that sending an ID of the element requires you to also pass the container that this ID refers to! Otherwise the function won't know what to do with that ID. Whether that ID is an iterator or index value doesn't matter in this regard - neither have an inherent knowledge of the container they refer to.
If the container is class scope, and you pass an ID to a class member function, that would work. But from a design view, doing so would still be questionable as the function actually shouldn't need to care about the rest of the container - you have introduced an unnecessary dependency. If at some point you realize you want to use that same function for elements not related to this class, you cannot easily do it, due to that dependency.
|
|
|
|
|