www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Check the class of objects

reply Mael <mael.primet gmail.com> writes:
Hello,

another newb question !
I'm trying to design a large image library, that should be usable enough for
students to learn it quickly, but powerful enough to be used in medium-sized
projects

Since I'm building several image classes (depending on the characteristics of
the image, such as grey / color, byte / float, etc), I can either include a
flag in each image that tells its class ("I'm a color-byte image", "I'm a
grey-byte image", etc.), or use many if( cast(ColorByteImage)img !is null ) {
doactionColorByte ; } to check the type. What would be the most efficient way
to test the type of the class ? How is the cast(Class)v construct implemented
in the compiler (I guess there's some equivalent of a "i'm a color-byte image"
flag in the class structure) ?

Another question for people designing graphic libraries : would you recommend
to pad image rows on 32-bits ? I hardly see when this is useful in practice
(usually it's done for efficiency reasons, but I don't see how this would
improve speed significantly, except if you're trying to extract a couple of
lines of an image...). The only advantage I see is that it permits to make fast
'ImageViews' of a subset of an Image without having to duplicate it. Maybe some
webcams give padded output, and it would therefore be faster to have them
output directly in the image ? Someone sees the main reason (I might be missing
some thing here?) 
Jun 05 2008
next sibling parent Urban Hafner <urban bettong.net> writes:
Mael wrote:
 Since I'm building several image classes (depending on the characteristics of
