Friday, October 18, 2013

Partial template specialization and boost::enable_if

After dabbling with templates for a while, one question that nearly always comes up among beginning C++ developers is 'how do I partially specialize this template'? Now, unless you happen to be referring to a class or struct template, you're generally out of luck. That is, you cannot partially specialize a method or function template like so:

template< typename A, typename B >
void myMethod( A a, B b )
{}

template< typename A >
void myMethod< A, float >( A a, float b )
{ }

Fortunately, you CAN specialize class templates, e.g.:

template< typename A, typename B >
struct myStruct
{
   static void fn( A a, B b ) { }
}

template< typename A >
struct myStruct< A, float >
{
   static void fn( A a, float b ) { }
}

which means that in many cases you're able to perform your partial specialization by breaking the desired functionality out into a class or struct. So you'd be able to do this:

template< typename A, typename B >
void myMethod( A a, B b )
{
    myStruct< A, B >::fn( a, b );
}

and let the struct deal with the partial specialization.

Now this is all good and well in many cases, but what if you wanted to specialize your template on some more complex, or abstract, predicate than a simple type? Say, do something special for all integral types, or all floating point types? Or something even more complex like one specialization for when a  type is either X, Y or Z, and has a virtual destructor?
Enter boost::enable_if, and perhaps as importantly, Boost's type traits. Beginning with the former, this nearly trivial little construct (in the boost namespace):

template
struct enable_if_c {
  typedef T type;
};

template 
struct enable_if_c {};

among other things lets you specialize your templates on any condition that can be expressed as an "integral constant" (think of it as any integer that is known at compile time). Also, the more generic

template 
struct enable_if : public enable_if_c {};

does exactly the same, but instead of directly taking an integral constant it assumes a template parameter type that has an integral constant member 'value' defined. This allows you to wrap your integral constant logic into classes, and perhaps more importantly to use any of a slew of Boost's extensive collection of 'type traits' classes, such as boost::is_same, boost::is_float, and boost::is_integral.

So how do you actually implement any of this in practical terms? Returning to our previous example, all we really need to do is extend the myStruct template by one more template argument:

template< typename A, typename B, class Enable = void >
struct myStruct
{
   static void fn( A a, B b ) { }
}

Then, a predicate-driven specialization can be created e.g. like so:

template< typename A, typename B >
struct myStruct< A, B, boost::enable_if< boost::is_same< A, B > >::type >
{
   static void fn( A a, A b ) { }
}

So what happened here? Basically, since we added the third template parameter 'Enable' (which obviously could have been called anything, like 'MumboJumbo' or 'C') with a default 'void' argument,  IFF the predicate is_same holds true for some types A and B, the enclosing enable_if template will see a 'true' value member and thus its specialization for 'false' won't trigger, and so its type will be defined, and thus our newly created specialization will trigger (which in this particular instance means we can use A as the type of both arguments to fn()). In any other case - that is, unless A and B are of the same type - is_same will evaluate to 'false', which means enable_if will not have a type member defined and thus the specialization will not be considered.

Let's try something slightly more complex!

template< typename A, typename B >
struct myStruct< A, B, boost::enable_if_c< boost::is_float< A >::value || boost::is_integral< A >::value >::type >
{
   static void fn( A a, B b ) { return a + static_cast< A >( b ); }

}

In this example, I assert that both A and B are floating point or integral types, and so I can safely cast B into A (barring overflowing integral types) and add the results. Note that, in order to be able to apply an OR operation to the underlying integral constants, I've resorted to accessing the raw ::value members of each predicate and passing the resulting integral constant to the enable_if_c template. Another option would have been to use boost::ice_or which is actually recommended for certain older compilers, but since it's hidden in type_traits/detail I'm hesitant to using it in portable and/or future-proof code.


Friday, January 04, 2013

MTransformationMatrix and the Maya Python API

Here's my approach to how to work around the numerous limitations in the Maya Python API wrt. decomposing a matrix into its translate/rotate/scale components using MTransformationMatrix. This assumes you have a list of floats representing the elements of the matrix you want to decompose, and want to obtain the t/r/s components as 3-vectors stored as Python lists.

# Identity rotation/scale with translation (2,3,4):
listOfFloats = 
  [1.0, 0.0, 0.0, 0.0, 
   0.0, 1.0, 0.0, 0.0, 
   0.0, 0.0, 1.0, 0.0,
   2.0, 3.0, 4.0, 1.0]
# create MMatrix from list
mm = om.MMatrix()
om.MScriptUtil.createMatrixFromList(listOfFloats,mm)
# create MTransformationMatrix from MMatrix
mt = om.MTransformationMatrix(mm)
# translation is easy to obtain
translate = mt.translation(om.MSpace.kWorld)
# rotation needs to go past Quaternion representation due to API 
# limitations
rotate = mt.rotation().asEulerRotation()
# for scale we need to utilize MScriptUtil to deal with the native
# double pointers
scaleUtil = om.MScriptUtil()
scaleUtil.createFromList([0,0,0],3)
scaleVec = scaleUtil.asDoublePtr()
mt.getScale(scaleVec,om.MSpace.kWorld)
scale = [om.MScriptUtil.getDoubleArrayItem(scaleVec,i) for i in range(0,3)]

print 'Translation: %s, rotation: %s, scale: %s'%(translate,rotate,scale)

Please let me know if you know of neater ways of doing any of the above!

Monday, April 16, 2012

Tupac Hologram at Coachella 2012

The tastefulness of this is in the eye of the beholder I suppose, but apparently a lot of people are liking it: a digital holographic Tupac (!!) rapping with Snoop at Coachella 2012. Here's Digital Domain's press release announcing their participating in this becoming reality:

http://phx.corporate-ir.net/phoenix.zhtml?c=246637&p=irol-newsArticle&ID=1683477

Apparently Mom was pleased:

http://www.tmz.com/2012/04/16/tupac-mother-afeni-shakur-coachella-hologram/#.T40niO0Z54U

and here's the Journal's take on it:

http://online.wsj.com/article/SB10001424052702304818404577348243109842490.html

Here's hoping Digital Domain will be permitted and willing to release a how-to eventually so everyone can appreciate just how much work goes into one of these projects. 

Tuesday, February 14, 2012

VES Award for Gears of War 3: Dust to Dust

Digital Domain has won a VES Award for their work on the Gears of War 3 trailer "Dust to Dust", which features hair by "Samson", the digital hair pipeline I helped create. From the press release:

"Digital Domain talent was honored this week at the 10th Annual Visual Effects Society Awards. Gears of War 3 - Dust to Dust took home the award for Outstanding Visual Cinematography in a Broadcast Program or Commercial. Niles Heckman, Richard Morton, and Vernon Wilbert Jr. were there to accept the accolade."

Congrats to Niles, Richard, Vernon and the rest of the DD commercials team!

Niles Heckman, Richard Morton, and Vernon Wilbert Jr. at the 2012 VES Awards. (Photo credit: Alex J. Berliner/abi images.)