Archived

This topic is now archived and is closed to further replies.

johnnyBravo

Is it possible to make a class accept value null?

Recommended Posts

Ive got an Image class for directx that loads textures. and ive got a drawVertex function that requires you enter an image from the image class. But sometimes if you don''t want an image i want it so you can make it equal null. how would i do this? eg
//image class

class Image {
public:
	LPDIRECT3DTEXTURE9 lp_Texture;
	int width;
	int height;
	void init(char filename[], DWORD colourKey);

	~Image()
	{
		if(lp_Texture != NULL )
			lp_Texture->Release();
		
void Image::init(char filename[], DWORD colourKey=0)
{
	lp_Texture=NULL;
	D3DXIMAGE_INFO fileInfo={NULL};
	D3DXGetImageInfoFromFile(filename,&fileInfo);
	width =fileInfo.Width;
	height=fileInfo.Height;
	D3DXIMAGE_INFO d3dxImageInfo;
	D3DXCreateTextureFromFileEx( D3D::lp_Device, filename, fileInfo.Width, fileInfo.Height, 1, D3DPOOL_DEFAULT, D3DFMT_UNKNOWN, D3DPOOL_DEFAULT, D3DX_DEFAULT, D3DX_DEFAULT, colourKey, &d3dxImageInfo, NULL, &lp_Texture );
	}

//draw vertex function

static void drawVertice(enum _D3DPRIMITIVETYPE type, int index, int triangles,	Image &image )
	{
		lp_Device->SetTexture( 0, image.lp_Texture );
		lp_Device->DrawPrimitive( type, index, triangles );
	}

//running drawvertex, i want to enter NULL for the class instead of an image class, so it knows there is no image to use

D3D::drawVertice(D3DPT_TRIANGLESTRIP,0,1,NULL);
thanks,

Share this post


Link to post
Share on other sites
Just check if the image pointer is valid. ie: check if it''s not 0, becuase NULL is actually just a define for 0 in C++.


static void drawVertice(enum _D3DPRIMITIVETYPE type, int index, int triangles, Image *image )
{
if( image )
lp_Device->SetTexture( 0, image.lp_Texture );
lp_Device->DrawPrimitive( type, index, triangles );
}



:::: [ Triple Buffer ] ::::

Share this post


Link to post
Share on other sites
when i have "Image *image" instead of "Image &image i get the error
quote:

error C2228: left of ''.lp_Texture'' must have class/struct/union type



and if i try put null in while ive got "Image &image"
i get the error
quote:

error C2664: ''drawVertice'' : cannot convert parameter 4 from ''const int'' to ''class graphic::Image &''
A reference that is not to ''const'' cannot be bound to a non-lvalue



so what to do?

Share this post


Link to post
Share on other sites
quote:
Original post by johnnyBravo
when i have "Image *image" instead of "Image &image i get the error
quote:

error C2228: left of ''.lp_Texture'' must have class/struct/union type





use image->lp_Texture

Share this post


Link to post
Share on other sites
ah thanks,

just a quick question, is there any advantage or disadvantage of using pointers instead of references in functions apart from being able directly edit or whatever the referenced thing? The only reasion i orginally used references was so i didnt have to type "&" infront of every thing.

Share this post


Link to post
Share on other sites
quote:
Original post by johnnyBravo
Ive got an Image class for directx that loads textures.

and ive got a drawVertex function that requires you enter an image from the image class.

But sometimes if you don''t want an image i want it so you can make it equal null.

how would i do this?

You could possibly use the Null Object Idiom in conjunction with function overloading. For example:


#include <iostream>

class Image
{
};

class Null
{
} nil;

void foo(int, int, int, const Image&)
{
std::cout << "foo with Image\n";
}

void foo(int, int, int, const Null&)
{
std::cout << "foo with Null\n";
}

int main()
{
foo(1,2,3, Image());
foo(1,2,3, nil);
}


Here, you create a globally-scoped entity called "nil" of a type distinct from all others (in this case Null) and you use it for overloading the function. This simulates how languages such as Python and Lisp work, and is arguably more sane than the built-in way in which C++ deals with null (where it has no distinct type of its own).

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
quote:
Original post by SabreMan
You could possibly use the Null Object Idiom in conjunction with function overloading. For example:
&lt;source code&gt;
Here, you create a globally-scoped entity called "nil" of a type distinct from all others (in this case Null) and you use it for overloading the function. This simulates how languages such as Python and Lisp work, and is arguably more sane than the built-in way in which C++ deals with null (where it has no distinct type of its own).

Is there an advantage to this as opposed to overloading the function with one less argument, eg:

class Image
{
};
void foo(int, int, int, const Image&)
{
// draw with image

}

void foo(int, int, int)
{
// draw without image

}

int main()
{
foo(1,2,3, Image());
foo(1,2,3);
}

To the OP:
In fact, in this particular case, I would expect the use of a pointer, with NULL checks within the function would be better, since it would a) let you pass in a null pointer without the caller knowing that it''s null, and b) mean you don''t have to duplicate code between two versions of the function (nb: of course, duplication could be reduced or eliminated even if you choose to overload the function, by breaking it down into separate sub-functions, but depending on what exactly you need to do in the overloaded function, this may result in less readable code).

