Sign in to follow this  

C++, ' ** ' and Graphics Engines...

This topic is 4399 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I know this is a bit cheeky of me, putting this all into one thread, but I need help with a couple of broad subjects. Firstly, having come from a (shakey) C programming background, I'm still not too sure about classes, and was wondering how programs and APIs such as DirectX can use a single class to create what seems to be a billion unrelated objects, along with the methods to handle them all( I've yet to find a use for the constructor\destructor methods...) secondly, I've been looking around, but I can't seem to find out what the ** operator is for(the place its most often seen is in the definition of main() in C and Cpp) Finally, I'm trying to create my own graphics engine. I not expecting much from it, and I've found an interesting article about it on www.flipcode.com and I'm following their advice, but it feels like my header/source is missing some things. here is the listing for my header;
Quote:
#ifndef __VERTEX_H #define __VERTEX_H #include <math.h> #ifndef M_PI #define M_PI 3.14159265358979323846 #endif /******************************************************************************** *List of functions and variables * ********************************************************************************* *SQR(name) * maths macro to define the square of variable 'name' * ********************************************************************************* *sine[],cosine[], * maths tables to increase speed * *tangent[] * * ********************************************************************************* *POINT,PPOINT * 3d point structures. PPOINT is the pointer version * * * * *LINE,PLINE * 3d line structure. consists of 2 POINT structures * * * * *VECTOR,PVECTOR * mathematical definition of a vector * * * * *VERTEX,PVERTEX * vertex data structure. includes position,colour and * * * texture coordinates * * * * *TRIANGLE,PTRIANGLE * defines a triangle as an array of 3 VERTEX structures * * * * *QUAD,PQUAD * defines a quad as an array of 4 VERTEX structures * * * * *POLYGON,PPOLYGON * defines a polygon with 'num_points' points, starting * * * at address 'point' * ********************************************************************************* #define SQR(name) name*name //Not too sure this'll work... enum plane_pos{BEHIND,ON,IN_FRONT} typedef enum plane_pos PLANE_POS ; typedef float[4][4] MATRIX_4x4;//4x4 matrix MATRIX_4x4 identity_matrix; MATRIX_4x4 inv_identity_matrix; MATRIX_4x4 test_matrix_1;// used during InitMathset to check for correct working of functions MATRIX_4x4 test_matrix_2;// (as above) MATRIX_4x4 add_result_matrix;// (ditto) MATRIX_4x4 concat_result_matrix;// (get the picture yet?) MATRIX_4x4 inv_result_matrix;//(no comment :) int sine[360]; int cosine[360]; int tangent[360]; typedef struct{ int x,y,z;//position int r,g,b,a;//colour and alpha }POINT,*PPOINT;//Use for particles( if poss) typedef struct{ POINT point[2];//two points make a line }LINE,*PLINE;//Use for ... ... ... something ... typedef struct{ int x,y,z;//magnitude int dirx,diry,dirz;//direction }VECTOR,*PVECTOR;// used in conjunction with one of the others, to move them in a set direction typedef struct{ int x,y,z;//position int r,g,b,a;//colour and alpha int u,v;//texture coordinates }VERTEX,*PVERTEX; typedef struct{ PVERTEX vertices[3];//the 3 vertices that make up a triangle }TRIANGLE,*PTRIANGLE; typedef struct{ PVERTEX vertices[4];//the 4 vertices that make up a quad }QUAD,*PQUAD; typedef struct{ int num_vertices;//number of vertices in quad PVERTEX vertices;//pointer to vertex list }POLYGON,*PPOLYGON; bool InitMathset(void); //call this before anything else. This can be ignored, but it will check that all of the functions work properly, //and sets up the sine,cosine and tangent tables PLANE_POS CheckPlane(POINT point,TRIANGLE triangle); //checks to see where point "point" is in comparison to plane "plane" LINE ScaleLine(PLINE line,float scale_factor); //scales a line "line" by "scale_factor" TRIANGLE ScaleTriangle(PTRIANGLE triangle,float scale_factor); //scales "triangle" by "scale_factor" QUAD ScaleQuad(PQUAD quad,float scale_factor); //scales "quad" by "scale_factor" POLYGON ScalePolygon(PPOLYGON polygon,float scale_factor); //scales "polygon" by "scale_factor" VECTOR Normalise(VECTOR vector); //Normalises the magnitude of a vector to 1.0 float GetMagnitude(VECTOR vector); // Gets the magnitude of a vector |U|=sqrt(Ux^2+Uy^2+Uz^2) float FindDotProduct(VECTOR u,VECTOR v); //uses dot product to find the angle between2 vectors theta=COS^-1*((u.x*v.x+u.y*v.y+u.z*v.z)/(GetMagnitude(u)*GetMagnitude(v))) VECTOR FindCrossProduct(VECTOR u, VECTOR v); //finds and returns the normal vector of vectors u and v <(u.y*v.z)-(u.z*v.y),(u.x*v.z)-(u.z*v.x),(u.x*v.y)-(u.y*v.x)>=<Nx,Ny,Nz> MATRIX_4x4 ConcatenateMatrix(MATRIX_4x4 mat_1,MATRIX_4x4 mat_2); //performs mat_1*mat_2 and returns the result MATRIX_4x4 AddMatrix(MATRIX_4x4 mat_1,MATRIX_4x4 mat_2); //adds mat1 to mat2 MATRIX_4x4 MultIdent(MATRIX_4x4 matrix); //multiplies the given matrix by the identity matrix. This just returns the original matrix MATRIX_4x4 AddVectorToMatrix(VECTOR vector, MATRIX_4x4 matrix); //transforms a vector into a matrix and multiplies it into the supplied matrix MATRIX_4x4 InvertMatrix(MATRIX_4x4 matrix); //multiples the given matrix by the inverse of the identity matrix #endif//__VERTEX_H
Is there anything else that I need to add mathematically, bearing in mind that I'll implement the drawing\rendering through OpenGL? Also, why can't I get the two flipping pluses to show when I'm trying to write 'C plus plus'?

