Sign in to follow this  
Halifax2

Middleware Development Help

Recommended Posts

Alright, I just need some quick help with some of my development concerns. Right now I am developing some middleware, and as everyone knows, middleware should be easily integrated into existing engines. Basically, I want the user to be able to "plug" their own implementations for vectors, matrices, file handling, and other things into the middleware, and then the middleware will use their implementation. So my solution: I create an abstract interface for the user implementation to adhere to, and then I create an abstract interface for a factory class, with one function, that the user must implement. The user then registers their factory class, and the engine uses that factory class to create the objects without any idea of the underlying implementation. Does this seem reasonable? Is there a better way to do it?

Share this post


Link to post
Share on other sites
I suspect that an abstract interface for matrix and vector handling will incur too much overhead. You'd want them to write a 3D vector factory, for example? And would those vectors have to derive from your base class?

Share this post


Link to post
Share on other sites
Yes, currently, they have to derive from the base class. And that's exactly what I was afraid of, the overhead.

So any suggestions on how I would gaurantee that the user's implementation has specific functions, such as normalize(), dotproduct(), etc.?

Share this post


Link to post
Share on other sites
Forcing vector, matrix and file handling class' to derive from your base class is just bad design, take some time to learn what good middleware design involves. For example this blog post by Kyle Wilson and the presentation I link to in the comments.

edit: link fixed

Share this post


Link to post
Share on other sites
Quote:
Original post by stonemetal
provide templates that require them.

provide the classes yourself then all they have to do is write conversion constructors or use them as is.


I don't quite understand your first explanation, can you explain that. And your second example does seem alright, but then the class I provide does not use their implementation, which could be optimized for some special cases or something.

Quote:
Original post by dmail
Forcing vector, matrix and file handling class' to derive from you base class is just bad design, take some time to learn what good middleware design involves. For example this blog post by Kyle Wilson and the presentation I link to in the comments.


That link doesn't work.

Share this post


Link to post
Share on other sites
Quote:
Original post by dmail
Forcing vector, matrix and file handling class' to derive from your base class is just bad design, take some time to learn what good middleware design involves. For example this blog post by Kyle Wilson and the presentation I link to in the comments.
That link is going in bookmarks forthwith.

Share this post


Link to post
Share on other sites
Quote:
Original post by swiftcoder
Quote:
Original post by dmail
Forcing vector, matrix and file handling class' to derive from your base class is just bad design, take some time to learn what good middleware design involves. For example this blog post by Kyle Wilson and the presentation I link to in the comments.
That link is going in bookmarks forthwith.


Odd, it works for me now too. I guess I will have a read then.

EDIT:

Ah, you fixed the link, that's why. But at any rate, I have read that article before when I was searching the internet for answers to my question. This is the exact reason why I wrote, "Basically, I want the user to be able to "plug" their own implementations for...file handling..."

Currently, I have no answers on how to do this efficiently.

Share this post


Link to post
Share on other sites
Did you also listen to the presentation?
If you are going to be doing matrix and vector operations then you need to use you own representation (or a well defined implementation), yet not force the user of your library to have to use it. Instead give them options by supplying more than one method of informing you of a matrix or vector.
Most math libraries have a conversion to a block of data from a class in the form of float* or double* so having this option is really a must. The presentation makes the point of writing user code before library code, ie what will your uses have to do, so ...

enum MATRIX_ORDER{ROW,COLUMN};
void Lib::bar(float*);
void Lib::set_matrix_order(COLUMN);//initialise the lib
...
//user code
D3DXMATRIX foo;
Lib::bar(foo);//D3DXMATRIX has the conversion and the library knows the layout.



You could also support the DirectX math library directly in a windows build or at least give the user the option, possibly also giving the user the _option_ of using your libraries fancy matrix type. Another possibility is to document your matrix type and say to the user here is a abstract class derive from it with an implementation of converting from my type to yours and vice versa and tell the library about it. You could also have an option which accepts 16 floats or ...

Share this post


