Interfaces and their independent from data structures - how to achieve it?

Started by
6 comments, last by SiS-Shadowman 13 years, 9 months ago
Hello.
I have written some interface for my module. Let's say it's a interface of 3d engine. I have a few functions that returns/need to get some parameters like vector in 3d space. The structures from DirectX (like D3DXVECTOR) and windows.h (RECT, POINT) suit me well, but the engine should be independent of system and API (I will want to create engine based on DirectX and also the other based on OGL - on the same interface).
And I don't know what's the best way to achieve it.
For now I have done sth like that:
#ifdef WINDOWS#include <windows.h>#include <d3dx9.h>typedef D3DXVECTOR3 VECTOR3D;#elsestruct RECT{   long left;   long top;   long right;   long bottom;};struct POINT{   long x;   long y;};struct VECTOR3D{   float x;   float y;   float z;};#endif // WINDOWS


But I really don't like that way.
Moreover it's not 'nice' when OGL programmers will have to use the structures from DirectX to represent OGL structures (one more conversion...).

On the other hand, creating my own structure for vector 3d, rect etc. will make me to make conversions to that type both in DirectX and OGL versions of engine :/
Advertisement
If you want your interface to be independent of the underlying implementation all communication with the interface must be so as well.

Why do you worry about converting your own vector type to the D3D or OGL equivalent? If it is speed then:
1) Are you really sure speed is an issue? How often does conversion happen?
2) You can get around this by using the preprocessor to make your vector type have the same binary structure as the D3D or OGL version. Then conversion will be free (but you need to be vary of how the data is saved).


Quote:Moreover it's not 'nice' when OGL programmers will have to use the structures from DirectX to represent OGL structures (one more conversion...).

If you have done your abstraction job probably there is no such thing as OGL programmers. People do not know or care about your underlying implementation (reality may be slightly different in a real-time environment with possibly buggy drivers, but it doesn't really matter).
The conversions happen about few times in each game loop. It's not the extremely high amount, but also not too small to think about some optimizations.
Ofc more important is the independence of interface.

Now it looks like the best way is (according to rasnjo) to declare own structures that will be converted to/from current API any time the few functions from engine (the functions that use that specified types for attributes/returned value) will be call.

In the modules from 'higher' level (modules that use my interface) I can also use structures I will declare for this interface. So the conversion will have to be done only at the level of chosen engine implementation.

Thx for quick help.
Quote:Original post by PolGraphic
The conversions happen about few times in each game loop. It's not the extremely high amount, but also not too small to think about some optimizations.
Ofc more important is the independence of interface.

If few means less than 100 times per game loop, then it's definitely not something to worry about.
Indeed ;]
Nice to hear that own structures and conversions are 'normal' and won't make somebody to scream...
Thx once again, now everything it's clear, but firstly I wasn't sure how to deal with that :)

One more thing - about HWND structure. Should I cast it to sth (long? not pretty nice when sb want to use 64-bit code...), or just include windows.h inside #ifdef WINDOWS?
Quote:Original post by PolGraphic
One more thing - about HWND structure. Should I cast it to sth (long? not pretty nice when sb want to use 64-bit code...), or just include windows.h inside #ifdef WINDOWS?


HWND is merely a pointer to a random structure:
#define DECLARE_HANDLE(name) struct name##__ { int unused; }; typedef struct name##__ *nameDECLARE_HANDLE(HWND)


Therefore you could make it a void pointer, although including windows.h is not a big deal.
This is why developing a cross-platform engine is an incredibly boring and tedious endeavor.

Considering there is no common ground between the two APIs, the simplest generic abstraction is something like this:
#ifdef DXstruct PortableRect : RECT {  void set(float x, float y, ....);};#elsestruct PortableRect {  void set(float x, float y, ....);private:  float x,y,w,h;}#endifvoid foo(PortableRect & pr);void bar(PortableRect & pr);...
The API remains identical and consistent, requires no casts, no checks, no nothing and is fully abstracted using interfaces.

The getter/setter/mutator overhead in C++ should be zero for all practical purposes.
I wrote alot of stuff that is already "offered" by DirectX or Windows myself (mathematical structures for the most part). This had several reasons: For once I wanted to improve my skills by implementing a lot of mathematical calculations by myself, but I also hated to write D3DXVec3Dot for every dot product.

When writing those parts I ensured that both vector3 and matrix3x3/4x4 had the same layout of memory as their DirectX counterpart (checked during compile time via offsetof).

But I highly doubt that writing something like:
D3DXVector3 convert(myvector3 vec){    D3DXVector3 v; v.x = vec.x; v.y = vec.y; v.z = vec.z;    return v;}

will make any difference (concerning performance) in a hobby project.
(As always: Only start worrying if your code is actually running slowly)

Maybe it boils down to personal preference, but I prefer something like
math::vector3 c = cross(a, b);float l = c.length();

over what DirectX offers. For example, I really enjoyed writing (and using) my own rectangle implementation, since it offers functions like
class rectangle{...    boost::optional<rectangle> intersect(rectangle that);    float area() const;    rectangle& union(rectangle that);}

However nothing stops you from writing non-member functions for POINT or RECT.
It may simply be personal preference to enjoy member functions over non-member functions (though I usually implement the latter in special cases as well: length(vec1 + vec2) is less code than vector3(vec1 + vec2).length() :D).

This topic is closed to new replies.

Advertisement