Share this post


Link to post
Share on other sites
Quote:
Original post by webwraith
secondly, I've been looking around, but I can't seem to find out what the ** operator is for(the place its most often seen is in the definition of main() in C and Cpp)
As you already know, using a * in a declaration makes the type a pointer. ** is simply a pointer-to-a-pointer; in fact, you could if you wanted to, go crazy and have a pointer-to-a-pointer-to-a-pointer-to-a-pointer-to-a-pointer.

I imagine you've seen it in main as: char** argv
this is equivalent to char* argv[], or char argv[][].

Share this post


Link to post
Share on other sites
Quote:
Original post by webwraith
I know this is a bit cheeky of me, putting this all into one thread, but I need help with a couple of broad subjects. Firstly, having come from a (shakey) C programming background, I'm still not too sure about classes, and was wondering how programs and APIs such as DirectX can use a single class to create what seems to be a billion unrelated objects, along with the methods to handle them all( I've yet to find a use for the constructor\destructor methods...)


If you're talking about what I think you are, it's rather simple:

struct Manager {
public:
ObjectA * CreateObjectA( void ) {
return new ObjectA;
}
void DestroyObjectA( ObjectA * object ) {
delete object;
}
ObjectB * CreateObjectB( void ) {
return new ObjectB;
}
void DestroyObjectB( ObjectB * object ) {
delete object;
}
};


Quote:
secondly, I've been looking around, but I can't seem to find out what the ** operator is for(the place its most often seen is in the definition of main() in C and Cpp)


That's not an operator, that's a pointer to a pointer. It's often used as the second parameter of main (argv) like so:

int main( int argc , char ** argv );

argv is layed out in a specific manner:

It is a pointer to the beginning of an array.
This array is an array of pointers to the beginnings of arrays of characters (strings).

Thus, if we were to use STL containers and objects (presuming it were legal in this context), it's quite similar to:

std::vector< std::string > argv;

The main difference being they use C style arrays:

std::string * argv; or std::string argv[];

And C style strings:

(char *) * argv; or (char *) argv[];

Quote:
Finally, I'm trying to create my own graphics engine. I not expecting much from it, and I've found an interesting article about it on www.flipcode.com
and I'm following their advice, but it feels like my header/source is missing some things.

here is the listing for my header;

*snip*

Is there anything else that I need to add mathematically, bearing in mind that I'll implement the drawing\rendering through OpenGL?


If there is, you'll find out when you run accross it. And then you can add it. See YAGNI.

Quote:
Also, why can't I get the two flipping pluses to show when I'm trying to write 'C plus plus'?


The forum PMSes like that. I don't know why either.

Share this post


Link to post
Share on other sites
When it came to the APIs, I was speaking generally, I know when to use the constructor\destructor methods, I've just never needed them before(the fact that I've never managed to program a functional class before, may have something to do with that...).And I tried to implement a class once, but it was basically a mess, as I tend to need someone going through these things with me step-by-step with actual uses for the classes( in particular, my DirectX book is sitting on the shelf, gathering dust until I can find some simpler examples of good classes)

Thanks to the both of you for the 'pointer to a pointer' part, but I've got another question for you about it now;

what's the point? (pun not intended)

why not use a single pointer to whatever data you want to retrieve?

In the meantime, I'll keep an eye out for those elusive math functions... ;)

Share this post


Link to post
Share on other sites
Quote:
Original post by webwraith
Thanks to the both of you for the 'pointer to a pointer' part, but I've got another question for you about it now;