Link to post
Share on other sites
Quote:
Original post by Halifax2
Basically, I want the user to be able to "plug" their own implementations for vectors, matrices
Don't. I've seen it tried, and it doesn't work, and it sucks. Manually written conversion functions, mildly annoying though they may be, are the way to go. Just make sure to store your matrices in the same order as everyone else, so that conversion functions are no-ops.
Quote:
file handling

This one's a better idea, and it's also a lot easier. The way you proposed will work fine. Consider, though, how streaming will affect things.

Share this post


Link to post
Share on other sites
If your middleware is distributed with full source code, then you could use the client/engine's math classes within your code, by including the engine's math code within your middleware's code.
E.g.
// my_math.h
#ifdef USING_GAMEBRYO_ENGINE
# include <gamebryo_SDK/math.h>
# include "gamebryo_math.h"
#else
# ifdef USING_UNREAL3_ENGINE
# include <unreal_SDK/math.h>
# include "unreal3_math.h"
# else
# include "backup_math.h"
# endif
#endif


// backup_math.h
class MyVec3
{
MyVec3() : x(0), y(0), z(0) {}
MyVec3( const MyVec3& o ) : x(o.x), y(o.y), z(o.z) {}
MyVec3& operator+=( const MyVec3& rhs ) { x+=rhs.x; ... return *this; }
float X() const { return x; }
//etc...
private:
float x,y,z;
}
// gamebryo_math.h
class MyVec3 : public NiPoint3
{
MyVec3() : NiPoint3(0,0,0) {}
MyVec3( const NiPoint3& o ) : NiPoint3(o) {}
MyVec3& operator+=( const MyVec3& rhs ) { NiPoint3::operator+=(rhs); return *this; }
float X() const { return m_flX; }
//etc...
}

This means you're using your own interface (not virtual tho) but you can actually just be using their classes behind that interface.

Share this post


Link to post
Share on other sites
Quote:
Original post by Halifax2
So my solution: I create an abstract interface for the user implementation to adhere to, and then I create an abstract interface for a factory class, with one function, that the user must implement. The user then registers their factory class, and the engine uses that factory class to create the objects without any idea of the underlying implementation.


I'm sorry to be a bit blunt, but if you don't see that this is an absolutely horrible idea for vector and matrix classes (for file classes it's ok though), then I don't think you are ready to develop any viable middleware yet.

About those math classes, I would really just suggest you to use your own implementation, which would exist side by side with the math classes the users own code uses. This is how pretty much all middleware does it, and it's like the least problematic thing when integrating middleware into an existing engine.

And a personal opinion of mine: It's definitely a good idea to give the user the option to provide their own file handling to the middleware, but I really think it should be optional, and not be something the user has to implement, before it can even start using your middleware. Always provide a simple fallback to something like a fopen based implementation or so, just so it will be easier for the users to get started with trying it out a bit.

Share this post


Link to post
Share on other sites
Quote:
Original post by Halifax2
So my solution: I create an abstract interface for the user implementation to adhere to, and then I create an abstract interface for a factory class, with one function, that the user must implement. The user then registers their factory class, and the engine uses that factory class to create the objects without any idea of the underlying implementation.


A good design philosophy for these kinds of things it to make sure you implement your memory allocation, file management, debug output and anything you want pluggable as plugins yourself. For example, if you support reading from a file, you'll need a way to open a file, read from a file, and close the file. I can imagine you might provide an abstract interface IFileRoutines that can be implemented by the end user and handed to the library on startup. In one case, you might write:


if ( pFileRoutinesInterface )
pFileRoutinesInterface->Read(...);
else
fread( fp, data, ... );


Instead implement the built-in file handling as a concrete implementation of the IFileRoutines class so that it excersises the same interface as the end user. You can additionally provide the source code for the default implementation so that people can see and cut and paste to get started.

I find this works really well for making sure you provide all the hooks to the end user - if you suddenly realise you need fseek you won't forget to add it to the IFileRoutines interface.

As far as the math routines go, I'm with everyone else - the implementation details of a math library should be considered internal functions of your middleware package - the client code should use their own math library and pass the data to yours. This is exactly how all the middelware products I've ever seen work.

That article about middleware was great. Going to have to put that in my reference list.

S.

Share this post


Link to post
Share on other sites

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