the image, such as grey / color, byte / float, etc), I can either include a
flag in each image that tells its class ("I'm a color-byte image", "I'm a
grey-byte image", etc.), or use many if( cast(ColorByteImage)img !is null ) {
doactionColorByte ; } to check the type. What would be the most efficient way
to test the type of the class ? How is the cast(Class)v construct implemented
in the compiler (I guess there's some equivalent of a "i'm a color-byte image"
flag in the class structure) ?
I think you should have each class report it's "class". This way it's easy to add new subclass to your Image class. If you do it in the Image class (your second idea) you would have to change the Image class every time you add a new subclass! This is (IMHO) bad design as the parent class should only contain stuff that's the same for all subclasses. After all, if that's not the case, what's the point of having subclasses at all? Urban
Jun 05 2008
prev sibling next sibling parent reply janderson <askme me.com> writes:
Mael wrote:
 Hello,
 
 another newb question ! I'm trying to design a large image library,
 that should be usable enough for students to learn it quickly, but
 powerful enough to be used in medium-sized projects
 
 Since I'm building several image classes (depending on the
 characteristics of the image, such as grey / color, byte / float,
 etc), I can either include a flag in each image that tells its class
 ("I'm a color-byte image", "I'm a grey-byte image", etc.), or use
 many if( cast(ColorByteImage)img !is null ) { doactionColorByte ; }
 to check the type. What would be the most efficient way to test the
 type of the class ? How is the cast(Class)v construct implemented in
 the compiler (I guess there's some equivalent of a "i'm a color-byte
 image" flag in the class structure) ?
I don't like either approaches. RTTI should be looked at with a suspicions eye in my opinion (ie has its uses but the this is not one of them). Efficiency wise, dynamic casts can be slow depending on how the compiler implements them it may need to traverse each item in the tree. So the deeper the tree (another thing you should avoid) the worse the performance. enum/flag compares are pretty efficient (as long as they aren't strings). Polymorphism's bread and butter is this type of problem. Virtual lookups aren't that much worse when compared to enum flag. Its 2 memory lookups and 1 jump. Look at the surrounding code around the virtual, how much work is that doing comparatively? Probably a whole lot more. I wouldn't prematurely optimize this sort of thing.
 
 Another question for people designing graphic libraries : would you
 recommend to pad image rows on 32-bits ? I hardly see when this is
 useful in practice (usually it's done for efficiency reasons, but I
 don't see how this would improve speed significantly, except if
 you're trying to extract a couple of lines of an image...). The only
 advantage I see is that it permits to make fast 'ImageViews' of a
 subset of an Image without having to duplicate it. Maybe some webcams
 give padded output, and it would therefore be faster to have them
 output directly in the image ? Someone sees the main reason (I might
 be missing some thing here?)
 
I'm not exactly sure what sort of padding your talking about so I'll take a stab at what I think you mean. I think you more or less say this but I'll clarify it a bit. Its important because of how data is aligned. If the data is not aligned the compiler will have to do extra work to realign the data before it can do work on it. Or alternatively the software will work on each byte individually rather then working on the whole 32-bits at once (or even several 32-bits at the same time). That's of course on a 32-bit machine, on a 64-bit machine you might want to align to 64-bits in some cases. It can make a big difference to performance since there are 1000s X 1000s of pixels in an image. I hope that was helpful. -Joel
Jun 05 2008
next sibling parent janderson <askme me.com> writes:
janderson wrote:
 Mael wrote:
 Hello,

 another newb question ! I'm trying to design a large image library,
 that should be usable enough for students to learn it quickly, but
 powerful enough to be used in medium-sized projects

 Since I'm building several image classes (depending on the
 characteristics of the image, such as grey / color, byte / float,
 etc), I can either include a flag in each image that tells its class
 ("I'm a color-byte image", "I'm a grey-byte image", etc.), or use
 many if( cast(ColorByteImage)img !is null ) { doactionColorByte ; }
 to check the type. What would be the most efficient way to test the
 type of the class ? How is the cast(Class)v construct implemented in
 the compiler (I guess there's some equivalent of a "i'm a color-byte
 image" flag in the class structure) ?
I don't like either approaches. RTTI should be looked at with a suspicions eye in my opinion (ie has its uses but the this is not one of them). Efficiency wise, dynamic casts can be slow depending on how the compiler implements them it may need to traverse each item in the tree. So the deeper the tree (another thing you should avoid) the worse the performance. enum/flag compares are pretty efficient (as long as they aren't strings). Polymorphism's bread and butter is this type of problem. Virtual lookups aren't that much worse when compared to enum flag. Its 2 memory lookups and 1 jump. Look at the surrounding code around the virtual, how much work is that doing comparatively? Probably a whole lot more. I wouldn't prematurely optimize this sort of thing.
BTW: let me clarify what I mean by polymorphism. I'm not talking about the class return from a function its type like bool isGray(); That's almost RTTI. No, what I mean is moving the code that does stuff on isGray() into the image class, if it makes sense. Note, depending on what your doing it may be better to use a flag, or "is function" instead of polymorphism but I'd need an example. -Joel
Jun 05 2008
prev sibling parent Mael <mael.primet gmail.com> writes:
 Its important because of how data is aligned.  If the data is not 
Yes, it's obviously linked, but the question is : since each time you want to process an RGB triple in the middle of any row, it won't be necessarily aligned, I don't clearly see where do you save processing time when doing for instance a constant addition to an image ? I guess there must be reasons linked to image viewing (like screens that like to have their data padded), but I could not find precise references on this
Jun 05 2008
prev sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Mael" wrote
 Hello,

 another newb question !
 I'm trying to design a large image library, that should be usable enough 
 for students to learn it quickly, but powerful enough to be used in 
 medium-sized projects

 Since I'm building several image classes (depending on the characteristics 
 of the image, such as grey / color, byte / float, etc), I can either 
 include a flag in each image that tells its class ("I'm a color-byte 
 image", "I'm a grey-byte image", etc.), or use many if( 
 cast(ColorByteImage)img !is null ) { doactionColorByte ; } to check the 
 type. What would be the most efficient way to test the type of the class ? 
 How is the cast(Class)v construct implemented in the compiler (I guess 
 there's some equivalent of a "i'm a color-byte image" flag in the class 
 structure) ?