what's the point? (pun not intended)

why not use a single pointer to whatever data you want to retrieve?


Because argv (which is a list of arguments given to the program) is an array of C-strings (which are char arrays). Remember, arrays are basically pointers. If it was a pointer to a char, it would be more difficult to know where one argument ended and another began.

Share this post


Link to post
Share on other sites
If the data you want to retrieve is a pointer (say, a pointer to an interface of some sort), then you need a pointer to a pointer to actually retrieve it. (Or, alternately, a reference to a pointer)

Note that when you say CreateVertexBuffer() in DirectX, that will (at least conceptually) create a CVertexBuffer object internally; initialize it as necessary; and then return a pointer to the actual object (through the pointer-to-pointer interface).

When you call Release() on the interface, it will decrement the refcount, and if it reaches 0, call delete on itself, which will call the destructor, which will clean up any data referenced by the object.

Share this post


Link to post
Share on other sites
Quote:

#define SQR(name) name*name //Not too sure this'll work...


SQR(x+1) will expand to: x+1*x+1, which equals x+x+1. Definitely not what you wanted.

Quote:

#define SQR(name) (name)*(name)

But even better would be:


template <class T>
inline T sqr(const T& x)
{
return x*x;
}



In case you're unfamiliar with templates...the above code will create a function sqr(x) for any type that has a defined * operator. The performance will be the same as a #define, but it's typesafe.

-Alex

Share this post


Link to post
Share on other sites
Quote:
template <class T>
inline T sqr(const T& x)
{
return x*x;
}

I've never actually used templates, although I've got a tutorial on a simple one, so how does this actually work?

also, how would I use it? My understanding of templates was that you create a class (or whatever) with one before you can use it, meaning I'd still need a different one for each numerical type I intend to use.

Share this post


Link to post
Share on other sites
Quote:
Original post by webwraith
Quote:
template <class T>
inline T sqr(const T& x)
{
return x*x;
}

I've never actually used templates, although I've got a tutorial on a simple one, so how does this actually work?

Basically, the compiler creates an instance of the function sqr for every data type you use it with. If you pass a float value to sqr, the compiler will create an instance of sqr where the unknown type T is replaced with float. Templates are very powerful code generation tools, which save you, the coder, from having to create n different versions of sqr to deal with n different data types.

Quote:
Original post by webwraith
also, how would I use it? My understanding of templates was that you create a class with one before you can use it, meaning I'd still need a different one for each numerical type I intend to use.

That is true with templatized classes, but not with templatized functions. Take the sqr function above. In your code, you'd call sqr like so:
sqr<double>(1.0);
This explicitly tells the compiler that 1.0 is to be treated as a value of type double. However, you can omit the part in the < > brackets if you are passing a variable to the function; the compiler can determine the type being passed from the type of the variable.

[Edited by - iNsAn1tY on December 31, 2005 9:49:41 AM]

Share this post


Link to post
Share on other sites
I'd have thought that 1.0 would have been treated as a float?
I take it, you only need to add the <..> when casting as a new/different type?

Thanks for the help, so far. I'm all ears for any other suggestions

Share this post


Link to post
Share on other sites
Quote:
Original post by webwraith
I'd have thought that 1.0 would have been treated as a float?
I take it, you only need to add the <..> when casting as a new type?

