Skip to main content
gamedev.net
🔒 Locked

phase1: portability

Started by original vesoljc 23 replies 700+ views
Original Post
original vesoljc
original vesoljc
when designing a project, what everything has to be "included" so that code is actuall portable? as i see it, platform is the first issue (win32, mac, unix, linux, etc...) and compiler is the second (msvc, g++, etc...) is there anything else? Abnormal behavior of abnormal brain makes me normal...
Abnormal behavior of abnormal brain makes me normal...
null_pointer
null_pointer
third issue: machine architecture, which determines endian-ness, floating-point precision (in part), and representation of negative numbers

Why do you ask?
LilBudyWizer
LilBudyWizer
Using DirectX on a PalmPilot doesn''t work to well. For that matter a 1gb install on a PalmPilot doesn''t work to well either.
Keys to success: Ability, ambition and opportunity.
original vesoljc
original vesoljc
LilBudyWizer: lets leave multimedia options (video,audio,etc..) out for now.

neurokaotix: no logo!

null_pointer: any basic guidelines on how to support this?

Abnormal behavior of abnormal brain makes me normal...
Abnormal behavior of abnormal brain makes me normal...
owl
owl
quote:
Original post by original vesoljc
when designing a project, what everything has to be "included" so that code is actuall portable?
as i see it, platform is the first issue (win32, mac, unix, linux, etc...) and compiler is the second (msvc, g++, etc...)
is there anything else?

Abnormal behavior of abnormal brain makes me normal...


To write *real* portable code...
[size="2"]I like the Walrus best.
original vesoljc
original vesoljc
xaxa: yeah, i get that part
if sticking with standard c++ (and libs) things should work anywhere, right?
ps: how do u do a quote?

Abnormal behavior of abnormal brain makes me normal...
Abnormal behavior of abnormal brain makes me normal...
owl
owl
quote:
Original post by original vesoljc
xaxa: yeah, i get that part
if sticking with standard c++ (and libs) things should work anywhere, right?


No, but it helps a lot. Design is the key. Design = To_Think_Beforehand + (experience).

quote:
Original post by original vesoljc
ps: how do u do a quote?





*ERROR 500*
*ERROR 500*
[size="2"]I like the Walrus best.
original vesoljc
original vesoljc
quote:
Original post by xaxa
[quote]Original post by original vesoljc
xaxa: yeah, i get that part
if sticking with standard c++ (and libs) things should work anywhere, right?


No, but it helps a lot. Design is the key. Design = To_Think_Beforehand + (experience).

quote:
Original post by original vesoljc
ps: how do u do a quote?





*ERROR 500*
*ERROR 500*

got it! tnx



Abnormal behavior of abnormal brain makes me normal...
Abnormal behavior of abnormal brain makes me normal...
original vesoljc
original vesoljc
so, if using "general" integer and floating point operations (math,etc...) do i need to care what kind of architecture is inside?
to be honest, i''m little in the dark here...

Abnormal behavior of abnormal brain makes me normal...
Abnormal behavior of abnormal brain makes me normal...
null_pointer
null_pointer
quote:
so, if using "general" integer and floating point operations (math,etc...) do i need to care what kind of architecture is inside?
to be honest, i''m little in the dark here...


It depends on your application and how it was written. For most applications, you do not need to worry about the format when actually processing the data. You only need to care when you intend to share the data between other instances of your program. Examples of this sharing include writing files to disk and sending information over a network.

Some applications, such as machine emulators, are very tied to the low level details of the machine. They may depend on the exact behavior machine-specific instructions and/or the bit representation of the numbers as the program is running. They must keep the bits of each of the emulated machine''s registers and memories at exactly the same state they would be if the machine were actually running natively.

Applications that depend on the exact representation of numbers also tend to use *lots* of bit operations, so it is usually fairly obvious that they would break compatibility on a different platform.
original vesoljc
original vesoljc
so, a binary file could be "ordered" diffrently on diffrent platforms?
ex:
class vector
{
float x,y,z;
}

vector myarray[10000];
// pseudo code
// open bin file
fprint(f?)(file,myarray,sizeof(myarray),1);