The correct way is to use the if(cast(ColorByteImage)img) if you have a single attribute you want to switch on. This is implemented in the compiler as a check to make sure the class is of the valid type, and then return the casted value if it is correct, or return null if it is not. You can also get information about the most derived class at runtime using the .classinfo property, but if you have a deep hierarchy, it is less code to just do the cast test. If you want to switch on multiple orthogonal attributes, it would be better to provide properties in the class that indicate which ones are which. For example, the properties grey/color and byte/float could be orthogonal producing 4 different variations. If you only care if an image is grey or color, it would be inefficient to do something like: if(cast(ColorByteImage)img !is null || cast(ColorFloatImage)img !is null) if you could just do: if(img.isColor) -Steve
Jun 05 2008
parent reply Mael <mael.primet gmail.com> writes:
Well, most of the time, the check won't occur a big penalty, because, first the
image hierarchy will be small, and second, the pattern of use will be like :

algorithmprocess(Type)(args)
{
 static switch(Type) dostuff ;
}

algorithm(args)
{
if( isByteImage(img) ) algorithmprocess(ByteImage)(cast(ByteImage)img) ;
...
}

which is a bit ugly but should work.. except I can't make it work :

I did something like

_myalgo( T )( T u )
{
static if (T == ByteImage) { do something ; }
else static if( T == FloatImage) { ..; }
}

myalgo( Image u )
{
if( isByteImage(u) ) _myalgo( ByteImage )(cast(ByteImage)u) ;
 ...
}


