Board index » cppbuilder » List index out of bounds exception

List index out of bounds exception


2006-01-19 01:51:44 AM
cppbuilder62
I have created a TGraphicControl component which works as it
should so far, at least in design-time. If I try to compile an
app with it or even delete the component after placing it on a
form, I get a "List index out of bounds (-nnnnnnn)" error.
Here's the code. Please point out my (probably obvious) error?
Thanks!
-----------------------------------------------------
SegmentBar.h
-----------------------------------------------------
class TSegment : public TCollectionItem
{
private:
double FPercent;
TColor FColor;
bool FOutlined;
void __fastcall SetPercent(double Pct);
void __fastcall SetColor(TColor Clr);
void __fastcall SetOutlined(bool IsOutlined);
public:
__fastcall TSegment(TCollection *ACollection);
virtual void __fastcall Assign(TPersistent *ASource);
__published:
__property double Percent = {read=FPercent, write=SetPercent};
__property TColor Color = {read=FColor, write=SetColor};
__property bool Outlined = {read=FOutlined, write=SetOutlined};
};
//---------------------------------------------------------------------------
class TSegments : public TOwnedCollection
{
private:
double FPercentSum;
TSegment* __fastcall GetSegment(int Index);
void __fastcall SetSegment(int Index, TSegment *ASegment);
public:
__fastcall TSegments(TComponent* AOwner);
TSegment* __fastcall Add(void);
TSegment* __fastcall Add(double Pct, TColor Clr);
__published:
__property double PercentSum = {read=FPercentSum, write=FPercentSum};
__property TSegment* Segment[int Index] = {read=GetSegment, write=SetSegment};
};
//---------------------------------------------------------------------------
class PACKAGE TSegmentBar : public TGraphicControl
{
private:
TColor FColor;
TColor FOutlineColor;
TSegments *FSegments;
void __fastcall SetColor(TColor Clr);
void __fastcall SetOutlineColor(TColor Clr);
public:
__fastcall TSegmentBar(TComponent* AOwner);
__fastcall ~TSegmentBar(void);
void __fastcall Paint(void);
__published:
__property TColor Color = {read=FColor, write=SetColor};
__property TColor OutlineColor = {read=FOutlineColor, write=SetOutlineColor};
__property TSegments *Segments = {read=FSegments, write=FSegments};
};
//---------------------------------------------------------------------------
----------------------------------------
SegmentBar.cpp
----------------------------------------
static inline void ValidCtrCheck(TSegmentBar *)
{
new TSegmentBar(NULL);
}
//---------------------------------------------------------------------------
namespace Segmentbar
{
void __fastcall PACKAGE Register()
{
TComponentClass classes[1] = {__classid(TSegmentBar)};
RegisterComponents("Win32", classes, 0);
}
}
//---------------------------------------------------------------------------
__fastcall TSegment::TSegment(TCollection* ACollection) : TCollectionItem(ACollection)
{
FPercent = 1.0;
FColor = clBlue;
FOutlined = false;
TControl *Bar = dynamic_cast<TControl*>(Collection->Owner());
Bar->Invalidate();
}
//---------------------------------------------------------------------------
void __fastcall TSegment::Assign(TPersistent *ASource)
{
TSegment *ASegment = dynamic_cast<TSegment*>(ASource);
if(ASegment){
Percent = ASegment->Percent;
Color = ASegment->Color;
Outlined = ASegment->Outlined;
}
else
TCollectionItem::Assign(ASource);
}
//---------------------------------------------------------------------------
void __fastcall TSegment::SetPercent(double Pct)
{
double PctSum = 0.0;
if(FPercent != Pct){
FPercent = Pct;
TSegments *Segs = dynamic_cast<TSegments*>(Collection);
for(int i = 0; i < Segs->Count; ++i)
PctSum += Segs->Segment[i]->Percent;
if(PctSum>100.0)
throw Exception("Segment percentages total>100.0%");
else
Segs->PercentSum = PctSum;
TControl *Bar = dynamic_cast<TControl*>(Collection->Owner());
Bar->Invalidate();
}
}
//---------------------------------------------------------------------------
void __fastcall TSegment::SetColor(TColor Clr)
{
if(FColor != Clr){
FColor = Clr;
TControl *Bar = dynamic_cast<TControl*>(Collection->Owner());
Bar->Invalidate();
}
}
//---------------------------------------------------------------------------
void __fastcall TSegment::SetOutlined(bool IsOutlined)
{
if(FOutlined != IsOutlined){
FOutlined = IsOutlined;
TControl *Bar = dynamic_cast<TControl*>(Collection->Owner());
Bar->Invalidate();
}
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
__fastcall TSegments::TSegments(TComponent* AOwner)
: TOwnedCollection(AOwner, __classid(TSegment))
{
}
//---------------------------------------------------------------------------
TSegment* __fastcall TSegments::Add(void)
{
if(FPercentSum < 100.0)
return dynamic_cast<TSegment*>(TOwnedCollection::Add());
else
return 0;
}
//---------------------------------------------------------------------------
TSegment* __fastcall TSegments::Add(double Pct, TColor Clr)
{
if(FPercentSum < 100.0){
TSegment *Seg = dynamic_cast<TSegment*>(TOwnedCollection::Add());
Seg->Percent = Pct;
Seg->Color = Clr;
if(Seg->Percent>= 0.0)
return Seg;
else{
delete Seg;
return 0;
}
}
else
return 0;
}
//---------------------------------------------------------------------------
TSegment* __fastcall TSegments::GetSegment(int Index)
{
return dynamic_cast<TSegment*>(Items[Index]);
}
//---------------------------------------------------------------------------
void __fastcall TSegments::SetSegment(int Index, TSegment *ASegment)
{
if(Segment[Index])
Segment[Index]->Assign(ASegment);
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
__fastcall TSegmentBar::TSegmentBar(TComponent* AOwner) : TGraphicControl(AOwner)
{
FSegments = new TSegments(AOwner);
Color = clWindow;
Height = 16;
Width = 150;
}
//---------------------------------------------------------------------------
__fastcall TSegmentBar::~TSegmentBar(void)
{
delete FSegments;
}
//---------------------------------------------------------------------------
void __fastcall TSegmentBar::SetColor(TColor Clr)
{
if(FColor != Clr){
FColor = Clr;
Invalidate();
}
}
//---------------------------------------------------------------------------
void __fastcall TSegmentBar::SetOutlineColor(TColor Clr)
{
if(FOutlineColor != Clr){
if(Clr == FColor){
if(FColor != clBlack)
FOutlineColor = clBlack;
else
FOutlineColor = clGray;
}
else
FOutlineColor = Clr;
Invalidate();
}
}
//---------------------------------------------------------------------------
void __fastcall TSegmentBar::Paint(void)
{
TRect Rect;
double Mult = 0.0;
int NextLeft = 0;
TSegment *Seg;
Canvas->Brush->Color = Color;
Canvas->Pen->Color = Color;
Canvas->Rectangle(0, 0, Width, Height);
Rect.Top = 0;
Rect.Bottom = Height;
for(int i = 0; i < Segments->Count; ++i){
Seg = Segments->Segment[i];
Mult = Seg->Percent / 100.0;
Rect.Left = NextLeft;
Rect.Right = (Width * Mult) + Rect.Left;
if(Rect.Right - Rect.Left < 1)
Rect.Right = Rect.Left + 1;
Canvas->Brush->Color = Seg->Color;
if(Seg->Outlined)
Canvas->Pen->Color = OutlineColor;
else
Canvas->Pen->Color = Seg->Color;
Canvas->Rectangle(Rect);
NextLeft = Rect.Right;
}
}
//---------------------------------------------------------------------------
 
 

Re:List index out of bounds exception

"TerryC" < XXXX@XXXXX.COM >wrote in message
Quote
__property TSegment* Segment[int Index] = {read=GetSegment,
write=SetSegment};
Properties that have an index accessor should not be published. The Object
Inspector doesn't know how to handle them.
Quote
__property TSegments *Segments = {read=FSegments, write=FSegments};
You need to use a setter method for that property, otherwise your
TGraphicControl can take ownership of collections that it has no business
taking ownership of. This is particularly important at design-time, since
the IDE uses temporary collections when running the Items editor. If your
component takes ownership of the IDE's internal memory, you can crash your
component, if not the whole IDE, since the memory won't be valid for very
long but your component won't know that.
Quote
TSegment* __fastcall Add(void);
Since TCollection already has its own Add() method which is not virtual, you
need to use the HIDESBASE macro so that your Add() does not conflict with
TCollection's Add().
Quote
class PACKAGE TSegmentBar : public TGraphicControl
{
private:
TColor FColor;
TControl already has its own Color property. You should be using that
property instead of declaring a new one.
Quote
TControl *Bar = dynamic_cast<TControl*>(Collection->Owner());
Bar->Invalidate();
You should not be doing that, especially not in the TSegment's constructor.
If you want the TGraphicControl to react to changes in the collection, then
you should be overriding the collection's Update() method instead. That
also applies to all of your TSegment's property setters as well - when a
value of a particular item is changed, call the item's Changed() method,
which then calls the collection's Update() method. This way, you have a
single centralized location to update your TGraphicControl from regardless
of which changes occur in the collection items.
Quote
Percent = ASegment->Percent;
Color = ASegment->Color;
Outlined = ASegment->Outlined;
You are invoking the setter methods for those properties, which will then
update the TGraphicControl 3 separate times. You should update the member
variables directly and then call Changed() once.
Quote
return dynamic_cast<TSegment*>(TOwnedCollection::Add());
You should be using static_cast instead of dynamic_cast. Your items are
guaranteed to always be TSegment instances, so there is no need to perform a
run-time check to verify that each time.
Quote
else
return 0;
That can be dangerous since Add() is expected to always return a valid
object. If you do not want to return an object, then you should be throwing
an exception instead.
In fact, you don't need to validate the PercentSum inside of Add() anyway
since TSegment::SetPercent() already does that for you. If you change the
TSegment constructor to call SetPercent(), then TSegment can throw an
exception automatically if the PercentSum is already too high. That
exception will then abort the creation of the new TSegment object, and the
exception can then be allowed to escape from Add() into the calling code.
This is also important since SetPercent() is the only place where you are
updating the TSegments::PercentSum property. You are not updating the
PercentSum when new items are added to the collection. Or removed from the
collection, for that matter, which means that your TSegment destructor would
have to call SetPercent() as well in order for the TSegments::PercentSum
property to be decremented properly.
A better design would be for TSegment to not update the
TSegments::PercentSum property directly at all. Instead, change the
PercentSum property to be read-only, and then give it its own getter method
that calculates the sum dynamically.
Quote
void __fastcall TSegments::SetSegment(int Index, TSegment *ASegment)
{
if(Segment[Index])
Segment[Index]->Assign(ASegment);
}
That is wrong, and likely to be the source of your "Out of Bounds" error,
because you are not performing any bounds checking when reading the
Segment[] property. Also, you should be calling the inherited SetItem()
method instead of accessing the TSegment object directly:
void __fastcall TSegments::SetSegment(int Index, TSegment *ASegment)
{
TOwnedCollection::SetItem(Index, ASegment);
}
If you want to do bounds checking, then it would be like this:
void __fastcall TSegments::SetSegment(int Index, TSegment *ASegment)
{
if( (Index>= 0) && (Index < Count) )
TOwnedCollection::SetItem(Index, ASegment);
}
Quote
FSegments = new TSegments(AOwner);
You need to use the TGraphicControl itself, not the TGraphicControl's Owner,
for the Owner of the collection, otherwise the collection will not be
streamsed properly.
Now, with all of that said, try this code instead:
--- SegmentBar.h ---
class TSegment : public TCollectionItem
{
typedef TCollectionItem inherited;
private:
double FPercent;
TColor FColor;
bool FOutlined;
void __fastcall SetPercent(double AValue);
void __fastcall SetColor(TColor AValue);
void __fastcall SetOutlined(bool AValue);
public:
__fastcall TSegment(TCollection *ACollection);
virtual void __fastcall Assign(TPersistent *ASource);
__published:
__property double Percent = {read=FPercent, write=SetPercent};
__property TColor Color = {read=FColor, write=SetColor};
__property bool Outlined = {read=FOutlined, write=SetOutlined};
};
class TSegments : public TOwnedCollection
{
typedef TOwnedCollection inherited;
private:
TNotifyEvent *FOnUpdate;
double __fastcall GetPercentSum(void);
TSegment* __fastcall GetSegment(int AIndex);
void __fastcall SetSegment(int Index, TSegment *AValue);
protected:
virtual void __fastcall Update(TCollectionItem *AItem);
public:
__fastcall TSegments(TComponent* AOwner);
HIDESBASE TSegment* __fastcall Add(void);
TSegment* __fastcall Add(double APercent, TColor AColor, bool
AOutlined);
__property double PercentSum = {read=GetPercentSum};
__property TSegment* Segment[int AIndex] = {read=GetSegment,
write=SetSegment};
__property TNotifyEvent OnUpdate = {read=FOnUpdate,
write=FOnUpdate};
};
class PACKAGE TSegmentBar : public TGraphicControl
{
typedef TGraphicControl inherited;
private:
TColor FOutlineColor;
TSegments *FSegments;
void __fastcall SegmentsUpdated(TObject *ASender);
void __fastcall SetOutlineColor(TColor AValue);
void __fastcall SetSegments(TSegments *AValue);
public:
__fastcall TSegmentBar(TComponent* AOwner);
__fastcall ~TSegmentBar(void);
virtual void __fastcall Paint(void);
__published:
__property Color;
__property TColor OutlineColor = {read=FOutlineColor,
write=SetOutlineColor};
__property ParentColor;
__property TSegments *Segments = {read=FSegments,
write=SetSegments};
};
--- SegmentBar.cpp ---
#include "SegmentBar.h"
__fastcall TSegment::TSegment(TCollection* ACollection)
: TCollectionItem(ACollection)
{
FColor = clBlue;
FOutlined = false;
SetPercent(1.0);
}
void __fastcall TSegment::Assign(TPersistent *ASource)
{
TSegment *ASegment = dynamic_cast<TSegment*>(ASource);
if( ASegment )
{
FColor = ASegment->Color;
FOutlined = ASegment->Outlined;
FPercent = ASegment->Percent;
Changed(false);
}
else
inherited::Assign(ASource);
}
void __fastcall TSegment::SetPercent(double AValue)
{
if( FPercent != AValue )
{
if( AValue < 0.0 )
throw Exception("New percentage cannot be negative");
TSegments *Segs = dynamic_cast<TSegments*>(Collection);
if( (Segs) && (((Segs->PercentSum - FPercent) + AValue)>
100.0) )
throw Exception("New percentage is too high");
FPercent = AValue;
Changed(false);
}
}
void __fastcall TSegment::SetColor(TColor AValue)
{
if( FColor != AValue )
{
FColor = AValue;
Changed(false);
}
}
void __fastcall TSegment::SetOutlined(bool AValue)
{
if( FOutlined != AValue )
{
FOutlined = AValue;
Changed(false);
}
}
__fastcall TSegments::TSegments(TComponent* AOwner)
: TOwnedCollection(AOwner, __classid(TSegment))
{
}
TSegment* __fastcall TSegments::Add(void)
{
return static_cast<TSegment*>(inherited::Add());
}
TSegment* __fastcall TSegments::Add(double APercent, TColor AColor, bool
AOutlined)
{
TSegment *Result = NULL;
try
{
BeginUpdate();
try
{
Result = this->Add();
Result->Percent = APercent;
Result->Color = AColor;
Result->Outlined = AOutlined;
}
__finally
{
EndUpdate();
}
}
catch(const Exception &)
{
if( Result )
delete Result;
throw;
}
return Result;
}
double __fastcall TSegments::GetPercentSum(void)
{
double Result = 0.0;
for(int i = 0; i < Count; ++i)
Result += Segments[i]->Percent;
return Result;
}
TSegment* __fastcall TSegments::GetSegment(int AIndex)
{
return static_cast<TSegment*>(inherited::GetItem(Index));
}
void __fastcall TSegments::SetSegment(int AIndex, TSegment *AValue)
{
inherited::SetItem(AIndex, AValue);
}
void __fastcall TSegments::Update(TCollectionItem *Item)
{
if( FOnUpdate )
FOnUpdate(this);
}
__fastcall TSegmentBar::TSegmentBar(TComponent* AOwner)
: TGraphicControl(AOwner)
{
FSegments = new TSegments(this);
FSegments->OnUpdate = SegmentsUpdated;
SetBounds(Left, Top, 16, 150);
}
__fastcall TSegmentBar::~TSegmentBar(void)
{
delete FSegments;
}
void __fastcall TSegmentBar::SegmentsUpdated(TObject *ASender)
{
Invalidate();
}
void __fastcall TSegmentBar::SetOutlineColor(TColor AValue)
{
if( FOutlineColor != AValue)
{
if( Color == AValue )
{
if( Color != clBlack )
FOutlineColor = clBlack;
else
FOutlineColor = clGray;
}
else
FOutlineColor = AValue;
Invalidate();
}
}
void __fastcall TSegmentBar::Paint(void)
{
TRect Rect;
int NextLeft = 0;
TSegment *Seg;
Canvas->Brush->Color = Color;
Canvas->FillRect(ClientRect);
Rect.Top = 0;
Rect.Bottom = Height;
for(int i = 0; i < Segments->Count; ++i)
{
Seg = Segments->Segment[i];
Rect.Left = NextLeft;
Rect.Right = (Rect.Left + (Width * (Seg->Percent / 100.0)));
if( Rect.Width()>0 )
{
Canvas->Brush->Color = Seg->Color;
if( Seg->Outlined )
{
Canvas->Pen->Color = OutlineColor;
Canvas->Rectangle(Rect);
}
else
Canvas->FillRect(Rect);
}
NextLeft = Rect.Right;
}
}
Gambit
 

Re:List index out of bounds exception

Remy,
Thank you! I appreciate the time you put into your response. I
have printed it out and will study / implement your corrections.
-Terry
 

{smallsort}