The MicroStation Development Library (MDL) and MicroStationAPI provide APIs for developers wanting to create custom applications for MicroStation® from Bentley Systems. We create a MicroStation application as a DLL, written using C++ and built with the Microsoft C++ compiler and linker provided with Visual Studio.
When editing your source code, you can choose whether to use Microsoft Visual Studio, Microsoft Visual Studio Code, or one of your favourite text editors.
When building your app, you can use Visual Studio or the Bentley Systems make (bmake) tools.
Q
I want to declare an C++ typedef
in a struct
(to pass some data to my application)
but I am getting an error saying some C++ typedef is not a valid type.
How can I include some C++ typedef
as a valid type in my header file?
Q
I don't know how to get from DPoint3dCP
to DPoint3dCR
.
I scanned the C++ header files and didn't find anything …
Where are those Hungarian notation suffixes defined?
Q What's an opaque pointer?
A
The MicroStationAPI defines an enormous variety of struct
s and typedef
s in its header files.
In a default MicroStation CONNECT installation, those headers are in a sub-folder of the SDK's
\include
folder, similar to this …
C:\Program Files\Bentley\MicroStation\SDK\include
As an C++ 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.
The typedef
alias mechanism has existed since the C language was conceived.
C++ 11 and later provide a new type alias mechanism that some consider easier to read.
For example …
using DPoint3dP = DPoint3d*;
The using
mechanism has benefits other than clarity.
You'll find it handy when you venture into
template meta-programming.
However, the MicroStationAPI sticks, as far as I can see, exclusively to the typedef
alias mechanism.
That doesn't mean you have to use typedef
:
the using
idiom means just the same to the C++ compiler, and you may find it easier to read.
Because MicroStation's resource compilers have grown from C-style programming, structs that are shared with the MicroStation's Dialog Manager must conform to C rules.
A
C++ and the MicroStationAPI define and use a large number of typedef
s.
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; MicroStationAPI_someFunction (&var, … );
What's the data type in the function prototype? A pointer to DPoint3d
(DPoint3d*
).
void MicroStationAPI_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. }; MicroStationAPI_someFunction (&var, … );
…but the function prototype changes …
void MicroStationAPI_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 typedef
s, the above function prototypes could be written …
void MicroStationAPI_someFunction (DPoint3dP copyDataToMe, … ); void MicroStationAPI_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 C++ 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 typedef
s) 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.
You're probably aware that C++ 11 and later have smart pointers.
You're free to use the standard smart pointers (std::unique_ptr
) and std::shared_ptr
)
in your own code.
When interfacing with the MicroStationAPI we must use smart pointers defined by Bentley Systems.
For an explanation of Bentley smart pointers, please address your queries to the
Be Communities
MicroStation Programming Forum.
The RefCountedPtr
class is declared in header file RefCounted.h
.
You'll often find it when looking up type definitions in the MicroStationAPI help documentation.
For example, here's the alias of a smart pointer to a ClipVector
…
typedef RefCountedPtr <DgnPlatform::ClipVector> ClipVectorPtr
The following C++ #include
files (there may be more that I haven't found) 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 typedef
s
from a given type …
BENTLEY_TYPEDEFS
ADD_TYPEDEFS1
DEFINE_GEOM_STRUCT
DEFINE_GEOM_CLASS
The macros behave differently in C++ compared to C or MDL and place the typedef
s 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 typedef
s for your struct's pointer and const pointer,
in this case …
// C++ 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 C++ reference typedef
s.
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
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 …
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 C++ 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 C++ compiler doesn't like that struct
definition 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 C++ 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; WChar stringVal [45]; StringList* strListP; } Globals;
With the vital #include <msstrngl.h>
your C++ code should compile without an error message about an undefined type.
It's common practise to use a header (.h
) file that you #include
in your implementation (.cpp
) file and in your resource data (.r
) files.
Unfortunately, the Bentley Systems 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 Systems anticipated that 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;
WChar stringVal [45];
StringList* strListP;
} Globals;
#endif // !defined (resource)
Return to C++ articles index.
Post questions about C++ and the MicroStationAPI to the MicroStation Programming Forum.