and I obtain something like
Error: cannot evaluate opEquals(cast(Object)(ByteImage)) at compile time
(refering to the line "static if( T == ByteImage ) .."

someone knows what is the correct way to check for the arg types?

sorry to ask newb question .. 
  
Jun 05 2008
parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Mael" wrote
 Well, most of the time, the check won't occur a big penalty, because, 
 first the image hierarchy will be small, and second, the pattern of use 
 will be like :

 algorithmprocess(Type)(args)
 {
 static switch(Type) dostuff ;
 }

 algorithm(args)
 {
 if( isByteImage(img) ) algorithmprocess(ByteImage)(cast(ByteImage)img) ;
 ...
 }

 which is a bit ugly but should work.. except I can't make it work :

 I did something like

 _myalgo( T )( T u )
 {
 static if (T == ByteImage) { do something ; }
 else static if( T == FloatImage) { ..; }
 }

 myalgo( Image u )
 {
 if( isByteImage(u) ) _myalgo( ByteImage )(cast(ByteImage)u) ;
 ...
 }


 and I obtain something like
 Error: cannot evaluate opEquals(cast(Object)(ByteImage)) at compile time
 (refering to the line "static if( T == ByteImage ) .."

 someone knows what is the correct way to check for the arg types?
Static if can only inspect compile-time objects. This will not look up the derived type. If you did want static if to work, you need to use an is expression. Something like: static if(is(T == ByteImage)) But you are unnecessarily distributing your code, adding unnecessary templates, making it harder to understand/maintain What's wrong with: if(isByteImage(u)) { do something; } ... Either way, you still need the if-else block to determine what the image is at runtime. -Steve
Jun 05 2008
parent reply Mael <mael.primet gmail.com> writes:
 What's wrong with:
 
 if(isByteImage(u))
 { do something; }
 ...
 
 Either way, you still need the if-else block to determine what the image is 
 at runtime.
Well, actually the point is that the action "do something" is consistent accross images except when trying to access data (compare bytes/floats/value/triples), and using a run time if inside the loops would be unefficient, that's why I want to only use static if in the loop, but still share most of the code since it's truly redundant
Jun 05 2008
next sibling parent reply Chris Wright <dhasenan gmail.com> writes:
Mael wrote:
 What's wrong with:

 if(isByteImage(u))
 { do something; }
 ...

 Either way, you still need the if-else block to determine what the image is 
 at runtime.
Well, actually the point is that the action "do something" is consistent accross images except when trying to access data (compare bytes/floats/value/triples), and using a run time if inside the loops would be unefficient, that's why I want to only use static if in the loop, but still share most of the code since it's truly redundant
Why not use the Visitor pattern?
Jun 06 2008
parent janderson <askme me.com> writes:
Chris Wright wrote:
 Mael wrote:
 What's wrong with:

 if(isByteImage(u))
 { do something; }
 ...

 Either way, you still need the if-else block to determine what the 
 image is at runtime.
Well, actually the point is that the action "do something" is consistent accross images except when trying to access data (compare bytes/floats/value/triples), and using a run time if inside the loops would be unefficient, that's why I want to only use static if in the loop, but still share most of the code since it's truly redundant
Why not use the Visitor pattern?
This may be a good idea, however I wonder how you'd construct visitor that does the right conversion since you can't have virtual template inheritance. I guess you could write up a function for each version. -Joel
Jun 06 2008
prev sibling parent reply janderson <askme me.com> writes:
Mael wrote:
 What's wrong with:

 if(isByteImage(u))
 { do something; }
 ...

 Either way, you still need the if-else block to determine what the image is 
 at runtime.
Well, actually the point is that the action "do something" is consistent accross images except when trying to access data (compare bytes/floats/value/triples), and using a run time if inside the loops would be unefficient, that's why I want to only use static if in the loop, but still share most of the code since it's truly redundant
I still think you should do this instead: interface Image { void DoSomething1(); } class ByteImage : Image { override void DoSomething1() { //do something; } } class ByteImage : Image { override void DoSomething1() { //do something else; } } ... Use case Image image = new ByteImage(); ... image.DoSomething(); Using a switch statement like you have is very poor design in my book. The great thing about doing it this way is you have no coupling to a switch statement. Someone wants to create a new type of Image they can just by inheriting from your interface. They don't need to update a whole heap of switch statements (which they may not even have access to if you used a lib.) Its becomes more like a plug-in architecture. Sorry to come across as harsh here but if your going to show this to students you should show them the right way to do such stuff. This is called the adapter pattern BTW. Wiiiiiiiiiiiiiiiiiii, -Joel
Jun 06 2008
parent reply janderson <askme me.com> writes:
janderson wrote:
 Mael wrote:
 What's wrong with:

 if(isByteImage(u))
 { do something; }
 ...

 Either way, you still need the if-else block to determine what the 
 image is at runtime.
Well, actually the point is that the action "do something" is consistent accross images except when trying to access data (compare bytes/floats/value/triples), and using a run time if inside the loops would be unefficient, that's why I want to only use static if in the loop, but still share most of the code since it's truly redundant
I still think you should do this instead: interface Image { void DoSomething1(); } class ByteImage : Image { override void DoSomething1() { //do something; } } class ByteImage : Image { override void DoSomething1() { //do something else; } } ... Use case Image image = new ByteImage(); ... image.DoSomething(); Using a switch statement like you have is very poor design in my book. The great thing about doing it this way is you have no coupling to a switch statement. Someone wants to create a new type of Image they can just by inheriting from your interface. They don't need to update a whole heap of switch statements (which they may not even have access to if you used a lib.) Its becomes more like a plug-in architecture. Sorry to come across as harsh here but if your going to show this to students you should show them the right way to do such stuff. This is called the adapter pattern BTW. Wiiiiiiiiiiiiiiiiiii, -Joel
Oh the other piece of the puzzle in your case since your doing the same thing for each case is a template. void DoStuffTemplate(T)(T obj) { //Do nasty stuff to obj (no cast required since is the given type) } class ByteImage : Image { override void DoSomething1() { DoStuffTemplate(this); //Note, can't be in base class } } class ByteImage : Image { override void DoSomething1() { DoStuffTemplate(this); //Note, can't be in base class } } Also if you need to special case something you have 2 options, template spealization or changing the contents of DoSomething1(). -Joel
Jun 06 2008
parent reply mael <mael.primet gmail.com> writes:
Actually, this wouldn't be the right solution,
because the doSomething action is specific to a particular algorithm, and there
will be tens of such algorithms, and I don't want to clutter the main class
with parts of algorithms adapted to each class...
 
Jun 07 2008
parent reply janderson <askme me.com> writes:
mael wrote:
 Actually, this wouldn't be the right solution,
 because the doSomething action is specific to a particular algorithm, and
there will be tens of such algorithms, and I don't want to clutter the main
class with parts of algorithms adapted to each class...
  
http://c2.com/cgi/wiki?ReplaceConditionalWithPolymorphism http://c2.com/cgi/wiki?SwitchStatementsSmell The following is an excerpt from Scott Meyers’ new book, Effective C++, Third Edition: 55 Specific Ways to Improve Your Programs and Designs. "Effective C++, Third Edition Item 27 7 One thing you definitely want to avoid is designs that involve cascading dynamic_casts, i.e., anything that looks like this: class Window { ... }; ... // derived classes are defined here typedef std::vector<std::tr1::shared_ptr<Window> > VPW; VPW winPtrs; ... for (VPW::iterator iter = winPtrs.begin(); iter != winPtrs.end(); ++iter) { if (SpecialWindow1 *psw1 = dynamic_cast<SpecialWindow1*>(iter->get())) { ... } else if (SpecialWindow2 *psw2 = dynamic_cast<SpecialWindow2*>(iter->get())) { ... } else if (SpecialWindow3 *psw3 = dynamic_cast<SpecialWindow3*>(iter->get())) { ... } ... } Such C++ generates code that’s big and slow, plus it’s brittle, because every time the Window class hierarchy changes, all such code has to be examined to see if it needs to be updated. (For example, if a new derived class gets added, a new conditional branch probably needs to be added to the above cascade.) Code that looks like this should almost always be replaced with" I think that only becomes appropriate if you use a template or operator overloads to generate this so its generic, less brittle and without evil down casting. -Joel
Jun 07 2008
parent janderson <askme me.com> writes:
janderson wrote:
 mael wrote:
 Actually, this wouldn't be the right solution,
 because the doSomething action is specific to a particular algorithm, 
 and there will be tens of such algorithms, and I don't want to clutter 
 the main class with parts of algorithms adapted to each class...
  
http://c2.com/cgi/wiki?ReplaceConditionalWithPolymorphism http://c2.com/cgi/wiki?SwitchStatementsSmell The following is an excerpt from Scott Meyers’ new book, Effective C++, Third Edition: 55 Specific Ways to Improve Your Programs and Designs. "Effective C++, Third Edition Item 27 7 One thing you definitely want to avoid is designs that involve cascading dynamic_casts, i.e., anything that looks like this: class Window { ... }; ... // derived classes are defined here typedef std::vector<std::tr1::shared_ptr<Window> > VPW; VPW winPtrs; ... for (VPW::iterator iter = winPtrs.begin(); iter != winPtrs.end(); ++iter) { if (SpecialWindow1 *psw1 = dynamic_cast<SpecialWindow1*>(iter->get())) { ... } else if (SpecialWindow2 *psw2 = dynamic_cast<SpecialWindow2*>(iter->get())) { ... } else if (SpecialWindow3 *psw3 = dynamic_cast<SpecialWindow3*>(iter->get())) { ... } ... } Such C++ generates code that’s big and slow, plus it’s brittle, because every time the Window class hierarchy changes, all such code has to be examined to see if it needs to be updated. (For example, if a new derived class gets added, a new conditional branch probably needs to be added to the above cascade.) Code that looks like this should almost always be replaced with"
Sorry part of that got cut off. It says something like "replaced with virtual functions".
 
 
 I think that only becomes appropriate if you use a template or operator 
 overloads to generate this so its generic, less brittle and without evil 
 down casting.
 
 -Joel
Jun 07 2008