John B
[gdnick: JohnBSmall, but I''m at school and don''t wish to log in]

Share this post


Link to post
Share on other sites
quote:
Original post by Anonymous Poster
Is there an advantage to this as opposed to overloading the function with one less argument

Maybe, maybe not. It really depends on how the OP wants to call the function, which is not something for me to decide.
quote:

In fact, in this particular case, I would expect the use of a pointer, with NULL checks within the function would be better

A way of extending the Null Object idea might be to derive an hierarchy for Images, where the necessary logic is polymorphic across different types of Image. The hierarchy could then include a NullImage type and the call-site could remain ignorant of the concrete type passed through. There''s actually other problems in the OP''s code that I didn''t comment on, such as lack of encapsulation and cohesiveness. Resolution of these problems would probably result in the use of polymorphic despatch dropping out as the natural solution.
quote:

it would a) let you pass in a null pointer without the caller knowing that it''s null

This really depends on the creation policy for Image types.
quote:

b) mean you don''t have to duplicate code between two versions of the function

This is a two-way argument. If the code is switching on the (conceptual) type of something, then it really contains two different implementations of something. Whether you perceive that as a problem depends on your coding ethics, I suppose. I''ve grown very suspicious of explicit type-switches. The only reason this is not perceived as a type-switch in C++ is because C++ handles nulls in an incredibly stupid way. Its still conceptually a type-switch.

Share this post


Link to post
Share on other sites
quote:
Original post by SabreMan
quote:
Original post by me
Is there an advantage to this as opposed to overloading the function with one less argument

Maybe, maybe not. It really depends on how the OP wants to call the function, which is not something for me to decide.

That''s true of course, but ignoring the OP''s specific problem for a moment, is there a situation where using a null parameter instead of no parameter at all would give an advantage (other than simple stylistic preference)?
quote:
A way of extending the Null Object idea might be to derive an hierarchy for Images, where the necessary logic is polymorphic across different types of Image. The hierarchy could then include a NullImage type and the call-site could remain ignorant of the concrete type passed through. There''s actually other problems in the OP''s code that I didn''t comment on, such as lack of encapsulation and cohesiveness. Resolution of these problems would probably result in the use of polymorphic despatch dropping out as the natural solution.

Perfectly true. Do you believe that such class hierarchies should be used even if they result in a very simple (but, I suspect, not uncommon) trio of classes?
ie, abstract base class defining interface, single real concrete class implementing interface, and a concrete null class implementing everything (or nearly everything) with empty functions.
quote:
quote:
it would a) let you pass in a null pointer without the caller knowing that it''s null

This really depends on the creation policy for Image types.

True of course.
quote:
If the code is switching on the (conceptual) type of something, then it really contains two different implementations of something. Whether you perceive that as a problem depends on your coding ethics, I suppose. I''ve grown very suspicious of explicit type-switches. The only reason this is not perceived as a type-switch in C++ is because C++ handles nulls in an incredibly stupid way. Its still conceptually a type-switch.

True. However, it is (again, I suspect - I can''t provide statistics) common to have a situation in which a type-switch is a matter of running through the full code path for one type, but excluding part of that code path for another type (generally, the null type). Do you believe that it is still bad practice if the type-switches consist of having blocks of the function code wrapped up in if statements? And if so, what do you think is the best way of organising the code in this case?
// by breaking the function down?  something like:

void foo(int a, int b, const Object* c)
{
// code block 1

if (c != 0)
{
// code block 2

}
// block 3

if (c != 0)
// block 4 (single statement block)

// block 5

}

// Should foo be broken up? Becoming:

void foo(int a, int b, const Object& c)
{
block_1_code();
block_2_code(a, c);
block_3_code(b);
block_4_code(c);
block_5_code();
}

