|
||
An application often needs to convert the individual items within a resource into data which can be stored within the data members of a class.
A resource is defined in terms of a struct; this struct might be defined in terms of other structs, or arrays of structs.
The following code fragments show how a simple resource can be used to initialise an instance C++ class. The TResourceReader class provides a safe way to interpret individual items within the resource.
The following code fragment defines a simple struct. The convention
is to define such structs in resource header files that, by convention, have
the .rh
extension. Resource header files are included in resource
definition files, that have a .rss
extension. This is analogous to
the conventions for C++ header and source files.
// for defining a single data resource
STRUCT DATA
{
WORD wrd=16;
WORD flags=0;
LONG lng;
BYTE byt;
DOUBLE dbl=0.0;
LTEXT ltxt; // variable length
}
The WORD
item labelled flags
is used to
contain flag bit settings. Each bit setting is defined by a corresponding
symbol. Such symbols are almost always needed both by the resource and the C++
code, and are placed in a file with a .hrh
extension. The
.hrh
file is included in both the resource files and the C++ files
at the appropriate locations.
So, in the .hrh
file, we might have:
// define some flags
#define EFlagItem1 0x0001
#define EFlagItem2 0x0002
#define EFlagItem3 0x0004
...
The resource itself is defined within the resource file, the
.rss
file, and must include the resource header, the
.rh
file, so that the resource compiler can find the definition of
the DATA
struct. In this example, the resource header file is
called ReadData.rh
.
#include "ReadData.rh"
RESOURCE DATA first
{
wrd=0;
flags=EFlagItem1+EFlagItem2;
lng=2;
byt=255;
dbl=99.9;
ltxt=This is LTEXT type (leading byte count);
}
After resource compilation, the generated .rsg
header
file contains:
#define FIRST 1
The following code fragment defines an example class,
CResData
, that uses the resource data as a construction parameter.
The class definition is placed in its own C++ header file, a .h
file.
class CResData : public CBase
{
public:
~CResData()
static CResData* NewLC(TResourceReader& aReader);
void ShowData(const TInt aStructNum = 0);
private:
void ConstructL(TResourceReader& aReader);
private:
TInt iWrd; // STRUCT member type: WORD,
TInt iFlags; // WORD
TInt iLng; // LONG,
TInt iByt; // BYTE,
TReal iDbl; // DOUBLE,
HBufC* iLtxt; // LTEXT
};
The data members of the CResData
class correspond to the
individual data items within the resource as defined by the DATA
struct.
The following example code fragment loads the resource, with id
FIRST
, into a descriptor and constructs the CResData
object from that data using a TResourceReader
object to interpret
the component items:
// Read the first resource
HBufC8* res = resourceFile.AllocReadLC(FIRST);
TResourceReader theReader;
theReader.setBuffer(res);
// construct a CResData object to contain
// the simple resource of type DATA.
CResData* resData = CResData::NewLC(theReader);
CResData::NewLC()
allocates the CResData
object
and calls ConstructL()
to complete the construction
process.
ConstructL()
takes a reference to a
TResourceReader
that refers to the resource data itself.
Note that raw resource data is always treated as general binary data.
The construction process of a CResData
object, implemented
by the ConstructL()
function, extracts the individual members from
the resource and stores them within the corresponding data members of the C++
class. Thus, the wrd
member, whose type is WORD
, is
copied to iWrd
and the flags
member, whose type is
also WORD
, is copied to iFlags
and so on.
void CResData::ConstructL(TResourceReader& aReader)
{
// Interpret next bytes as a TInt16
iWrd = aReader.ReadInt16();
// Interpret next bytes as a TInt16
iFlags = aReader.ReadInt16();
// Interpret next bytes as a TInt32
iLng = aReader.ReadInt32();
// Interpret the next byte as a TInt8
iByt = aReader.ReadInt8();
// Interpret next bytes as a TReal
iDbl = aReader.ReadReal64();
// Interpret the next byte as the length
// of text. The text itself follows
// this byte.
iLtxt = aReader.ReadHBufCL();
}
This code uses the TResourceReader
object to interpret and
extract the component items of the resource data. The extracted component items
are copied into the members of the CResData
class.
The resource data is expected to be described by the DATA
struct. It is important to note that if the DATA
struct were
altered, then this code would also need to be altered. Supplying a resource
that does not follow the pattern described by the DATA
struct,
would not prevent the C++ code from compiling successfully. However, the
results of execution would be unpredictable. Thus, the resource loading system
is not type safe in the same way that C++ programming is.
The integers in the C++ class are specified as TInt
values.
This means that they are the natural word size of the machine, at least 32 bits. The corresponding resource data, however,
is specified as
either BYTE
, WORD
or LONG
members: 8, 16 or 32 bits. The data is broadened as it is read
into the C++ variables. As a rule, resource data should be stored in as compact
a form as possible, taking into account the variation in possible
values.