- 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.
 
 
  
 |   
 
 
 
 |