Abnormal behavior of abnormal brain makes me normal...
Abnormal behavior of abnormal brain makes me normal...
null_pointer
null_pointer
quote:
so, a binary file could be "ordered" diffrently on diffrent platforms?


Yes, basically. There are three problems with your code.

1. float could be a different size on other platforms, turning the float array into mush when you read it in.

2. float could use a different representation, making your code running on one platform interpret the file data totally differently, turning the float array into mush when you read it in.

3. Your vector class''s data members could be subject to alignment issues and therefore have padding bytes inserted into them, turning the float array into mush when you read it in.

For the first two problems, you would need some preprocessor magic to make sure that you always convert the array from native to common format when you write the array to the file and convert the array from common to native format when you read it back in.

For the third problem, never write whole classes to disk; only write objects of the basic types (i.e., int, float, char, etc.) to disk. An easy way to do this is to give the vector a member function that writes the floats to disk. Ditto that for reading the class''s members back in.
original vesoljc
original vesoljc
1) size issue can be handled
2) diffrent representation: can we and how to check it? ie: float, as i remember, is made out of an exponent and mantisa, right?
integers should not be problem, since only size matters (and signed,unsigned). you mentioned negative numbers can have diffrent representation?
3) i see, so if an object has data, it should first be sortoff "packed" into raw data type, and then write/send this?

any reading u recommend (preferablly online)?



Abnormal behavior of abnormal brain makes me normal...
Abnormal behavior of abnormal brain makes me normal...
null_pointer
null_pointer
quote:
1) size issue can be handled
2) diffrent representation: can we and how to check it? ie: float, as i remember, is made out of an exponent and mantisa, right?
integers should not be problem, since only size matters (and signed,unsigned). you mentioned negative numbers can have diffrent representation?


You must use the C++ preprocessor to detect the compiler and the machine architecture being used. For example, Visual C++ defines a preprocessor symbol named _MSC_VER which serves as a version number of the compiler; if _MSC_VER is defined, then your program is being compiled under Visual C++. Other compilers will have their own symbols, and by detecting the presence of these symbols you can determine which compiler is being used.

Each compiler has its own symbols that tell which machine architecture is being targeted. For Visual C++, the symbol _M_IX86 is defined if your program is being compiled for Intel''s x86 architecture. IIRC there are other symbols for Itanium and Athlon-64 architectures. Other compilers may have completely different symbols for these architectures, which is why you detect the compiler first.

Both the sizes of the types and also their representations are dependent on the specific compiler and machine architecture being used. You will have to consult their help files to find out the exact details for each platform. Basically, you set up your typedefs and defines based on these two conditions. You might define float32, float64, etc. types along with FLOAT_USES_X_REPRESENTATION or something to that effect.

Throughout your project you would then use the typedefs and defines so that you know which format is native and how to convert it to and from the "portable" format that you have selected. It would be a good idea to wrap up all those icky details about the conversion into two functions, portable_read and portable_write.

quote:
3) i see, so if an object has data, it should first be sortoff "packed" into raw data type, and then write/send this?


I am not sure I understand you. The idea is to just write the data members individually instead of the entire class at once:


  
# include <ostream>
 
class vector
{
public:
float x, y, z;
};
 
int main ()
{
vector v;
ofstream output ("somefile.bin");
 
// This is bad! Even if float is the same size on another platform, the compiler

// may have padded the vector class members so that vector is a different size and/or

// x, y, and z have different offsets within the class.

 
output.write (reinterpret_cast <char*> (&v), sizeof (v));
 
// This is much better, because it does not have to worry about the memory

// layout of the vector class, just the floats themselves.

 
output.write (reinterpret_cast <char*> (&v.x), sizeof (v.x));
output.write (reinterpret_cast <char*> (&v.y), sizeof (v.y));
output.write (reinterpret_cast <char*> (&v.z), sizeof (v.z));
}

null_pointer
null_pointer
quote:
any reading u recommend (preferablly online)?


Try doing a search for byte order, twos complement, etc. Oh, there is a free book on assembly that serves as a good introduction to how numbers are represented in binary. Search for pcasm-book.pdf or PC Assembly Language Programming. Also, a TCP/IP reference might have something about byte order.
Greg Brown
Greg Brown
The way I approach this problem is to write all my projects for Win32/Intel, since all other systems are irrelevant anyway. Mac users are too dumb to worry about and Linux users are too cheap to buy anything, so why waste time making software for them? Go where the money is, and that''s Windows!
original vesoljc
original vesoljc
so far i have this:
axpTypes.h

  
#ifndef _AXP_TYPES_H_
#define _AXP_TYPES_H_

