This page concerns C and C++ development for applications for use with Bentley Systems' MicroStation®. Questions similar to these appear in the Be Community MicroStation Programming Forums.
Q
I want to declare an MDL typedef in a struct (to pass some data to my application)
but I am getting an error saying some MDL typedef is not a valid type.
How can I include some MDL typedef as a valid type in my header file?
Q
What are macros LEGACY_DVEC3D and NO_LEGACY_DVEC3D?
Where are those macros defined?
Part of Bentley Systems complex pre-processor macro definition system is an attempt to
distinguish between DVec3d and DPoint3d.
Those macros switch between different typedefs.
Read more …
Q
I don't know how to get from DPoint3dCP to DPoint3dCR.
I scanned the MDL header files and didn't find anything …
Where are those Hungarian notation suffixes defined?
Q What's an opaque pointer?
A
MDL defines an enormous variety of structs and typedefs in its header files.
In a default MicroStation V8i or MicroStation XM installation, those headers are in folder
C:\Program Files\Bentley\MicroStation\mdl\include
In a default MicroStation V8.5 or earlier installation, those headers are in folder
C:\Program Files\Bentley\Program\MicroStation\mdl\include
As an MDL developer, you must become familiar with that folder and its contents.
It also includes, by the way, all the function definition files (.fdf files) that you'll need.
Function definition files are just a header file with an .fdf extension.
You will often need to find a particular structure to use in your own code.
A text editor with file-search capability is invaluable.
It will improve your productivity by providing search operations from your editing environment.
We mention some useful text editors elsewhere.
A
MDL and the MicroStationAPI define and use a large number of typedefs.
A C/C++ typedef lets you — or Bentley Systems in this case — extend the compiler's type checking to cover
your custom data type definitions.
DPoint3dCP to DPoint3dCR are two examples that extend the well-known DPoint3d
data type. But what do those suffixes mean?
Somebody at Bentley thought it would be a good idea to have a consistent way to append a Hungarian-notation suffix to type names. Not a bad idea! It clarifies code for human consumption and provides consistently-derived types for the compiler to check. For example, we're all familiar with a DPoint3d data type …
struct _dpoint3d
{
double x;
double y;
double z;
} dpoint3d;
It's common to pass address-of a DPoint3d when we want data copied into our variable …
DPoint3d var;
mdlAPI_someFunction (&var, … );
What's the data type in the function prototype? A pointer to DPoint3d (DPoint3d*).
void mdlAPI_someFunction (DPoint3d* copyDataToMe, … );
We also pass address-of a DPoint3d when we want data copied from our variable
to somewhere inside the function.
In this case, we should really pass DPoint3d const* to indicate that it's our data and we don't want it changed.
The call site looks identical …
DPoint3d var = { 1., 2., 3. };
mdlAPI_someFunction (&var, … );
…but the function prototype changes …
void mdlAPI_someFunction (DPoint3d const* copyDataFromMe, … );
The Hungarian-suffix notation for the above data types uses P for pointer and
CP for const-pointer.
Expressed formally …
typedef DPoint3d* DPoint3dP; typedef DPoint3d const * DPoint3dCP;
Using those typedefs, the above function prototypes could be written …
void mdlAPI_someFunction (DPoint3dP copyDataToMe, … ); void mdlAPI_someFunction (DPoint3dCP copyDataFromMe, … );
When writing C++, it's convenient to pass a reference-to-a variable.
As with pointers, a reference can be const
or non-const.
Expressed formally …
typedef DPoint3d& DPoint3dR; typedef DPoint3d const& DPoint3dCR;
So there are four common Hungarian-suffix labels for const and
non-const
references and pointers …
DPoint3dP
DPoint3dCP
DPoint3dR
DPoint3dCR
There are how many data types in MDL and the MicroStationAPI? Don't answer the question; the answer is 'plenty'.
For a short example, look at #include file <bentleytype.h>.
Somebody at Bentley decided it would be a good idea to use a macro to create those
type definitions.
Instead of laboriously writing out the above typedefs by hand, why not invoke a pre-processor macro
to do the hard work?
Some people will tell you that pre-processor macros are evil
(macros are not really evil: this is a programmers' light-hearted indication that they present unforseen problems).
In this case, the use of a macro to perform a typedef (in fact, multiple typedefs) obfuscates their meaning.
In #include file <msgeomstructs_typedefs.h> you will find macro DEFINE_GEOM_STRUCT.
It uses the pre-processor's stringize capability to append the appropriate Hungarian notation
suffix to an existing type name …
DEFINE_GEOM_STRUCT (DPoint3d,_dPoint3d)
That's why you can't find a type definition for DPoint3dP and its relatives.
They're hidden behind some pre-processor prestidigitation.
The following MDL #include files provide typedef-generator macros …
bentley.h
mstypes.h
msgeomstructs_typedefs.h
These are complex nested macros that use C pre-processor stringization to construct a number of typedefs
from a given type …
BENTLEY_TYPEDEFS
ADD_TYPEDEFS1
DEFINE_GEOM_STRUCT
DEFINE_GEOM_CLASS
The macros behave differently in C++ compared to C (MDL) and place the typedefs in a namespace.
The end result is that you can pass in a basic type, say my_data_struct, and the macro generates
Hungarian-notation suffix typedefs for your struct's pointer and const pointer,
in this case …
// MDL typedefs
typedef struct my_data_struct* my_data_structP;
typedef struct my_data_struct const* my_data_structCP
For example, <mstypes.h> runs the ScanCriteria struct through macro GLOBAL_TYPEDEF.
That macro creates the pointer and const pointer typedefs of ScanCriteria,
which are
ScanCriteriaP and ScanCriteriaCP.
When building a C++ project, the macros are more complex and produce both pointer and reference typedefs.
Something like this …
// C++ typedefs
typedef struct Bentley::my_data_struct* Bentley::my_data_structP;
typedef struct Bentley::my_data_struct const* Bentley::my_data_structCP
typedef struct Bentley::my_data_struct& Bentley::my_data_structR;
typedef struct Bentley::my_data_struct const& Bentley::my_data_structCR
Undocumented macro LEGACY_DVEC3D modifies the typedef generated by DEFINE_GEOM_STRUCT.
You may need to #define, or possibly #undefine, LEGACY_DVEC3D before including Bentley header files.
For applications that don't use the C++ interfaces, the LEGACY_DVEC3D change to
DVec3d and DPoint3d
can be disruptive so Bentley Systems made macro LEGACY_DVEC3D
to disable it (see mdl.h).
You can override that behavior by defining NO_LEGACY_DVEC3D in your makefile.
Add the following lines to your bmake file.
This may necessitate cleaning up calls to routines that accept DVec3d to have the correct argument type …
nameToDefine=NO_LEGACY_DVEC3D %include cdefapnd.mki
The effect of the above is to pass define a switch for the Visual C++ compiler -dNO_LEGACY_DVEC3D
which in turn will effect the way the pre-processor interprets the header files that define
the DVec3d and DPoint3d data types.
A
Often you may find that a typedef exists for a struct but you can't find the struct definition.
For example, StringList is a typedef but its struct is not published.
It doesn't matter that you can't see the definition of the StringList struct,
because you can declare a pointer of type StringList*.
In fact, you only ever need to declare a pointer to a StringList*.
The StringList API only ever requires a StringList* and never a StringList.
Bentley documentation calls this an opaque pointer, because you can declare and use a pointer to that data structure,
but can't see the struct definition.
Other examples of an opaque pointer include the MSWindow* and TransDescrP.
As with the StringList*, you only ever manipulate a MSWindow* or TransDescrP using the API.
If you mistakenly declare a variable of a type only referenced through an opaque pointer, the compiler issues an error. That is, if you attempt this for example …
DialogBox db; db.id = 1324;
You will see a compiler error because you can't instantiate a struct whose definition does not exist.
StringList Example
For example, a StringList is a common data type that is typedef'd in an MDL header file.
Suppose you want to define a struct that includes a StringList.
You write something like this in your header file …
typedef struct globals
{
int intVal;
double doubleVal;
char stringVal [45];
StringList* strListP;
} Globals;
However, the MDL compiler (mcomp.exe) doesn't like that and issues a rude message along the lines
StringList is not a valid type. To fix that error, you must find and #include the
relevant MDL header.
Using a text editor with built-in file search makes that task easy.
In this case you will find that typedef struct stringList StringList; appears in several header files.
The more relevant files are msstrlst.h and msstrngl.h.
Include either of those in your header to obtain a compilable typedef …
#include <msstrngl.h>
typedef struct globals
{
int intVal;
double doubleVal;
char stringVal [45];
StringList* strListP;
} Globals;
With the vital #include <msstrngl.h> your MDL code should compile without an error message about an undefined type.
It's common practise to use a common header (.h) file that you #include in your implementation (.mc) file and in your resource data (.r) files.
Unfortunately, the resource compiler (rcomp.exe) only knows about data and structures.
It doesn't know about pointers.
When rcomp.exe finds a pointer in a data structure, it doesn't know what to do with it and issues an error.
You need to hide pointers from the resource compiler in your header file.
Bentley antipated this problem (or maybe they discovered it before we did) and define a symbol resource when rcomp.exe is executing.
You can test for that symbol in your header file to hide structure definitions that contain pointers …
#if !defined (resource)
#include <msstrngl.h>
typedef struct globals
{
int intVal;
double doubleVal;
char stringVal [45];
StringList* strListP;
} Globals;
#endif /* !defined (resource) */
Return to MDL articles index.