void foo(int a, int b, const Null& c)
{
block_1_code();
block_3_code(b);
block_5_code();
}

// or perhaps an attempt should be made to remove all type

// specific code from functions that aren''t members of the

// type they''re operating with, so that abstract interfaces

// can be used to remove all explicit type checks

// eg,

void foo(int a, int b, const Object* c)
{
// code block 1

if (c != 0)
{
c->bar(a);
}
// block 3

if (c != 0)
c->do_some_stuff();
// block 5

}
// where Object has become an abstract class, and foo() is

// given a pointer to an instance of a concrete implementation

// of the Object interface


John B

Share this post


Link to post
Share on other sites
quote:
Original post by JohnBSmall
That's true of course, but ignoring the OP's specific problem for a moment, is there a situation where using a null parameter instead of no parameter at all would give an advantage (other than simple stylistic preference)?

As a completely distinct type (as opposed to subtype) it can merely give the advantage of explicitly signifying null-ness, which is probably no more than a simple stylistic preference.
quote:

Perfectly true. Do you believe that such class hierarchies should be used even if they result in a very simple (but, I suspect, not uncommon) trio of classes?

Yes, if the decision to do so is balanced with a simplification of code that uses these classes (client code). Certainly, if you are likely to write code which switches on the null-ness of a value in various different places, then spending the time to create a null subtype could be time well spent. Even if there aren't that many places, it shouldn't take long to implement a reasonably sane null subtype that results in greater clarity of code. I'm loathe to refer to the idiocy of code-complexity metrics, but I do believe that the more explicit branches in the code, the more complex the code will generally be.
quote:

True. However, it is (again, I suspect - I can't provide statistics) common to have a situation in which a type-switch is a matter of running through the full code path for one type, but excluding part of that code path for another type (generally, the null type). Do you believe that it is still bad practice if the type-switches consist of having blocks of the function code wrapped up in if statements?

I'm not going to make a generalisation one way or another. For the OP's purposes, he needs to review the suggested solutions and weigh up which one is the best for his particular context. He might choose a non-optimal solution through lack of experience, but if he's been paying attention he might realise another solution could have been better and use that knowledge in the future. For the general case, I can only say "it depends".

Something that could affect the decision would be where the "null" object originates from. For instance, if you're already using a factory method, a null object could be a very good idea for keeping client code simple.
quote:

And if so, what do you think is the best way of organising the code in this case?

[...]

Its all a bit hypothetical, but what I'm driving at is something like this:

void foo(int a, int b, const Object& c)
{
c.bar(a);
c.do_some_stuff();
}

i.e. the body of "foo" does not contain implementational logic.

A further consideration which comes into this is my belief that using integral zero as the null pointer constant was a bad decision.


[edited by - SabreMan on November 18, 2003 1:35:59 PM]

Share this post


Link to post
Share on other sites
quote:
Original post by SabreMan
Its all a bit hypothetical, but what I'm driving at is something like this:
...
i.e. the body of "foo" does not contain implementational logic.

Actually, that's what I intended to write in my second example (possibly with a pointer instead of reference, although that's inconsequential). Unfortunately, I copy-pasted the code from the original function call, replaced the code blocks with calls to member functions, but (being my absent-minded self) forgot to remove the null checks.
Yet another reason why copy-pasting is a bad idea: it makes it very easy to change just one thing without changing something that's related.

Edit: Looking at it again, I may have misinterpretted your code. It depends on what you mean by "does not contain implementational logic", or on whether you deliberately left out the other commented code blocks. My assumption was that the other code blocks were implied, and that the important point was simply the lack of null checks. If that is an incorrect assumption, then, having taken the implementational logic out of foo(), where would you put it?
I admit the point is somewhat moot really, since as you said (or at least implied), it will depend on the specific situation, but even so, I would be interested in any comments.
quote:
A further consideration which comes into this is my belief that using integral zero as the null pointer constant was a bad decision.

Yes. My habits have fluctuated somewhat in this regard over the (relatively short) time that I've been programming in C++. I started with nothing (just using a construct of if (some_pointer) { ... }, or if (!some_pointer) { ... }). At some point I migrated to explicitly checking against NULL, and some time after that, I started to use explicit checks against 0. If I think about it, then I can see that in terms of code clarity, an explicit check against NULL may be best, but I can't honestly say I follow that rule.

[other points ignored, since I can't think of anything to ask about them ]

John B

[edited by - JohnBSmall on November 18, 2003 2:23:49 PM]

Share this post


Link to post
Share on other sites