// SYSTEM INCLUDES


// PROJECT INCLUDES

#include "axpPlatform.h"

// LOCAL INCLUDES

#include <string>

///////////////////////

// COMPILER SPECIFIC //

///////////////////////


#ifdef _MSC_VER
#include "axpTypesMSVC.h"
#endif


#ifdef __GNUC__
#include "axpTypesGNUC.h"
#endif

// TODO: add integer size check


////////////

// COMMON //

////////////


// BASE TYPES


// Real types

typedef float axpReal32;
typedef double axpReal64;

// TODO: add real size check


// Character type

typedef char axpChar;
typedef char* axpPChar;

// Bool type

typedef bool axpBool;

// String type

typedef std::string axpString;
typedef std::string* axpPString;

// Void type

typedef void axpVoid;
typedef void* axpPVoid;

// Bit flags type

typedef axpUInt8 axpFlag8;
typedef axpUInt16 axpFlag16;
typedef axpUInt32 axpFlag32;
typedef axpUInt64 axpFlag64;

#endif //_AXP_TYPES_H_



axpTypesGNUC.h

  
#ifndef _AXP_TYPES_GNUC_H_
#define _AXP_TYPES_GNUC_H_

// SYSTEM INCLUDES

// PROJECT INCLUDES

// LOCAL INCLUDES


// Integer types

typedef signed char axpSInt8;
typedef unsigned char axpUInt8;
typedef signed short axpSInt16;
typedef unsigned short axpUInt16;
typedef signed int axpSInt32;
typedef unsigned int axpUInt32;
typedef signed long long axpSInt64;
typedef unsigned long long axpUInt64;

#endif //_AXP_TYPES_GNUC_H_



axpTypesMSVC.h

  
#ifndef _AXP_TYPES_MSVC_H_
#define _AXP_TYPES_MSVC_H_

// SYSTEM INCLUDES

// PROJECT INCLUDES

// LOCAL INCLUDES


// Integer types

typedef signed __int8 axpSInt8;
typedef unsigned __int8 axpUInt8;
typedef signed __int16 axpSInt16;
typedef unsigned __int16 axpUInt16;
typedef signed __int32 axpSInt32;
typedef unsigned __int32 axpUInt32;
typedef signed __int64 axpSInt64;
typedef unsigned __int64 axpUInt64;

#endif //_AXP_TYPES_MSVC_H_



i am on the right track?

"packed" data:
what i meant to say is that for performance reasons (in vector class that is) data should be grouped (batch?) to do a single read/write. instead of write(x),write(y),write(z),
u''d use float data[3] and do write/read(data)


greg brown: imho that kind of thinking suxx

Abnormal behavior of abnormal brain makes me normal...
Abnormal behavior of abnormal brain makes me normal...
null_pointer
null_pointer
quote:
so far i have this:


You might want to use an else-if condition on the GNU and subsequent compilers so that you do not end up including more than one. The preprocessor directive for this is "elif" <rant>because someone deciding that reducing typing by two letters was worth the hassle of creating words not found in any non-programming language.</rant>

quote:
i am on the right track?


Yes, it looks like you are on the right track. Just keep in mind that writing portable code does not consist in restricting your whole program to, say, axpSInt32. You only want to use your typedefs when you actually need to know the exact size, such as when you save program data between instances (i.e., a game''s save files or a network packet) because those instances might be compiled with a different compiler, perhaps on another platform.

quote:
"packed" data:
what i meant to say is that for performance reasons (in vector class that is) data should be grouped (batch?) to do a single read/write. instead of write(x),write(y),write(z),
u''d use float data[3] and do write/read(data)


If you think about it, though, it should not matter much. The reads and writes are buffered anyway, so unless you are flushing the stream after each read or write, it should not matter if you break up each read or write into the equivalent number of smaller operations. Your data will still be flushed to disk in nice, big blocks.
acw83
acw83
interesting

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.