Thanks for the help, so far.` I'm all ears for any other suggestions

1.0 is treated as a double by default (by Visual Studio.NET, at least). 1.0f is treated as a float.

You actually need to add the < > every time you call the function, if you're not sending a value from a variable.

EDIT: Made another change above, something wasn't right.

[Edited by - iNsAn1tY on December 31, 2005 9:10:08 AM]

Share this post


Link to post
Share on other sites
Just to make sure you're clear on this...


template <class T>
inline T sqr(const T& x)
{ return x*x; }

int ix = 1;
float fx = 1.0f;
double dx = 1.0;

sqr(ix);
// compiler generates: inline int sqr_1(const int& x){return x*x;}

sqr(fx);
// compiler generates: inline float sqr_2(const float& x) {return x*x;}

sqr(dx);
// compiler generates: inline double sqr_3(const double& x) { return x*x;}




Now here's a question for someone more C++ savvy than me...
Does passing by const reference instead of by value lose performance?
Will the program have to dereference x inside sqr? Is passing by value faster for POD types?

-Alex

Share this post


Link to post
Share on other sites
I wouldn't know, I'm not even sure what the difference between a reference and a pointer is. when I first heared about them, I thought they were the same thing with different names

Share this post


Link to post
Share on other sites
Another thing, if you were trying to cast as a different type, would you use <..> or would you add the cast inside the brackets? Or both?

Finally (phew!) would this need to go in the header, the associated source, or my main source?

Share this post


Link to post
Share on other sites
Quote:
Original post by webwraith
I wouldn't know, I'm not even sure what the difference between a reference and a pointer is. when I first heared about them, I thought they were the same thing with different names


a reference is a pointer without all the fancy sytax for accesses.

eg


void changeToFive( int* variable )
{
*variable = 5; // note the dereference operator...
}

void changeToFive( int& variable )
{
variable = 5;
}

class SomeClass
{
public:
void someMethod();
};

void someFunction( SomeClass *someObject )
{
someObject->someMethod();//more funky syntax to be aware of
}

void someFunction( SomeClass &someObject )
{
someObject.someMethod();//same thing, but clearer
}



pointers can do more, pointer math, have NULL pointers, used for dynamilcally getting memory. references are just easy-on-the-eyes limited pointers.

Share this post


Link to post
Share on other sites
Quote:

Another thing, if you were trying to cast as a different type, would you use <..> or would you add the cast inside the brackets? Or both?

Finally (phew!) would this need to go in the header, the associated source, or my main source?


Let's say you wanted to generate sqr(double) even though you were passing the function an integer.

Calling sqr( (double) ix ) will accomplish this.

-Alex

Share this post


Link to post
Share on other sites
Thanks, cipherx, and I'd assume the answer to your question is that passing a value would be quicker, though I don't imagine there would be much of a difference.

Don't quote me on that though...

Share this post


Link to post
Share on other sites
Calling it by
sqr<double>(someint)
is correct. Cypher's will work, but is containes a nasty little c-style cast.

Share this post


Link to post
Share on other sites
Answer 1:
Passing by const reference to a non-inline function may lose performance, because the piece of data needs to live on the stack (so that a pointer/reference can be generated). If the function is inline (such as most templates) then the compiler has enough information to optimize the passing to actually be by value -- although whether it chooses to do that may vary based on the compiler and specific compiler flags. If you know it's only for ints, floats, doubles and the like, then pass by value.

Answer 2:
In the case of a downcast, C-style casts will perform a static_cast<> when the base type is known, but a reinterpret_cast<> when the base type is not known, without telling you about it. static_cast<> will tell you (using an error) if you're trying to do a downcast without knowing the derived type definition.


class Junk {
public:
int j;
virtual void junkFunc() = 0;
};
class Base {
public:
virtual void someBaseFunction() = 0;
};
class Derived; // forward declared

Base * b;
Derived * d = (Derived *)b; // will not warn you about the reinterpret-cast; will blow up on use
Derived * d = static_cast< Derived * >( b ); // will tell you about the problem

class Derived : public Junk, public Base {
public:
};
Derived * d = (Derived *)b; // will do the right cast
Derived * d = static_cast< Derived * >( b ); // will do the right cast


C-style casts are fine for POD types, but when you're involving types that may have inheritance, you're better off with static_cast<> (which is almost always the behavior you actually want).

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
Quote:
Original post by cypherx
Quote:

#define SQR(name) name*name //Not too sure this'll work...


SQR(x+1) will expand to: x+1*x+1, which equals x+x+1. Definitely not what you wanted.

Quote:

#define SQR(name) (name)*(name)


-Alex





Even better would be:

#define SQR(name) ( (name) * (name) )

in case some other operator of higher precedence might be adjacent
(not overly likely for '*' but often a possibility in other cases.)

#define MAXPACKETLEN (MAXPACKETDATA - MSGHDRLEN - ACKLEN)



Similar goes for multi statement macros that should be surrounded by { }


#define MsgBox( strx ) { MessageBox(NULL, strx, "Damn You Master of Shoddy!!!", MB_ICONERROR | MB_OK); }

#define ASSERTX( cond , msgx ) { if ( ! ( cond )) MsgBox( msgx ); }



Share this post


Link to post
Share on other sites
Actually, surrounding macros with {} isn't really safe, because it may lead to a dangling statement (and doesn't REQUIRE that you terminate with a semicolon).

Two better ways of defining ASSERT():


// assume we have this function to deal with a failed assertion
void assert_failed(char const *file, int line, char const *expression);

// note -- no trailing semicolon in the define!
#define ASSERT(x) if(x);else assert_failed(__FILE__,__LINE__,#x)

// again, no trailing semicolon in the define!
#define ASSERT(x) do{if(!(x)) assert_failed(__FILE__,__LINE__,#x);}while(0)


Both of these will force the user to put a semicolon after usage, and neither of them have a problem with deep if/else statements. The definition by AP, however, might have such problems.

I prefer the first version, but that's just personal aesthetics; both are "correct" and should generate the same code. Actually, the second version isn't vulnerable to the comma operator, whereas the first version would be -- lucky we don't use the comma operator that much in everyday code :-)

Share this post


Link to post
Share on other sites

This topic is 4399 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this