Tuesday, May 09, 2006

Using MACROS inside ObjectARX

Hello,

Sometimes is very tedious to create functions to access your custom object members. In some situations we also forget to call the proper assert method before edit or read the variable. This can cause you some trouble later.

I have decided to make a bunch of macros to make our life easier. Using these macros you can speed up your development.

How to use:

First, place the following macros inside a general .H file, say ArxMacros.h, and place it inside your project:

#define _ARX_SGET(x) LPCTSTR Get##x(void) const { assertReadEnabled(); return _s##x; }
#define _ARX_SSET(x) void Set##x(LPCTSTR sValue) { assertWriteEnabled(); _s##x.Format("%s", sValue); }
#define _ARX_IGET(x) int Get##x(void) const { assertReadEnabled(); return _i##x; }
#define _ARX_ISET(x) void Set##x(int iValue) { assertWriteEnabled(); _i##x = iValue; }
#define _ARX_BGET(x) BOOL Get##x(void) const { assertReadEnabled(); return _b##x; }
#define _ARX_BSET(x) void Set##x(BOOL bValue) { assertWriteEnabled(); _b##x = bValue; }
#define _ARX_FGET(x) float Get##x(void) const { assertReadEnabled(); return _f##x; }
#define _ARX_FSET(x) void Set##x(float fValue) { assertWriteEnabled(); _f##x = fValue; }
#define _ARX_DTGET(x) COleDateTime Get##x(void) const { assertReadEnabled(); return _dt##x; }
#define _ARX_DTSET(x) void Set##x(COleDateTime dtValue) { assertWriteEnabled(); _dt##x = dtValue; }
#define _ARX_LGET(x) long Get##x(void) const { assertReadEnabled(); return _l##x; }
#define _ARX_LSET(x) void Set##x(long lValue) { assertWriteEnabled(); _l##x = lValue; }
#define _ARX_DGET(x) double Get##x(void) const { assertReadEnabled(); return _d##x; }
#define _ARX_DSET(x) void Set##x(double dValue) { assertWriteEnabled(); _d##x = dValue; }
#define _ARX_OBJIDGET(x) AcDbObjectId Get##x(void) const { assertReadEnabled(); return _id##x; }
#define _ARX_OBJIDSET(x) void Set##x(AcDbObjectId lValue) { assertWriteEnabled(); _id##x = lValue; }
#define _ARX_PT3DGET(x) const AcGePoint3d& Get##x(void) const { assertReadEnabled(); return _pt##x; }
#define _ARX_PT3DSET(x) void Set##x(AcGePoint3d ptValue) { assertWriteEnabled(); _pt##x = ptValue; }
#define _ARX_PT2DGET(x) const AcGePoint2d& Get##x(void) const { assertReadEnabled(); return _pt##x; }
#define _ARX_PT2DSET(x) void Set##x(AcGePoint2d ptValue) { assertWriteEnabled(); _pt##x = ptValue; }


Go to your custom object's class header and place them as follows:

_ARX_SGET(Name)
_ARX_SSET(Name)
_ARX_DGET(Number)
_ARX_DSET(Number)
_ARX_PT3DGET(Point)
_ARX_PT3DSET(Point)

When you compile the code these macros will expand as follows:
void SetName(LPCTSTR sValue);
LPCTSTR GetName(void) const;

void SetNumber(double dValue);
double GetNumber(void) const;

void SetPoint(AcGePoint3d ptValue);
const AcGePoint3d& GetPoint(void) const;

As they refer to local class variable we need to add them will proper naming convention (note that I put a prefix on each variable inside macros using a letter to identify its type):

CString _sName;
double _dNumber;
AcGePoint3d _ptPoint;

You don't need to place anything else inside your class CPP file. Of course if you need a special behavior when accessing your members you will need to fully create the access function.

That's it, now you have your access functions ready to use.

Fernando.

3 comments :

J. Daniel Smith said...

Your macros should have names along the lines of "ARX_SGET_" or "_arx_sget" instead of "_ARX_SGET" so that they are compliant with the C++ Standard.

Identifiers starting with an underscore followed by a capital letter or two underscores are reserved for "the implementation". I avoid the technicalities by simply not using leading underscores.

Fernando Malard said...

Hello,

Yes, in fact the idea is only to show what can be done with macros. The macro name and variable naming convention may vary from developer to developer. Some follow global standards and other follow their own standards.

Regards,
Fernando.

J. Daniel Smith said...

Unless you're writing a C++ compiler or something else that could be construed as "the implementation", you’re simply *NOT ALLOWED* to use a name such a _ARX_SGET. It’s not a matter of following "naming conventions", it's about being compliant with the C++ Standard.

While there's little need to change existing code that already compiles, at the same time there's no need to implicitly encourage bad practices either--especially when the expectation is that someone will cut-and-paste your code as-is.