- UID
- 1
- 积分
- 16111
- 精华
- 贡献
-
- 威望
-
- 活跃度
-
- D豆
-
- 在线时间
- 小时
- 注册时间
- 2002-1-3
- 最后登录
- 1970-1-1
|
马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
×

- Cool helper classes for ARX part 2: ARX goes STL
- ID 1696
- Applies to: AutoCAD 2000I
- AutoCAD 2002
- AutoCAD 2000
-
- Date 1/22/2002
-
- This document is part of AutoCAD Utilities
- Question
- This solution is an extension of the helper classes presented in <Solution 1695>
- "ACAD: COOL HELPER CLASSES FOR ARX" which is a prerequisite for this solution
- and should be looked at first.
- Answer
- I generalized the template class Apply even more, which now works only with
- MSVC 6.0 now, as I'm using template member functions in template classes.)
- Up to now (version 1), Apply took a function pointer as an argument for the
- constructor. This function pointer had to be a pointer to a function that takes
- one argument.
- Now (version 2), Apply takes a template parameter as argument for the constructor.
- Valid is everything that can be applied a '()' operation on. This CAN be - of
- course - a function pointer, like before. But it also can be a class object
- of a class with in overloaded '()' operator.
- You can do really cool things with it. For example, I wanted to have an apply
- function which takes not only one argument, as in ...
- Adesk::Boolean applyFunc( AcDbEntity * );
- ... but two arguments, as in ...
- Adesk::Boolean transform( AcDbEntity *p, const AcGeMatrix3d& m )
- {
- p->transformBy( m );
- return Adesk::kTrue;
- }
- ... because I wanted to apply an user-supplied transformation matrix to each
- entity in a block.
- Instead of modifying the Apply class (which is what I didn't want to do because
- I wanted to keep this class as minimal as possible), you can solve this entirely
- by using Template helper classes and -functions.
- The name of the first template helper class is '_2', which makes it possible
- to apply a function that takes two arguments.
- The calling syntax is:
- AcGeMatrix3d mat;
- mat.setToTranslation( AcGeVector3d( 5.0, 2.0, 0.0 ) );
- Apply<BlockTableRecord>( pMs, _2( transform, mat ), kForWrite );
- Now how does this work?
- _2 has a constructor which takes two template arguments. (transform and mat).
- The constructor just stores these two objects as data members. Now comes the
- trick: _2 has an overloaded '()' operator, which takes one argument; because
- this is exactly what apply needs. The operator '()' in turn just calls it's
- constructor's argument #1, passing the constructor's argument #2 as an additional
- argument to the apply function (argument #1).
- Sample code:
- _2::_2( T t, S s ) : m_t( t ), m_s( s ) { }
- Adesk::Boolean _2::operator () (P* pEnt)
- { return m_t( pEnt, s ); }
- That's it. It's really as simple as this. Still reading? :-)
- Another neat template helper class is 'If'. The If constructor takes (again)
- two arguments: the first one is a filter function, and the second one is the
- apply function. The apply function will be called only if a specific object
- passes the filter function. If you consider the following filter function:
- Adesk::Boolean isAcDbLine( AcDbEntity *pEnt )
- {
- return pEnt->isKindOf( AcDbLine::desc() );
- }
- ... then the calling sequence
- Apply<BlockTableRecord>( pMs,
- If( isAcDbLine, applyFunc ), kForRead );
- ... calls applyFunc() only if the entity is an AcDbLine. The implementation
- of If is similar to _2:
- If::If( T t, S s ) : m_t( t ), m_s( s ) { }
- Adesk::Boolean If::operator () (P* pEnt)
- { return m_s( pEnt ) ? m_t( pEnt ) : Adesk::kTrue; }
- This code above calls the apply function (m_t) only if the object passes the
- filter (m_s). This means, the following code:
- Apply<BlockTableRecord>( pMs,
- If( isAcDbLine, applyFunc ), kForRead );
- ... causes that applyFunc() is called for each Line in the model space. Of course,
- you can combine these two classes, as in:
- Apply<BlockTableRecord>( pMs,
- If( isAcDbLine, _2( transform, mat ) ), kForWrite );
- This applies the transformation matrix 'mat' to each line in model space. But
- the first parameter to If (the filter function) is also templatized, so it can
- also be a class object instead of a function pointer. Which means: taken the
- following filter function (note that this one also takes TWO arguments [you
- guess what happens...?]):
- Adesk::Boolean hasColor( AcDbEntity *pEnt, int color )
- {
- return pEnt->colorIndex() == color;
- }
- ..., if you use this like the following:
- Apply<BlockTableRecord>( pMs,
- If( _2( hasColor, 4 ), _2( transform, mat ) ), kForWrite );
- ... causes each entity in model space with the colorIndex of 4 to be translated
- by 'mat'. On the other hand,
- Apply<BlockTableRecord>( pMs,
- If( _2( hasColor, 6 ), _2( transform, mat2 ) ), kForWrite );
- ... will apply the matrix 'mat2' to each entity with the colorIndex of 6.
- Is this flexible? Is this cool? Or what?
- All this is done by only a few lines of code. Just a few minimal template classes:
- Apply, Filter and TwoArgs. It's completely customizable and extendible by users
- of these classes. (You can add helpers for functions with three parameters,
- and so on.) You can apply every complex operation you want on every object with
- every imaginable search-criteria in the database. Needless to say, Apply doesn't
- work only with entities, it works also with Dictionaries, and all other symbol tables.
- There's really no limit for improvements. Be creative! For example, the function
- 'isAcDbLine( AcDbEntity *pEnt )' above can be generalized to 'isKindOf( AcDbObject
- *pObj, AcRxClass *desc )', so the term
- Apply<BlockTableRecord>( pMs,
- If( isAcDbLine, testFunc1 ), kForRead );
- ... can be also be formulated as ...
- Apply<BlockTableRecord>( pMs,
- If( _2( isKindOf, AcDbLine::desc() ), testFunc1 ), kForRead );
- Or, what about two more classes 'And' and 'Or', which apply the following operation
- on their two parameters:
- Adesk::Boolean And::operator () ( P* p)
- { return m_t( p ) && m_s( p ); }
- Adesk::Boolean Or::operator () ( P* p)
- { return m_t( p ) || m_s( p ); }
- ... so you can use them as boolean operations for the Apply class:
- Apply<BlockTableRecord>( pMs,
- If( Or( _2( isKindOf, AcDbLine::desc() ),
- _2( isKindOf, AcDbCircle::desc() ) ),
- _2( transform, mat ) ),
- kForWrite );
- This code above will apply the transformation matrix 'mat' to all circles and
- lines in the model space, while ...
- Apply<BlockTableRecord>( pMs,
- If( And( _2( isColor, 3 ),
- _2( isKindOf, AcDbCircle::desc() ) ),
- _2( transform, mat ) ),
- kForWrite );
- ... will apply 'mat' only to all circles with the color 3.
- If you're familiar with STL (the Standard Template Library), then the code presented
- here will be familiar to you as well: this is *exactly* how STL works. I mean,
- applying generic functions to all sort of containers, with all sort of combinations.
- Full source code is attached.
|
|