I'm learning the Cinder framework.
There is a class Texture
in this framework, and it can be used like this:
Texture myImage;
myImage.loadImage(/*...*/);
if(myImage)
{
// draw the image.
}
I got confused about this, because myImage
is an object. Using it as a condition doesn't make sense to me. I expected something like myImage.exist();
. So I stepped through the code, and it turns out that Texture
class has a conversion operator defined:
public:
//@{
//! Emulates shared_ptr-like behavior
typedef std::shared_ptr<Obj> Texture::*unspecified_bool_type;
// What is this???
operator unspecified_bool_type() const { return ( mObj.get() == 0 ) ? 0 : &Texture::mObj; }
void reset() { mObj.reset(); }
//@}
Obj is defined as:
protected:
struct Obj {
Obj() : mWidth( -1 ), mHeight( -1 ), mCleanWidth( -1 ), mCleanHeight( -1 ), mInternalFormat( -1 ), mTextureID( 0 ), mFlipped( false ), mDeallocatorFunc( 0 ) {}
Obj( int aWidth, int aHeight ) : mInternalFormat( -1 ), mWidth( aWidth ), mHeight( aHeight ), mCleanWidth( aWidth ), mCleanHeight( aHeight ), mFlipped( false ), mTextureID( 0 ), mDeallocatorFunc( 0 ) {}
~Obj();
mutable GLint mWidth, mHeight, mCleanWidth, mCleanHeight;
float mMaxU, mMaxV;
mutable GLint mInternalFormat;
GLenum mTarget;
GLuint mTextureID;
bool mDoNotDispose;
bool mFlipped;
void (*mDeallocatorFunc)(void *refcon);
void *mDeallocatorRefcon;
};
std::shared_ptr<Obj> mObj;
I know that operator int() const
can implictly change the Object to int, but how is unspecified_bool_type working? The debugger stops at operator unspecified_bool_type() const { return ( mObj.get() == 0 ) ? 0 : &Texture::mObj; }
when if(myImage)
is executing.
And I may be a little confused about the grammar here, what does
typedef std::shared_ptr<Obj> Texture::*unspecified_bool_type;
mean?
And does
void (*mDeallocatorFunc)(void *refcon);
in Obj mean that mDeallocatorFunc is a member of Class Obj, a function pointer to a function with prototype: void xxx(void *)
?
This is the safe bool idiom. It doesn't use simply
operator bool()
because implicit conversions can cause all kinds of trouble with that operator. So instead it uses a type that is implicitly convertible tobool
(like a pointer to member) and that is the least dangerous possible.Luckily this sort of hack is not required in C++11 because we can write
explicit operator bool
instead and not fall prey to implicit conversions.