# file: generate_simtkcommon_source.py

import os
import sys
import re
from pygccxml import declarations
from pyplusplus import module_builder
from pyplusplus.module_builder.call_policies import *
from pyplusplus import function_transformers as FT
from simtk_wrap_utils import *
from doxygen_doc_extractor import doxygen_doc_extractor

def generate_simtkcommon_source():
    """
    Use pyplusplus to generate boost.python source code for simtk commmon module.
    See pyplusplus documentation at 
        http://www.language-binding.net/pyplusplus/pyplusplus.html
    """
    mb = create_module_builder(includes=['simtkcommon/wrap_simtkcommon.h']) 
    
    # Delegate wrapping details to other subroutines, to keep this one from getting too big
    mb.namespace('SimTK').include()
    perform_usual_wrapping_tasks(mb)
    disambiguate_typedef_aliases(mb)
    expose_cnts(mb)
    expose_arrays(mb)
    expose_mats(mb)
    wrap_conjugate(mb)
    expose_indexes(mb)
    expose_abstract_value(mb)
    expose_vectors(mb)
    expose_decorative_geometry(mb)
    expose_stage(mb)
    expose_state(mb)
    expose_event(mb)
    expose_system(mb)
    expose_subsystem(mb)
    expose_defaultsystemsubsystem(mb)
    expose_decorative_shapes(mb)
    expose_inertia(mb)
    expose_massproperties(mb)
    expose_measure(mb)
    expose_ntraits(mb)
    exclude_pimpl(mb)
    expose_rowvectorview(mb)
    expose_study(mb)
    expose_thisneg(mb)
    expose_free_functions(mb)
    expose_global_variables(mb)
    expose_exceptions(mb)
    expose_pathname(mb)
    expose_plugin(mb)
    expose_matrixoutline(mb)
    expose_atomicinteger(mb)
    expose_parallelexecutor(mb)
    expose_random(mb)
    wrap_ostream_methods(mb)
    expose_xml(mb)
    wrap_mat33(mb)

    exclude_instantiator(mb)
    shorten_array_aliases(mb)
    exclude_matrixhelpers(mb)
    exclude_registration_order_issues(mb)
    exclude_widest(mb)
    exclude_impl_methods(mb)
    exclude_operators(mb)
    expose_increment_operators(mb)
    exclude_strange_classes(mb)
    wrap_properties(mb)
    
    # Don't rewrap anything already wrapped by std module
    # See http://www.language-binding.net/pyplusplus/documentation/multi_module_development.html
    mb.register_module_dependency('external/std/generated_code/')
    mb.register_module_dependency('simtkvecs/generated_code/')
    
    extractor = doxygen_doc_extractor()

    mb.build_code_creator(module_name='_common', doc_extractor=extractor)
    
    mb.split_module(os.path.join(os.path.abspath('.'), 'simtkcommon', 'generated_code'))

    # 3>src\simtkcommon\generated_code\std_vector_simtk_String.pypp.cpp(7) : fatal error C1083: Cannot open include file: '_simtk_String__value_traits.pypp.hpp': No such file or directory
    # these value_traits are included in simtk_indexing_helpers.h
    missing_file = os.path.join(os.path.abspath('.'), 'simtkcommon', 'generated_code', '_simtk_String__value_traits.pypp.hpp')
    if not os.path.exists(missing_file):
        open(missing_file, "w").close()

    # If all succeeds, record this accomplishment by touching a particular file
    open(os.path.join(os.path.abspath('.'), 'simtkcommon', 'generated_code', 'generate_simtkcommon.stamp'), "w").close()

def exclude_instantiator(mb):
    mb.free_function('instantiate').exclude()

def wrap_mat33(mb):
    # Mat33 has some long named inner classes that cause problems for epydoc generator
    mat33 = mb.class_('Mat<3, 3, double, 3, 1>')
    mat33.classes().exclude()
    mb.class_('Mat<3, 3, double, 1, 3>').classes().exclude()

def wrap_conjugate(mb):
    mb.class_('conjugate<double>').alias = "conjugate_double"
    mb.class_('conjugate<float>').exclude()
    mb.class_('conjugate<long double>').exclude()

def expose_xml(mb):
    # Link errors:
    #6>Xml.pypp.obj : error LNK2019: unresolved external symbol "__declspec(dllimport) public: class SimTK::String __thiscall SimTK::Xml::getPathname(void)const " (__imp_?getPathname@Xml@SimTK@@QBE?AVString@2@XZ) referenced in function "void __cdecl register_Xml_class(void)" (?register_Xml_class@@YAXXZ)
    #6>Xml.pypp.obj : error LNK2019: unresolved external symbol "__declspec(dllimport) public: void __thiscall SimTK::Xml::clear(void)" (__imp_?clear@Xml@SimTK@@QAEXXZ) referenced in function "void __cdecl register_Xml_class(void)" (?register_Xml_class@@YAXXZ)
    #6>error: command '"c:\Program Files\Microsoft Visual Studio 9.0\VC\BIN\link.exe"' failed with exit status 1120
    #6>Project : error PRJ0019: A tool returned an error code from "Installing PySimTK to local directory C:/Documents and Settings/Christopher Bruns/My Documents/svn/simtk_python/test_home/lib/python"
    #6>Xml.pypp.obj : error LNK2019: unresolved external symbol "__declspec(dllimport) public: class SimTK::Xml::Text __thiscall SimTK::Xml::Element::insertText(class SimTK::Xml::const_node_iterator const &,class SimTK::String const &)" (__imp_?insertText@Element@Xml@SimTK@@QAE?AVText@23@ABVconst_node_iterator@23@ABVString@3@@Z) referenced in function "void __cdecl register_Xml_class(void)" (?register_Xml_class@@YAXXZ)
    #6>Xml.pypp.obj : error LNK2019: unresolved external symbol "__declspec(dllimport) public: class SimTK::Xml::Text __thiscall SimTK::Xml::Element::appendText(class SimTK::String const &)" (__imp_?appendText@Element@Xml@SimTK@@QAE?AVText@23@ABVString@3@@Z) referenced in function "void __cdecl register_Xml_class(void)" (?register_Xml_class@@YAXXZ)
    xml = mb.class_('Xml')
    xml.member_function('getPathname').exclude()
    xml.member_functions('clear', arg_types=[]).exclude() # Somehow there is more than one?!?
    element = xml.class_('Element')
    # element.member_function('insertText').exclude()
    # element.member_function('appendText').exclude()
    # 7>Xml.pypp.obj : error LNK2019: unresolved external symbol "__declspec(dllimport) public: void __thiscall SimTK::Xml::Unknown::setContents(class SimTK::String const &)" (__imp_?setContents@Unknown@Xml@SimTK@@QAEXABVString@3@@Z) referenced in function "void __cdecl register_Xml_class(void)" (?register_Xml_class@@YAXXZ)
    # 7>Xml.pypp.obj : error LNK2019: unresolved external symbol "__declspec(dllimport) public: class SimTK::String const & __thiscall SimTK::Xml::Unknown::getContents(void)const " (__imp_?getContents@Unknown@Xml@SimTK@@QBEABVString@3@XZ) referenced in function "void __cdecl register_Xml_class(void)" (?register_Xml_class@@YAXXZ)
    unknown = xml.class_('Unknown')
    unknown.member_function('getContents').exclude()
    unknown.member_function('setContents').exclude()

def expose_random(mb):
    #~ WARNING: SimTK::Random [class]
    #~ > warning W1031: `Py++` will generate class wrapper - user asked to
    #~ > expose non - public member function "Random"
    r = mb.class_("Random")
    r.constructors().exclude()
    r.no_init = True

def expose_parallelexecutor(mb):
    #~ WARNING: SimTK::ParallelExecutor [class]
    #~ > warning W1028: `Py++` will generate class wrapper - class contains
    #~ > definition of nested class "Task", which requires wrapper class
    # Does not help, the warning is still there
    t = mb.class_('ParallelExecutor').class_('Task')
    t.alias = 'ParallelExecutor_Task'
    t.no_init = True
    t.noncopyable = True
    t2 = mb.class_('Parallel2DExecutor').class_('Task')
    t2.alias = 'Parallel2DExecutor_Task'
    t2.no_init = True
    t2.noncopyable = True

def expose_defaultsystemsubsystem(mb):
    d = mb.class_('DefaultSystemSubsystem')
    # d.include()
    for fn in d.member_functions(lambda fn: fn.name.startswith('addEvent')):
        fn.call_policies = with_custodian_and_ward(1, 2) # really?
        fn.add_transformation( FT.transfer_ownership(0) )
        # Alias can get screwy for overloaded member function
        fn.transformations[-1].alias = fn.alias

def expose_atomicinteger(mb):
    #~ WARNING: SimTK::AtomicInteger::atomic [variable]^M
    #~ > compilation error W1036: `Py++` can not expose pointer to Python^M
    #~ > immutable member variables. This could be changed in future.
    mb.class_('AtomicInteger').variable('atomic').exclude()

def expose_matrixoutline(mb):
    #~ WARNING: void SimTK::MatrixOutline::getMinimumSize(int & m, int & n) const [member function]^M
    #~ > execution error W1009: The function takes as argument (name=m, pos=0)^M
    #~ > non-const reference to Python immutable type - function could not be^M
    #~ > called from Python. Take a look on "Function Transformation"^M
    #~ > functionality and define the transformation.
    mb.class_('MatrixOutline').member_function('getMinimumSize').add_transformation(
        FT.output('m'), FT.output('n'))

def expose_plugin(mb):
    #~ WARNING: static bool SimTK::Plugin::deconstructLibraryName(std::string const & name, bool & isAbsolutePath, std::string & directory, std::string & libPrefix, std::string & baseName, std::string & debugSuffix, std::string & extension) [member function]^M
    #~ > execution error W1009: The function takes as argument^M
    #~ > (name=isAbsolutePath, pos=1) non-const reference to Python immutable^M
    #~ > type - function could not be called from Python. Take a look on^M
    #~ > "Function Transformation" functionality and define the transformation.
    plugin = mb.class_('Plugin')
    plugin.member_function('deconstructLibraryName').add_transformation(
        FT.output('isAbsolutePath'), FT.output('directory'), FT.output('libPrefix'), 
        FT.output('baseName'), FT.output('debugSuffix'), FT.output('extension'))
    #~ WARNING: SimTK::Plugin::m_handle [variable]^M
    #~ > compilation error W1036: `Py++` can not expose pointer to Python^M
    #~ > immutable member variables. This could be changed in future.
    plugin.variable('m_handle').exclude()
    
def exclude_registration_order_issues(mb):
    #~ WARNING: bool SimTK::canStoreInInt(bool arg0) [free function]^M
    #~ > execution error W1010: The function introduces registration order^M
    #~ > problem. For more information about the problem read "registration^M
    #~ > order" document.Problematic functions list:           bool^M
    #~ > SimTK::canStoreInInt(long long unsigned int u) [free function]^M
    #~ > bool SimTK::canStoreInInt(long long int i) [free function]^M
    #~ > bool SimTK::canStoreInInt(long unsigned int u) [free function]^M
    #~ > bool SimTK::canStoreInInt(long int i) [free function]            bool^M
    #~ > SimTK::canStoreInInt(unsigned int u) [free function]            bool^M
    #~ > SimTK::canStoreInInt(int arg0) [free function]            bool^M
    #~ > SimTK::canStoreInInt(short unsigned int arg0) [free function]^M
    #~ > bool SimTK::canStoreInInt(short int arg0) [free function]^M
    #~ > bool SimTK::canStoreInInt(signed char arg0) [free function]^M
    #~ > bool SimTK::canStoreInInt(unsigned char arg0) [free function]^M
    #~ > bool SimTK::canStoreInInt(char arg0) [free function]
    mb.free_functions('canStoreInInt').exclude()
    mb.free_functions('isNonnegative').exclude()
    mb.free_functions('canStoreInNonnegativeInt').exclude()
    
def expose_pathname(mb):
    #~ WARNING: static void SimTK::Pathname::deconstructPathname(std::string const & name, bool & 
    #~ isAbsolutePath, std::string & directory, std::string & fileName, std::string & extension) [
    #~ member function]
    #~ > execution error W1009: The function takes as argument
    #~ > (name=isAbsolutePath, pos=1) non-const reference to Python immutable
    #~ > type - function could not be called from Python. Take a look on
    #~ > "Function Transformation" functionality and define the transformation.
    pathname = mb.class_('Pathname')
    pathname.member_function('deconstructPathname').add_transformation(
        FT.output('isAbsolutePath'),
        FT.output('directory'), FT.output('fileName'), 
        FT.output('extension'))

def expose_exceptions(mb):
    #~ WARNING: SimTK::Exception::ErrorCheck::ErrorCheck(char const * fn, int ln, char const * assertion, char const * whereChecked, char const * fmt, ...) [constructor]
    #~ > warning W1053: `Py++` will not expose function
    #~ > "SimTK::Exception::ErrorCheck::ErrorCheck(char const * fn, int ln,
    #~ > char const * assertion, char const * whereChecked, char const * fmt,
    #~ > ...) [constructor]" - the function has variable-argument list,
    #~ > spicified by ellipsis (...).
    for cls in mb.namespace('SimTK').namespace('Exception').classes():
        for ctor in cls.constructors(allow_empty=True):
            for arg in ctor.arguments:
                if arg.ellipsis:
                    ctor.exclude()

def exclude_widest(module_builder):
    mb = module_builder
    # File names are too long with Wider<... and Widest<... classes
    mb.classes(lambda c: c.name.startswith('Widest<')).exclude()
    mb.classes(lambda c: c.name.startswith('Wider<')).exclude()
    mb.classes(lambda c: c.name.startswith('CNT<')).exclude()

def expose_global_variables(module_builder):
    mb = module_builder
    # compile error
    mb.variables('keyLock').exclude()
    # global variables that are references to int or double cause compile error
    for var in mb.variables():
        if declarations.is_reference(var.type):
            if declarations.is_arithmetic(var.type.base):
                var.exclude()

def expose_thisneg(module_builder):
    mb = module_builder
    mb.member_functions('getContiguousData').exclude()
    mb.member_functions('updContiguousData').exclude()

def expose_study(module_builder):
    mb = module_builder
    study = mb.class_('Study')
    # Compile error Guts is not exposed
    study.member_function('adoptStudyGuts').exclude()
    study.member_function('getStudyGuts').exclude()
    study.member_function('updStudyGuts').exclude()
    for ctor in study.constructors(arg_types=[None]):
        if declarations.is_pointer(ctor.argument_types[0]):
            ctor.exclude()
    # Link errors - not implemented?
    study.member_function('updState').exclude()
    study.member_function('getState').exclude()
    study.member_function('getSystem').exclude()
    # only Study(System&) constructor has link error, so this is overkill
    study.constructors(arg_types=[None]).exclude()
    
def expose_rowvectorview(module_builder):
    mb = module_builder
    # compile errors
    for cls in module_builder.classes(lambda c: c.name.startswith('RowVectorView')):
        cls.member_functions('index', allow_empty=True).exclude()
        cls.member_functions('updIndex', allow_empty=True).exclude()
    # compile errors with RowVectorBase_<double>
    mb.member_operators('operator()').exclude()
    mb.member_functions('index').exclude()
    mb.member_functions('updIndex').exclude()

def exclude_pimpl(module_builder):
    # compile errors
    module_builder.classes(lambda c: c.name.startswith('PIMPL')).exclude()
    
def expose_ntraits(module_builder):
    mb = module_builder
    # compile error
    # methods that take a reference argument as their return value
    for fn_name in ['imag', 'positionalTranspose', 'transpose']:
        mb.member_functions(fn_name, arg_types=[None]).exclude()

def expose_measure(module_builder):
    mb = module_builder
    # Compile errors on Measure constructor
    mb.class_('Measure_<double>').constructors().exclude()
    mb.member_functions('cloneVirtual').exclude()
    mb.class_('AbstractMeasure').constructors().exclude()
    mb.classes('Implementation').exclude()

def expose_massproperties(module_builder):
    "Must come AFTER wrap_getters"
    mb = module_builder
    massprops = mb.class_('MassProperties')
    # compile error
    # massprops.constructor(arg_types=[]).exclude()
    # massprops.no_init = True
    # determine getMass as problem by trial-and-error
    massprops.member_function('getMass').call_policies = return_value_policy(copy_const_reference)
    # massprops.member_function('getMass').exclude()
    
def expose_inertia(module_builder):
    mb = module_builder
    inertia = mb.class_('Inertia_<double>')
    # default argument parsing trouble with Py++ causes compile failure
    # Wrong: Inertia__less__double__greater__exposer.def( bp::init< SimTK::Vec< 3, double, 1 > const &, bp::optional< SimTK::Vec< 3, double, 1 > const & > >(( bp::arg("moments"), bp::arg("products")=(SimTK::Vec<3, P, 1>)(0) )) );
    # Right: Inertia__less__double__greater__exposer.def( bp::init< SimTK::Vec< 3, double, 1 > const &, bp::optional< SimTK::Vec< 3, double, 1 > const & > >(( bp::arg("moments"), bp::arg("products")=(SimTK::Vec<3, double, 1>)(0) )) );
    for cls_name in 'Inertia_<double>', 'Gyration_<double>':
        cls = mb.class_(cls_name)
        for ctor in cls.constructors(arg_types=[None,None]):
            for arg in ctor.optional_args:
                arg.default_value = arg.default_value.replace("3, P, 1", "3, double, 1")
        for fn in cls.member_functions(lambda f: f.name.startswith('set'), arg_types=[None, None]):
            for arg in fn.optional_args:
                arg.default_value = arg.default_value.replace("3, P, 1", "3, double, 1")
    inertia.member_functions(lambda fn: fn.name.endswith('InPlace')).call_policies = return_self()
    #~ WARNING: SimTK::Inertia_<double> [struct]
    #~ > warning W1031: `Py++` will generate class wrapper - user asked to
    #~ > expose non - public member function "getAsGyration"
    inertia.member_function('getAsGyration').exclude()    
    inertia.member_function('updAsGyration').exclude()    
    inertia.member_function('errChk').exclude()

def expose_decorative_shapes(module_builder):
    mb = module_builder
    # pyplusplus gets some Vec3 default values goofy
    for cls_name in ['DecorativeBrick', 'DecorativeEllipsoid', 'DecorativeLine']:
        for ctor in mb.class_(cls_name).constructors():
            for arg in ctor.optional_args:
                s = arg.default_value.replace("(const double&)", "", 3)
                s = s.replace("(const double*)", "", 3)
                arg.default_value = s.replace("(&", "(", 3)   
    # Link errors
    for cls in mb.classes(lambda c: c.name.startswith('Decorative')):
        cls.member_functions('updDowncast', allow_empty=True).exclude()        
        cls.member_functions('isInstanceOf', allow_empty=True).exclude()        
        cls.member_functions('downcast', allow_empty=True).exclude()        

chainable_method_re = re.compile(r'^(add|set|upd)')
def is_chainable_method(fn):
    """
    Member functions that return a reference to the parent class,
    and begin with names like "add" or "set" (e.g. addBodyDecoration)
    probably return a reference to self.
    """
    if not chainable_method_re.match(fn.name):
        return False
    rt = fn.return_type
    if not rt:
        return False
    parent_ref = declarations.reference_t(declarations.declarated_t(fn.parent))
    return declarations.is_same(parent_ref, rt)

def wrap_chainable_methods(module_builder):
    module_builder.member_functions(is_chainable_method).call_policies = return_self()
    
def wrap_getters(module_builder):
    """
    Remember to run this BEFORE wrap_chainable_methods
    """
    mb = module_builder
    module_builder.member_functions(is_object_getter_method).call_policies = return_internal_reference()
    
getter_method_re = re.compile(r'^(get|upd)')
def is_object_getter_method(fn):
    if not getter_method_re.match(fn.name):
        return False
    rt = fn.return_type
    if not rt:
        return False
    return declarations.is_reference(rt)

def expose_unitvec(module_builder):
    mb = module_builder
    #~ WARNING: SimTK::UnitVec<double,3> [class declaration]
    #~ > execution error W1040: The declaration is unexposed, but there are
    #~ > other declarations, which refer to it. This could cause "no to_python
    #~ > converter found" run time error. Declarations:
    #~ > SimTK::UnitVec<double,3> const & SimTK::InverseTransform_<double>::x()
    #~ > const [member function]   SimTK::UnitVec<double,3> const &
    #~ > SimTK::InverseTransform_<double>::y() const [member function]
    #~ > SimTK::UnitVec<double,3> const & SimTK::InverseTransform_<double>::z()
    #~ > const [member function]
    # mb.class_('UnitVec<double,1>').include()
    # mb.class_('UnitRow<double,1>').include()
    # mb.class_('UnitVec<double,1>').include()
    # mb.class_('UnitRow<double,1>').include()
    # mb.class_('UnitVec<double,1>').exclude()
    # mb.class_('UnitRow<double,1>').exclude()
    # Attempt to debug screwiness with Constraint::PointOnLine constructor
    # by excluding UntiVec1
    #for cls_name in ['UnitVec<double,1>', 'UnitRow<double,1>']:
        #cls = mb.class_(cls_name)
        #cls.include()

def expose_rotation(module_builder):
    mb = module_builder
    #~ WARNING: SimTK::Rotation_<double> [struct]
    #~ > execution error W1040: The declaration is unexposed, but there are
    #~ > other declarations, which refer to it. This could cause "no to_python
    #~ > converter found" run time error. Declarations:
    #~ > SimTK::Rotation_<double> const &
    rot = mb.class_('Rotation_<double>')
    rot.include()
    invrot = mb.class_('InverseRotation_<double>')
    invrot.include()
    mb.enumeration('BodyOrSpaceType').include()
    mb.class_('CoordinateAxis').include()
    #~ WARNING: SimTK::CoordinateAxis::CoordinateAxis(SimTK::CoordinateAxis::XTypeAxis const & arg0) [constructor]
    #~ > compilation error W1005: `Py++` cannot expose function that takes as
    #~ > argument/returns instance of non-public class. Generated code will not
    #~ > compile.
    mb.class_('CoordinateAxis').constructors().exclude()
    #~ WARNING: SimTK::CoordinateAxis SimTK::CoordinateAxis::crossProduct(SimTK::CoordinateAxis const & axis2, int & sign) const [member function]
    #~ > execution error W1009: The function takes as argument (name=sign,
    #~ > pos=1) non-const reference to Python immutable type - function could
    #~ > not be called from Python. Take a look on "Function Transformation"
    #~ > functionality and define the transformation.
    mb.class_('CoordinateAxis').member_function('crossProduct').add_transformation(FT.output('sign'))
    #~ WARNING: SimTK::Quaternion_<double> [struct]
    #~ > execution error W1040: The declaration is unexposed, but there are
    #~ > other declarations, which refer to it. This could cause "no to_python
    #~ > converter found" run time error. Declarations:
    #~ > SimTK::Rotation_<double>::Rotation_(SimTK::Quaternion_<double> const &
    #~ > q) [constructor]   SimTK::Quaternion_<double>
    #~ > SimTK::Rotation_<double>::convertRotationToQuaternion() const [member
    #~ > function]   SimTK::Rotation_<double> & SimTK::Rotation_<double>::setRo
    #~ > tationFromQuaternion(SimTK::Quaternion_<double> const & q) [member
    #~ > function]
    quat = mb.class_('Quaternion_<double>')
    quat.include()
    # compile error...
    quat.member_function('normalizeThis').call_policies = return_self()
    #~ WARNING: SimTK::Vec<4, double, 1> [class]
    #~ > execution error W1040: The declaration is unexposed, but there are
    #~ > other declarations, which refer to it. This could cause "no to_python
    #~ > converter found" run time error. Declarations:
    #~ > SimTK::Quaternion_<double>::Quaternion_(SimTK::Vec<4, double, 1> const
    #~ > & q) [constructor]
    #~ > SimTK::Quaternion_<double>::Quaternion_(SimTK::Vec<4, double, 1> const
    #~ > & v, bool arg1) [constructor]   SimTK::Vec<4, double, 1> const &
    #~ > SimTK::Quaternion_<double>::asVec4() const [member function]
    #~ > SimTK::Vec<4, double, 1>
    #~ > SimTK::Quaternion_<double>::convertQuaternionToAngleAxis() const
    #~ > [member function]   void
    # quat.member_functions('setQuaternionFromAngleAxis').exclude()
    # quat.member_functions('convertQuaternionToAngleAxis').exclude()
    # quat.member_functions('asVec4').exclude()
    # quat.constructors(arg_types=[None, 'bool']).exclude() # TODO this might be too harsh
    # quat.constructors(arg_types=[None]).exclude() # TODO this might be too harsh
    #~ WARNING: SimTK::Mat<4,3,double,4,1> [class declaration]
    #~ > execution error W1040: The declaration is unexposed, but there are
    #~ > other declarations, which refer to it. This could cause "no to_python
    #~ > converter found" run time error. Declarations:    static
    #~ > SimTK::Mat<4,3,double,4,1> SimTK::Rotation_<double>::calcUnnormalizedN
    #~ > DotForQuaternion(SimTK::Vec<4, double, 1> const & qdot) [member
    #~ > function]   static SimTK::Mat<4,3,double,4,1>
    #~ > SimTK::Rotation_<double>::calcUnnormalizedNForQuaternion(SimTK::Vec<4,
    #~ > double, 1> const & q) [member function]
    # rot.member_functions('calcUnnormalizedNDotForQuaternion').exclude()
    # rot.member_functions('calcUnnormalizedNForQuaternion').exclude()
    # rot.member_functions('calcUnnormalizedNInvForQuaternion').exclude()
    # rot.member_functions('convertRotationToBodyFixedXY').exclude()
    # rot.member_functions('convertTwoAxesRotationToTwoAngles').exclude()
    # rot.member_functions('setRotationToBodyFixedXY').exclude()
    # rot.member_functions('convertRotationToAngleAxis').exclude()
    # rot.member_functions('convertQuaternionDotToAngVel').exclude()
    # rot.member_functions('convertAngVelToQuaternionDot').exclude()
    # rot.member_functions('convertAngVelDotToQuaternionDotDot').exclude()
    #~ WARNING: SimTK::Mat<3, 3, double, 3, 1> [class]
    #~ > execution error W1040: The declaration is unexposed, but there are
    #~ > other declarations, which refer to it. This could cause "no to_python
    #~ > converter found" run time error. Declarations:
    #~ > SimTK::Rotation_<double>::Rotation_(SimTK::Mat<3, 3, double, 3, 1>
    #~ > const & m, bool arg1) [constructor]
    #~ > SimTK::Rotation_<double>::Rotation_(SimTK::Mat<3, 3, double, 3, 1>
    #~ > const & m) [constructor]   SimTK::Mat<3, 3, double, 3, 1> const &
    #~ > SimTK::Rotation_<double>::asMat33() const [member function]   static
    #~ > SimTK::Mat<3, 3, double, 3, 1>
    #~ > SimTK::Rotation_<double>::calcNDotForBodyXYZInBodyFrame(SimTK::Vec<3,
    #~ > double, 1> const & q, SimTK::Vec<3, double, 1> const & qdot) [member
    #~ > function]   static SimTK::Mat<3, 3, double, 3, 1>
    # rot.constructors(arg_types=[None, 'bool']).exclude()
    # rot.constructors(arg_types=[None]).exclude()
    # rot.member_functions('asMat33').exclude()
    # rot.member_functions('calcNDotForBodyXYZInBodyFrame').exclude()
    # rot.member_functions('calcNForBodyXYZInBodyFrame').exclude()
    # rot.member_functions('calcNInvForBodyXYZInBodyFrame').exclude()
    # rot.member_functions('setRotationFromApproximateMat33').exclude()
    # rot.member_functions('setRotationFromMat33TrustMe').exclude()
    # rot.member_functions('toMat33').exclude()
    # rot.member_functions('reexpressSymMat33').exclude()
    # invrot.member_functions('asMat33').exclude()
    # invrot.member_functions('toMat33').exclude()
    # invrot.member_functions('reexpressSymMat33').exclude()
    rot.member_functions('calcUnnormalizedNForQuaternion').exclude() # To avoid instantiating Mat43
    rot.member_functions('calcUnnormalizedNDotForQuaternion').exclude()
    rot.member_functions('calcUnnormalizedNInvForQuaternion').exclude()
    
def expose_transform(module_builder):
    mb = module_builder
    #~ WARNING: SimTK::Transform_<double> [struct]
    #~ > execution error W1040: The declaration is unexposed, but there are
    #~ > other declarations, which refer to it. This could cause "no to_python
    #~ > converter found" run time error. Declarations:    SimTK::Transform
    #~ > const & SimTK::DecorativeGeometry::getTransform() const [member
    #~ > function]   SimTK::DecorativeGeometry &
    #~ > SimTK::DecorativeGeometry::setTransform(SimTK::Transform const & X_BG)
    #~ > [member function]
    xform = mb.class_('Transform_<double>')
    xform.include()
    invxform = mb.class_('InverseTransform_<double>')
    invxform.include()
    #~ WARNING: SimTK::Mat<3,4,double,3,1> [class declaration]
    #~ > execution error W1040: The declaration is unexposed, but there are
    #~ > other declarations, which refer to it. This could cause "no to_python
    #~ > converter found" run time error. Declarations:
    #~ > SimTK::Mat<3,4,double,3,1> SimTK::InverseTransform_<double>::toMat34()
    #~ > const [member function]   SimTK::Mat<3,4,double,3,1> const &
    #~ > SimTK::Transform_<double>::asMat34() const [member function]
    #~ > SimTK::Mat<3,4,double,3,1> SimTK::Transform_<double>::toMat34() const
    #~ > [member function]
    # invxform.member_functions('toMat34').exclude()
    # xform.member_functions('toMat34').exclude()
    # xform.member_functions('asMat34').exclude()
    # invxform.member_functions('toMat44').exclude()
    # xform.member_functions('toMat44').exclude()
    xform.member_function('asMat34').exclude() # avoid instantiating Mat34
    xform.member_function('toMat34').exclude()
    invxform.member_function('toMat34').exclude()


def expose_increment_operators(module_builder):
    "Wrap C++ operator++() and operator()-- methods as python incr() and decr() methods"
    mb = module_builder
    for op in mb.member_operators('operator++', arg_types=[]):
        try: op.parent.has_wrapped_increment
        except AttributeError: op.parent.has_wrapped_increment = False
        if not op.parent.has_wrapped_increment:
            # Create less ambiguous increment/decrement function names, since there might be more than one in the file
            incr_fn_name = "wrap_increment_%s" % op.parent.alias
            op.parent.add_declaration_code('static void %s(%s& val) {++val;}' % (incr_fn_name, op.parent.demangled) )
            op.parent.add_registration_code('def("incr", &%s)' % incr_fn_name)
            op.parent.has_wrapped_increment = True
    for op in mb.member_operators('operator--', arg_types=[]):
        try: op.parent.has_wrapped_decrement
        except AttributeError: op.parent.has_wrapped_decrement = False
        if not op.parent.has_wrapped_decrement:
            decr_fn_name = "wrap_decrement_%s" % op.parent.alias
            op.parent.add_declaration_code('static void %s(%s& val) {--val;}' % (decr_fn_name, op.parent.demangled) )
            op.parent.add_registration_code('def("decr", &%s)' % decr_fn_name)
            op.parent.has_wrapped_decrement = True
        
def exclude_impl_methods(module_builder):
    mb = module_builder
    # mb.classes(lambda c: c.name.endswith('Rep')).exclude()
    # mb.classes(lambda c: c.name.endswith('Impl')).exclude()
    mb.member_functions(lambda f: f.name.endswith('Rep')).exclude()
    mb.member_functions(lambda f: f.name.endswith('Impl')).exclude()

def expose_arrays(module_builder):
    "Polish up all Array_(whatever) classes"
    mb = module_builder
    #~ WARNING: SimTK::DontCopy [struct]
    #~ > execution error W1040: The declaration is unexposed, but there are
    #~ > other declarations, which refer to it. This could cause "no to_python
    #~ > converter found" run time error. Declarations:
    #~ > SimTK::Array_<SimTK::CacheEntryIndex, unsigned
    #~ > int>::Array_(SimTK::CacheEntryIndex * first, SimTK::CacheEntryIndex
    # [...]
    mb.class_('DontCopy').include()
    for array in mb.classes(lambda c: c.name.startswith('Array_')):
        wrap_array(array, mb)
    # Array_int has special problems, especially when compiling
    # in response to compile errors...
    # (many due to Array_<int>, which contains an immutable python type)
    #~ for aint in (mb.class_('Array_<int, unsigned int>'), 
                  #~ mb.class_('ArrayView_<int, unsigned int>'), 
                  #~ mb.class_('ArrayViewConst_<int, unsigned int>')):
        #~ aint.member_functions('getElt', allow_empty=True).exclude()
        #~ aint.member_functions('erase', allow_empty=True).exclude()
        #~ aint.member_functions('eraseFast', allow_empty=True).exclude()
        #~ aint.member_functions('insert', allow_empty=True).exclude()
        #~ aint.member_functions('front', allow_empty=True).exclude()
        #~ aint.member_functions('back', allow_empty=True).exclude()
        #~ aint.member_functions('begin', allow_empty=True).exclude()
        #~ aint.member_functions('end', allow_empty=True).exclude()
        #~ aint.member_functions('cbegin', allow_empty=True).exclude()
        #~ aint.member_functions('cend', allow_empty=True).exclude()    
        #~ aint.member_functions('cdata', allow_empty=True).exclude()    
        #~ aint.member_functions('data', allow_empty=True).exclude()    
        #~ aint.member_functions('raw_push_back', allow_empty=True).exclude()
    # compile errors with Array_<std::string>
    #~ for arr in (mb.class_('Array_<std::string, unsigned int>'),):
        #~ arr.member_functions('rend', allow_empty=True).exclude()
        #~ arr.member_functions('rbegin', allow_empty=True).exclude()
        #~ arr.member_functions('crend', allow_empty=True).exclude()
        #~ arr.member_functions('crbegin', allow_empty=True).exclude()
    #~ # And seemingly every Array of Arrays
    #~ for arr in mb.classes(lambda c: c.name.startswith('Array_<SimTK::Array_<')):
        #~ arr.member_functions('rend', allow_empty=True).exclude()
        #~ arr.member_functions('rbegin', allow_empty=True).exclude()
        #~ arr.member_functions('crend', allow_empty=True).exclude()
        #~ arr.member_functions('crbegin', allow_empty=True).exclude()
    # On second thought, don't expose the ArrayView_<> nor the ArrayViewConst_<> classes
    mb.classes(lambda c: c.name.startswith('ArrayView')).exclude()
    #1>WARNING: SimTK::ArrayViewConst_<SimTK::EventId, unsigned int> [class]
    #1>> execution error W1040: The declaration is unexposed, but there are
    #1>> other declarations, which refer to it. This could cause "no to_python
    #1>> converter found" run time error. Declarations:
    #1>> SimTK::ArrayViewConst_<SimTK::EventId, unsigned int>
    #1>> SimTK::Array_<SimTK::EventId, unsigned int>::getSubArray(unsigned int
    #1>> index, unsigned int length) const [member function]
    mb.member_functions('getSubArray').exclude()
    mb.member_functions('updSubArray').exclude()

def expose_negator(module_builder):
    "Wrap negator class, which is needed by vec3 wrapping"
    mb = module_builder
    negator_double = mb.class_('negator<double>')
    negator_double.alias = 'negator_double' # use a nicer name
    negator_double.include() # wrap it
    mb.class_('negator<float>').exclude()
    mb.class_('negator<long double>').exclude()
    for cls in mb.classes(lambda c: c.name.startswith('negator<')):
        # Notice below that I place a warning issued by pyplusplus, followed by 
        # code to stifle the warning.
        #
        # WARNING: SimTK::negator<double> const * SimTK::negator<double>::getData() const [member function]
        # > compilation error W1050: The function returns "SimTK::negator<double>
        # > const *" type. You have to specify a call policies.Be sure to take a
        # > look on `Py++` defined call policies
        cls.member_function('getData').call_policies = return_internal_reference()
        cls.member_function('updData').call_policies = return_internal_reference()
        #
        # WARNING: double & SimTK::negator<double>::operator-() [member operator]
        # > warning W1008: The function returns non-const reference to "Python
        # > immutable" type. The value cannot be modified from Python.
        cls.member_operators('operator-', arg_types=[]).call_policies = return_internal_reference()
        # compile errors about undefined call policies
        cls.member_functions('real').call_policies = return_internal_reference()
        cls.member_functions('imag').call_policies = return_internal_reference()

def expose_smallvec(vec):
    vec.include() # wrap it
    # Below are error messages, in comments, followed by operations used to correct the problem
    # WARNING: double & SimTK::Vec<3, double, 1>::operator()(int i) [member operator]
    # > warning W1008: The function returns non-const reference to "Python
    # > immutable" type. The value cannot be modified from Python.
    # vec.member_operators('operator()').call_policies = return_internal_reference()
    # confusing compile errors too...
    vec.member_operators('operator()').exclude()
    # WARNING: SimTK::Vec<2,SimTK::negator<double>,1> [class declaration]
    # > execution error W1040: The declaration is unexposed, but there are
    # > other declarations, which refer to it. This could cause "no to_python
    # > converter found" run time error. Declarations:
    # > SimTK::Vec<2,SimTK::negator<double>,1> SimTK::Vec<3,
    # > SimTK::negator<double>, 1>::drop1(int p) const [member function]
    vec.member_function('drop1').exclude() # avoid having to implement Vec(n-1)
    # default parameter=getDefaultTolerance() causes compile trouble, so exclude isNumericallyEqual()
    vec.member_function('isNumericallyEqual').exclude()
    # compile errors about undefined call policies
    vec.member_functions('real').call_policies = return_internal_reference()
    vec.member_functions('imag').call_policies = return_internal_reference()
    vec.member_functions('updCastAwayNegatorIfAny').call_policies = return_internal_reference()
    vec.member_functions('updNegate').call_policies = return_internal_reference()
    vec.member_functions('updPositionalTranspose').call_policies = return_internal_reference()
    vec.member_functions('updTranspose').call_policies = return_internal_reference()
    vec.member_functions('updAs').call_policies = return_internal_reference()
    # Pythonify sequence operations
    # define_py_sequence_methods(vec)
    # Addition and subtraction
    t = vec.demangled
    vec.add_declaration_code("""
        typedef %s vec_type;
        static vec_type vec_subtract(const vec_type& v1, const vec_type& v2) {return v1 - v2;}
        static vec_type vec_add(const vec_type& v1, const vec_type& v2) {return v1 + v2;}
    """ % (t))
    # vec.add_registration_code('def("__sub__", &vec_subtract)')
    # vec.add_registration_code('def("__add__", &vec_add)')
    # equality operator
    vec.add_declaration_code("""
        static bool are_vecs_equal(const vec_type& v1, const vec_type& v2) {return v1 == v2;}
        static bool are_vecs_unequal(const vec_type& v1, const vec_type& v2) {return v1 != v2;}
    """)
    vec.add_registration_code('def("__eq__", &are_vecs_equal)')
    vec.add_registration_code('def("__ne__", &are_vecs_unequal)')
    # use indexing suite to allow slicing etc.
    vec.include_files.append("simtk_indexing_helpers.hpp")
    vec.add_registration_code("""
        def(bp::indexing::container_suite<
                %s, 
                bp::indexing::all_methods, 
                list_algorithms<SimTKVec_container_traits<%s, %s::TElement, int> > >())
        """ % (t, t, t) )
    # absolute value - python arithmetic    
    vec.add_registration_code('def("__abs__", &%s::norm)' % t)
    # Use built-in boost.python mechanism for wrapping __str__
    vec.add_registration_code('def( bp::self_ns::str(bp::self) )')
    # Fudge repr by pretending all is Vec3 or Row3, regardless of stride, negator
    m = re.match(r'.*(Vec|Row)<\s*(\d+)\s*,\s*(\S.*\S)\s*,\s*(\d+)\s*>', vec.decl_string)
    vec_or_row = m.group(1)
    size = m.group(2)
    elt = m.group(3)
    stride = m.group(4)
    alias = vec_or_row + size
    repr_alias = alias
    if stride != "1":
        alias += "_%s" % stride
    if elt.find("negator") >= 0:
        alias = "Neg" + alias
    # vec.alias = alias # covered in instantiate.h
    vec.add_declaration_code("""
        std::string %s_repr_string(const %s& vec) {
            std::ostringstream s;
            s << "%s(";
            for(int i = 0; i < %s; ++i) {
                if (i > 0) s << ", ";
                s << vec[i];
            }
            s << ")";
            return s.str();
        }
    """ % (alias, vec.decl_string, repr_alias, size) )
    vec.add_registration_code('def("__repr__", %s_repr_string)' % alias)
    # Get rid of inner classes like "Result"
    for cls in vec.classes(allow_empty=True):
        cls.exclude()

def expose_vec3(module_builder):
    "Massage module_builder to make wrapping of Vec3 nice"
    mb = module_builder
    # My custom Vec3 <=> tuple conversion code
    mb.add_declaration_code('#include "convert_simtk_vec3.hpp"', tail=False)
    mb.add_registration_code("register_simtk_vec3_conversion();", tail=False)
    # Many templated classes must be defined at the same time, since
    # they make reference to one another.
    # e.g. 'Vec<3, double, 1>', 'Row<3, negator<double>, 1>', etc.
    for orient in ('Vec', 'Row'):
        for sign in ('double', 'SimTK::negator<double>'):
            for stride in (1, 3, 6):
                # e.g. 'Vec<3, double, 1>'
                classname = '%s<3, %s, %s>' % (orient, sign, stride) 
                vec = mb.class_(classname)
                expose_smallvec(vec)
    # exit(1)

def expose_system(module_builder):
    mb = module_builder
    system = mb.class_('System')
    # WARNING: SimTK::System [class]
    # > execution error W1040: The declaration is unexposed, but there are
    # > other declarations, which refer to it. This could cause "no to_python
    # > converter found" run time error. Declarations:
    # > SimTK::VerletIntegrator::VerletIntegrator(SimTK::System const & sys)
    # > [constructor]
    # > SimTK::VerletIntegrator::VerletIntegrator(SimTK::System const & sys,
    # > SimTK::Real stepSize) [constructor]
    system.include()
    # WARNING: SimTK::System::EventTriggerInfoRep const & SimTK::System::EventTriggerInfo::getRep() const [member function]
    # > compilation error W1005: `Py++` cannot expose function that takes as
    # > argument/returns instance of non-public class. Generated code will not
    # > compile.
    eventTriggerInfo = system.class_('EventTriggerInfo')
    eventTriggerInfo.member_function('getRep').exclude()
    eventTriggerInfo.member_function('updRep').exclude()
    # WARNING: SimTK::System::ProjectOptions SimTK::operator&(SimTK::System::ProjectOptions::Option o1, SimTK::System::ProjectOptions::Option o2) [free operator]
    # > warning W1052: `Py++` will not expose free operator
    # > "SimTK::System::ProjectOptions
    # > SimTK::operator&(SimTK::System::ProjectOptions::Option o1,
    # > SimTK::System::ProjectOptions::Option o2) [free operator]" - all
    # > classes, this operator works on, are excluded.
    # 
    # It might be impossible to add special "__and__" methods to enums, oh well...
    # declarated_t voodoo incantation to create value appropriate for "arg_types" parameter
    project_options = system.class_('ProjectOptions')
    project_options.include()
    option = project_options.enum('Option')
    option.include()
    option_t = declarations.declarated_t(option)
    mb.free_operators(r'operator&', arg_types=[option_t, None]).exclude()    
    mb.free_operators(r'operator|', arg_types=[option_t, None]).exclude()    
    mb.free_operators(r'operator~', arg_types=[option_t]).exclude()
    # WARNING: void SimTK::System::handleEvents(SimTK::State & arg0, SimTK::Event::Cause arg1, SimTK::Array_<SimTK::EventId,unsigned int> const & eventIds, SimTK::Real accuracy, SimTK::Vector const & yWeights, SimTK::Vector const & cWeights, SimTK::Stage & lowestModified, bool & shouldTerminate) const [member function]
    # > execution error W1009: The function takes as argument
    # > (name=shouldTerminate, pos=7) non-const reference to Python immutable
    # > type - function could not be called from Python. Take a look on
    # > "Function Transformation" functionality and define the transformation.
    # system.member_function('handleEvents').add_transformation(
    #        FT.output('lowestModified'), FT.output('shouldTerminate'))
    # Unfortunately handleEvents transformation tries to use Stage default constructor, which is private TODO
    system.member_function('handleEvents').exclude()
    system.member_function('calcTimeOfNextScheduledEvent').add_transformation(
            FT.output('tNextEvent'), FT.output('eventIds'))
    system.member_function('calcTimeOfNextScheduledReport').add_transformation(
            FT.output('tNextEvent'), FT.output('eventIds'))
    # WARNING: SimTK::System::Guts [class declaration]
    # > execution error W1040: The declaration is unexposed, but there are
    # > other declarations, which refer to it. This could cause "no to_python
    # > converter found" run time error. Declarations:
    # > SimTK::System::System(SimTK::System::Guts * g) [constructor]   void
    # > SimTK::System::adoptSystemGuts(SimTK::System::Guts * g) [member
    # > function]   SimTK::System::Guts const & SimTK::System::getSystemGuts()
    # > const [member function]   SimTK::System::Guts &
    # > SimTK::System::updSystemGuts() [member function]
    system.member_function('adoptSystemGuts').exclude()
    system.member_function('getSystemGuts').exclude()
    system.member_function('updSystemGuts').exclude()
    for ctor in system.constructors(arg_types=[None]):
        if declarations.is_pointer(ctor.argument_types[0]):
            ctor.exclude()
    # compile error - GCCXML problem with default argument 'All' to System.project()
    project = system.member_function('project')
    arg = project.arguments[5]
    if arg.default_value == r'SimTK::System::ProjectOptions(All)':
        arg.default_value = r'SimTK::System::ProjectOptions(SimTK::System::ProjectOptions::All)'
    if arg.default_value == r'All':
        arg.default_value = r'SimTK::System::ProjectOptions::All'
    #~ System.pypp.obj : error LNK2019: unresolved external symbol "__declspec(dllimport) public: void __thiscall SimTK::System::relax(class SimTK::State &,class SimTK::Stage,double,class SimTK::Vector_<double> const &,class SimTK::Vector_<double> const &)const " (__imp_?relax@System@SimTK@@QBEXAAVState@2@VStage@2@NABV?$Vector_@N@2@2@Z) referenced in function "void __cdecl register_System_class(void)" (?register_System_class@@YAXXZ)
    system.member_function('relax').exclude()
    
def expose_event(module_builder):
    mb = module_builder
    # WARNING: SimTK::EventId const & SimTK::EventId::operator++() [member operator]
    # > compilation error W1014: "operator++" is not supported. See
    # > Boost.Python documentation:
    # > http://www.boost.org/libs/python/doc/v2/operators.html#introduction.
    # event_id.member_operators('operator++').exclude()
    # event_id.member_operators('operator--').exclude()
    # mb.class_('EventId').include()
    # WARNING: SimTK::Event::Cause [class]
    # > execution error W1040: The declaration is unexposed, but there are
    # > other declarations, which refer to it. This could cause "no to_python
    # > converter found" run time error. Declarations:    void
    # > SimTK::System::reportEvents(SimTK::State const & s,
    # > SimTK::Event::Cause cause, SimTK::Array_<SimTK::EventId, unsigned int>
    # > const & eventIds) const [member function]
    event = mb.class_('Event')
    event.include()
    event.class_('Cause').include()
    trigger = event.enumeration('Trigger')
    # WARNING: SimTK::Event::Trigger [enumeration]
    # > execution error W1032: Boost.Python library does not support enums
    # > with duplicate values. You can read more about this here:
    # > http://boost.org/libs/python/todo.html#support-for-enums-with-
    # > duplicate-values . The quick work around is to add new class variable
    # > to the exported enum, from Python.
    # trigger.values.remove(('Rising', 2)) # no effect
    # trigger.values.remove(('Falling', 1)) # no effect
    # trigger.no_export_values.append('Rising')
    # trigger.no_export_values.append('Falling')
    # trigger.export_values.remove('Rising')
    # trigger.export_values.remove('Falling')
    trigger.include() # still does not prevent warning TODO
    #~ WARNING: SimTK::ScheduledEventHandler [class]
    #~ > execution error W1040: The declaration is unexposed, but there are
    #~ > other declarations, which refer to it. This could cause "no to_python
    #~ > converter found" run time error. Declarations:    void SimTK::DefaultS
    #~ > ystemSubsystem::addEventHandler(SimTK::ScheduledEventHandler *
    #~ > handler) [member function]
    #~ WARNING: SimTK::ScheduledEventHandler [class]
    #~ > warning W1023: `Py++` will generate class wrapper - there are few
    #~ > functions that should be redefined in class wrapper. The functions
    #~ > are: handleEvent.
    for cls_name in ['EventReporter', 'EventHandler']:    
        cls = mb.class_(cls_name)
        # cls.include()
        cls.held_type = 'std::auto_ptr< %s >' % cls.decl_string
        cls.no_init = True
        cls.noncopyable = True
    #~ WARNING: void SimTK::EventHandler::handleEvent(SimTK::State & state, SimTK::Real accuracy, SimTK::Vector const & yWeights, SimTK::Vector const & ooConstraintTols, SimTK::Stage & lowestModified, bool & shouldTerminate) const [member function]
    #~ > execution error W1009: The function takes as argument
    #~ > (name=shouldTerminate, pos=5) non-const reference to Python immutable
    #~ > type - function could not be called from Python. Take a look on
    #~ > "Function Transformation" functionality and define the transformation.
    # mb.class_('EventHandler').include()
    # Unfortunately handleEvents transformation tries to use Stage default constructor, which is private TODO
    # mb.class_('EventHandler').member_function('handleEvent').add_transformation(
    #         FT.output('lowestModified'), FT.output('shouldTerminate'))
    # TODO 
    #     
    # Abstract classes with auto_ptrs and base classes require special treatment
    # Because these can be subclassed in python
    for cls_name in ['ScheduledEventReporter', 
                    'PeriodicEventReporter', 
                    'TriggeredEventReporter',
                    'ScheduledEventHandler', 
                    'PeriodicEventHandler', 
                    'TriggeredEventHandler',]:
        cls = mb.class_(cls_name)
        cls.include()
        # notice the "_wrapper" part
        cls.held_type = 'std::auto_ptr< %s >' % cls.wrapper_alias
        cls_ptr_type = 'std::auto_ptr< %s >' % cls.decl_string
        parent_ptr_type = 'std::auto_ptr< %s >' % cls.bases[0].related_class.decl_string
        cls.add_registration_code(
            'bp::implicitly_convertible<%s, %s >();' % (cls_ptr_type, parent_ptr_type),
            works_on_instance=False)
        cls.add_registration_code(
            'bp::implicitly_convertible<%s, %s >();' % (cls.held_type, cls_ptr_type),
            works_on_instance=False)
        #~ WARNING: SimTK::ScheduledEventReporter [class]
        #~ > warning W1023: `Py++` will generate class wrapper - there are few
        #~ > functions that should be redefined in class wrapper. The functions
        #~ > are: handleEvent.
        # cls.member_functions('handleEvent', allow_empty=True).exclude() # doesn't help
    h = mb.class_('EventHandler')
    handleEvent = h.member_function('handleEvent')
    # virtual function, so we want same signature, but with outputs returned as well
    # handleEvent.add_transformation(
    #     FT.output('lowestModified'), FT.input('shouldTerminate'))

def expose_subsystem(module_builder):
    mb = module_builder
    # WARNING: SimTK::SubsystemIndex [class]
    # > execution error W1040: The declaration is unexposed, but there are
    # > other declarations, which refer to it. This could cause "no to_python
    # > converter found" run time error. Declarations:
    # > SimTK::SubsystemIndex SimTK::System::adoptSubsystem(SimTK::Subsystem &
    # > child) [member function]   SimTK::Subsystem const &
    # > SimTK::System::getSubsystem(SimTK::SubsystemIndex arg0) const [member
    # > function]   SimTK::Subsystem &
    # > SimTK::System::updSubsystem(SimTK::SubsystemIndex arg0) [member
    # > function]
    # wrap_custom_index(mb.class_('SubsystemIndex'))
    # WARNING: SimTK::Subsystem [class declaration]
    # > execution error W1040: The declaration is unexposed, but there are
    # > other declarations, which refer to it. This could cause "no to_python
    # > converter found" run time error. Declarations:
    # > SimTK::SubsystemIndex SimTK::System::adoptSubsystem(SimTK::Subsystem &
    # > child) [member function]   SimTK::Subsystem const &
    # > SimTK::System::getSubsystem(SimTK::SubsystemIndex arg0) const [member
    # > function]   SimTK::Subsystem &
    # > SimTK::System::updSubsystem(SimTK::SubsystemIndex arg0) [member
    # > function]
    subsystem = mb.class_('Subsystem')
    subsystem.include()
    # WARNING: SimTK::AbstractMeasure [class]
    # > execution error W1040: The declaration is unexposed, but there are
    # > other declarations, which refer to it. This could cause "no to_python
    # > converter found" run time error. Declarations:    SimTK::MeasureIndex
    # > SimTK::Subsystem::adoptMeasure(SimTK::AbstractMeasure & arg0) [member
    # > function]   SimTK::AbstractMeasure
    # > SimTK::Subsystem::getMeasure(SimTK::MeasureIndex arg0) const [member
    # > function]
    mb.class_('AbstractMeasure').include()
    mb.class_('AbstractMeasure').class_('Implementation').exclude()
    # WARNING: SimTK::AbstractMeasure::AbstractMeasure(SimTK::Subsystem & arg0, SimTK::AbstractMeasure::Implementation * g, SimTK::AbstractMeasure::SetHandle const & arg2) [constructor]
    # > compilation error W1005: `Py++` cannot expose function that takes as
    # > argument/returns instance of non-public class. Generated code will not
    # > compile.
    mb.class_('AbstractMeasure').constructors(arg_types=[None, None, None]).exclude()
    mb.class_('AbstractMeasure').constructors(arg_types=[None]).exclude()
    #~ WARNING: SimTK::DefaultSystemSubsystem [class]
    #~ > execution error W1040: The declaration is unexposed, but there are
    #~ > other declarations, which refer to it. This could cause "no to_python
    #~ > converter found" run time error. Declarations:
    #~ > SimTK::DefaultSystemSubsystem const &
    #~ > SimTK::System::getDefaultSubsystem() const [member function]
    #~ > SimTK::DefaultSystemSubsystem & SimTK::System::updDefaultSubsystem()
    #~ > [member function]
    # mb.class_('DefaultSystemSubsystem').include()
    #~ WARNING: SimTK::Subsystem [class]
    #~ > warning W1028: `Py++` will generate class wrapper - class contains
    #~ > definition of nested class "Guts", which requires wrapper class
    # But guts.include() doesn't eliminate warning!
    guts = subsystem.class_('Guts')
    guts.alias = 'Subsystem_Guts'
    guts.include()    
    #~ WARNING: SimTK::Subsystem::Guts [class]
    #~ > warning W1031: `Py++` will generate class wrapper - user asked to
    #~ > expose non - public member function "advanceToStage"
    guts.member_function('advanceToStage').exclude()
    #~ WARNING: void SimTK::Subsystem::Guts::handleEvents(SimTK::State & arg0, SimTK::Event::Cause arg1, SimTK::Array_<SimTK::EventId, unsigned int> const & eventIds, SimTK::Real accuracy, SimTK::Vector const & yWeights, SimTK::Vector const & ooConstraintTols, SimTK::Stage & lowestModified, bool & shouldTerminate) const [member function]
    #~ > execution error W1009: The function takes as argument
    #~ > (name=shouldTerminate, pos=7) non-const reference to Python immutable
    #~ > type - function could not be called from Python. Take a look on
    #~ > "Function Transformation" functionality and define the transformation.
    # Unfortunately transformation of handleEvents class tries to default construct a Stage, which is forbidden
    guts.member_function('handleEvents').exclude()
    guts.member_function('calcTimeOfNextScheduledReport').add_transformation(
            FT.output('tNextEvent'), FT.output('eventIds'))
    guts.member_function('calcTimeOfNextScheduledEvent').add_transformation(
            FT.output('tNextEvent'), FT.output('eventIds'))
    #~ WARNING: SimTK::Subsystem::Guts * SimTK::Subsystem::Guts::clone() const [member function]
    #~ > compilation error W1050: The function returns "SimTK::Subsystem::Guts
    #~ > *" type. You have to specify a call policies.Be sure to take a look on
    #~ > `Py++` defined call policies
    guts.member_function('clone').call_policies = return_value_policy(manage_new_object)
    #~ WARNING: SimTK::Subsystem::Guts::GutsRep [class declaration]
    #~ > execution error W1040: The declaration is unexposed, but there are
    #~ > other declarations, which refer to it. This could cause "no to_python
    #~ > converter found" run time error. Declarations:
    #~ > SimTK::Subsystem::Guts::Guts(SimTK::Subsystem::Guts::GutsRep * r)
    #~ > [constructor]
    guts.constructors(arg_types=[None]).exclude()
    # compile error
    guts.exclude()
    #~ WARNING: SimTK::Subsystem::Guts [class]
    #~ > execution error W1040: The declaration is unexposed, but there are
    #~ > other declarations, which refer to it. This could cause "no to_python
    #~ > converter found" run time error. Declarations:
    #~ > SimTK::Subsystem::Subsystem(SimTK::Subsystem::Guts * g) [constructor]
    #~ > void SimTK::Subsystem::adoptSubsystemGuts(SimTK::Subsystem::Guts * g)
    #~ > [member function]   SimTK::Subsystem::Guts const &
    #~ > SimTK::Subsystem::getSubsystemGuts() const [member function]
    #~ > SimTK::Subsystem::Guts & SimTK::Subsystem::updSubsystemGuts() [member
    #~ > function]
    subsystem.member_function('adoptSubsystemGuts').exclude()
    subsystem.member_function('getSubsystemGuts').exclude()
    subsystem.member_function('updSubsystemGuts').exclude()
    subsystem.constructors(arg_types=[None]).exclude()

def expose_decorative_geometry(module_builder):
    mb = module_builder
    dg = mb.class_('DecorativeGeometry')
    dg.include()
    #~ WARNING: SimTK::DecorativeGeometryRep [class declaration]
    #~ > execution error W1040: The declaration is unexposed, but there are
    #~ > other declarations, which refer to it. This could cause "no to_python
    #~ > converter found" run time error. Declarations:    SimTK::DecorativeGeo
    #~ > metry::DecorativeGeometry(SimTK::DecorativeGeometryRep * r)
    #~ > [constructor]
    dg.constructors(arg_types=[None]).exclude()
    #~ WARNING: SimTK::AnalyticGeometry [class declaration]
    #~ > execution error W1040: The declaration is unexposed, but there are
    #~ > other declarations, which refer to it. This could cause "no to_python
    #~ > converter found" run time error. Declarations:
    #~ > SimTK::DecorativeGeometry::DecorativeGeometry(SimTK::AnalyticGeometry
    #~ > const & arg0) [constructor]
    # mb.class_('AnalyticGeometry').include()
    #
    #~ WARNING: SimTK::DecorativeGeometryImplementation [class]
    #~ > execution error W1040: The declaration is unexposed, but there are
    #~ > other declarations, which refer to it. This could cause "no to_python
    #~ > converter found" run time error. Declarations:    void SimTK::Decorati
    #~ > veGeometry::implementGeometry(SimTK::DecorativeGeometryImplementation
    #~ > & arg0) const [member function]
    dg.member_function('implementGeometry').exclude()

def expose_state(module_builder):
    mb = module_builder
    # WARNING: SimTK::State [class]
    # > execution error W1040: The declaration is unexposed, but there are
    # > other declarations, which refer to it. This could cause "no to_python
    # > converter found" run time error. Declarations:
    # > SimTK::CacheEntryIndex
    # > SimTK::Subsystem::allocateCacheEntry(SimTK::State const & arg0,
    # > SimTK::Stage dependsOn, SimTK::Stage computedBy, SimTK::AbstractValue
    # > * v) const [member function]   SimTK::CacheEntryIndex
    # > SimTK::Subsystem::allocateCacheEntry(SimTK::State const & state,
    # > SimTK::Stage g, SimTK::AbstractValue * v) const [member function]
    # > SimTK::DiscreteVariableIndex
    # [...] many more...
    state = mb.class_('State')
    state.include()
    #~ WARNING: SimTK::Real & SimTK::State::updTime() [member function]
    #~ > warning W1008: The function returns non-const reference to "Python
    #~ > immutable" type. The value cannot be modified from Python.
    state.member_function('updTime').call_policies = return_internal_reference()
    #~ WARNING: std::pair<SimTK::DiscreteVariableIndex,SimTK::CacheEntryIndex> [class declaration]
    #~ > execution error W1040: The declaration is unexposed, but there are
    #~ > other declarations, which refer to it. This could cause "no to_python
    #~ > converter found" run time error. Declarations:
    #~ > std::pair<SimTK::DiscreteVariableIndex,SimTK::CacheEntryIndex>
    #~ > SimTK::State::allocateUpdatingDiscreteVariable(SimTK::SubsystemIndex
    #~ > arg0, SimTK::Stage invalidates, SimTK::AbstractValue * arg2,
    #~ > SimTK::Stage earliestUpdate) [member function]
    mb.class_(r'pair<SimTK::DiscreteVariableIndex,SimTK::CacheEntryIndex>').include()
    # TODO - compile error
    state.member_function('getTime').call_policies = return_value_policy(copy_const_reference)
    state.member_function('updTime').exclude()
    # Link errors - presumably not implemented in SimTK, or no Windows export macro
    # State.pypp.obj : error LNK2019: unresolved external symbol "__declspec(dllimport) public: class SimTK::EventIndex __thiscall SimTK::State::allocateEventIndex(class SimTK::SubsystemIndex,int)const " (__imp_?allocateEventIndex@State@SimTK@@QBE?AVEventIndex@2@VSubsystemIndex@2@H@Z) referenced in function "void __cdecl register_State_class(void)" (?register_State_class@@YAXXZ)
    state.member_functions('allocateEventIndex').exclude()
    state.member_functions('allocateEventTrigger').exclude()
    state.member_functions('allocateUpdatingDiscreteVariable', allow_empty=True).exclude()
    state.member_functions('getDiscreteVarLastUpdateTime', allow_empty=True).exclude()
    state.member_functions('getDiscreteVarUpdateEntry', allow_empty=True).exclude()
    state.member_functions('mapEventTriggerToStage').exclude()
    state.member_functions('mapEventTriggerToSubsystem').exclude()
    state.member_functions('mapMultiplierToSubsystem').exclude()
    state.member_functions('mapQToSubsystem').exclude()
    state.member_functions('mapQErrToSubsystem').exclude()
    state.member_functions('mapZToSubsystem').exclude()
    state.member_functions('mapUToSubsystem').exclude()
    state.member_functions('mapUErrToSubsystem').exclude()
    state.member_functions('mapUDotErrToSubsystem').exclude()
    state.member_functions('mapSubsystemEventTriggerToStage').exclude()
    state.member_functions('getDiscreteVarPrevValue', allow_empty=True).exclude()
    
def expose_abstract_value(module_builder):
    "Needed by Stage and State"
    mb = module_builder
    #~ WARNING: SimTK::AbstractValue [class]
    #~ > execution error W1040: The declaration is unexposed, but there are
    #~ > other declarations, which refer to it. This could cause "no to_python
    #~ > converter found" run time error. Declarations:
    #~ > SimTK::CacheEntryIndex
    #~ > SimTK::State::allocateCacheEntry(SimTK::SubsystemIndex arg0,
    # [...] many more
    mb.class_('AbstractValue').include()
    #~ WARNING: SimTK::AbstractValue * SimTK::AbstractValue::clone() const [member function]
    #~ > compilation error W1050: The function returns "SimTK::AbstractValue *"
    #~ > type. You have to specify a call policies.Be sure to take a look on
    #~ > `Py++` defined call policies
    mb.class_('AbstractValue').member_function('clone').call_policies = return_value_policy(manage_new_object)

def expose_stage(module_builder):
    mb = module_builder
    # WARNING: SimTK::Stage [class]
    # > execution error W1040: The declaration is unexposed, but there are
    # > other declarations, which refer to it. This could cause "no to_python
    # > converter found" run time error. Declarations:    SimTK::Stage
    # > SimTK::AbstractMeasure::getDependsOnStage(int derivOrder=0) const
    # > [member function]   SimTK::CacheEntryIndex
    # > SimTK::Subsystem::allocateCacheEntry(SimTK::State const & arg0,
    # > SimTK::Stage dependsOn, SimTK::Stage computedBy, SimTK::AbstractValue
    # [...] many more...
    mb.class_('Enumeration<SimTK::Stage>').include()
    mb.class_('Enumeration<SimTK::Stage>').noncopyable = True
    mb.class_('Stage').include()
    mb.class_('Stage').noncopyable = False
    #~ WARNING: SimTK::Enumeration<SimTK::Stage> [class]
    #~ > warning W1031: `Py++` will generate class wrapper - user asked to
    #~ > expose non - public member function "Enumeration"
    mb.class_('Enumeration<SimTK::Stage>').constructors().exclude()
    mb.class_('Enumeration<SimTK::Stage>').member_function('updAllValues').exclude()
    #~ WARNING: SimTK::Stage * SimTK::Enumeration<SimTK::Stage>::operator&() [member operator]
    #~ > compilation error W1050: The function returns "SimTK::Stage *" type.
    #~ > You have to specify a call policies.Be sure to take a look on `Py++`
    #~ > defined call policies
    mb.class_('Enumeration<SimTK::Stage>').member_operators('operator&', arg_types=[]).exclude()
    #~ WARNING: SimTK::Enumeration<SimTK::Stage>::iterator [class declaration]
    #~ > execution error W1040: The declaration is unexposed, but there are
    #~ > other declarations, which refer to it. This could cause "no to_python
    #~ > converter found" run time error. Declarations:    static
    #~ > SimTK::Enumeration<SimTK::Stage>::iterator
    #~ > SimTK::Enumeration<SimTK::Stage>::begin() [member function]   static
    #~ > SimTK::Enumeration<SimTK::Stage>::iterator
    #~ > SimTK::Enumeration<SimTK::Stage>::end() [member function]
    mb.class_(r'Enumeration<SimTK::Stage>').member_function('begin').exclude()
    mb.class_(r'Enumeration<SimTK::Stage>').member_function('end').exclude()
    #~ WARNING: SimTK::Enumeration<SimTK::Stage>::name [variable]
    #~ > compilation error W1036: `Py++` can not expose pointer to Python
    #~ > immutable member variables. This could be changed in future.
    mb.class_(r'Enumeration<SimTK::Stage>').variable('name').exclude()
    # WARNING: SimTK::Exception::RealizeCheckFailed::RealizeCheckFailed(char const * fn, int ln, SimTK::Stage g, int subsystemId, char const * subsystemName, char const * fmt, ...) [constructor]
    # > warning W1053: `Py++` will not expose function
    # > "SimTK::Exception::RealizeCheckFailed::RealizeCheckFailed(char const *
    # > fn, int ln, SimTK::Stage g, int subsystemId, char const *
    # > subsystemName, char const * fmt, ...) [constructor]" - the function
    # > has variable-argument list, spicified by ellipsis (...).
    mb.class_(r'RealizeCheckFailed').constructor(arg_types=[None,None,None,None,None,None,None]).exclude()
    #~ WARNING: SimTK::EnumerationSet<SimTK::Stage> [class declaration]
    #~ > execution error W1040: The declaration is unexposed, but there are
    #~ > other declarations, which refer to it. This could cause "no to_python
    #~ > converter found" run time error. Declarations:    void
    #~ > SimTK::State::createRestrictedState(SimTK::State & restrictedState,
    #~ > SimTK::EnumerationSet<SimTK::Stage> restrictedStages, std::set<SimTK::
    #~ > SubsystemIndex,std::less<SimTK::SubsystemIndex>,std::allocator<SimTK::
    #~ > SubsystemIndex> > restrictedSubsystems) [member function]
    #~ > SimTK::EnumerationSet<SimTK::Stage> const &
    #~ > SimTK::State::getRestrictedStages() const [member function]
    mb.class_('EnumerationSet<SimTK::Stage>').include()
    #~ WARNING: SimTK::EnumerationSet<SimTK::Stage>::EnumerationSetRep::EnumerationSetRep(SimTK::EnumerationSet<SimTK::Stage>::EnumerationSetRep const & set) [copy constructor]
    #~ > compilation error W1005: `Py++` cannot expose function that takes as
    #~ > argument/returns instance of non-public class. Generated code will not
    #~ > compile.
    esetrep = mb.class_('EnumerationSet<SimTK::Stage>').class_('EnumerationSetRep')
    esetrep.exclude()
    
def expose_vector(vec):
    vec.include()
    # Promiscuous exclusion to avoid wrapping other classes for now
    vec.constructors().exclude()
    vec.member_functions('begin', allow_empty=True).exclude() # avoid VectorIterator
    vec.member_functions('end', allow_empty=True).exclude() # avoid VectorIterator
    vec.casting_operators(allow_empty=True).exclude()
    vec.member_operators('operator()', allow_empty=True).exclude()
    vec.member_functions('index', allow_empty=True).exclude()
    vec.member_functions('updIndex', allow_empty=True).exclude()
    vec.member_functions('resize', allow_empty=True).exclude()
    vec.member_functions('resizeKeep', allow_empty=True).exclude()
    # Indexing
    # use indexing suite to allow slicing etc.
    t = vec.demangled
    elem_type_string = re.search('<(.+)>', t).group(1)
    vec.include_files.append("simtk_indexing_helpers.hpp")
    vec.add_registration_code("""
        def(bp::indexing::container_suite<
                %s, 
                bp::indexing::all_methods, 
                list_algorithms<SimTKVec_container_traits<%s, %s, int> > >())
        """ % (t, t, elem_type_string) )
    
    
def expose_vectors(module_builder):
    mb = module_builder
    expose_vector(mb.class_('Vector_<double>'))
    for orient in ('VectorView_', 'RowVectorView_'):
        for sign in ('double', 'SimTK::negator<double> '):
            # e.g. 'Vec<3, double, 1>'
            classname = '%s<%s>' % (orient, sign) 
            vec = mb.class_(classname)
            expose_vector(vec)
    mb.member_functions('elementwiseInvertInPlace').call_policies = return_self()
    mb.member_functions('drop1').exclude() # avoid having to implement Vec(n-1)
    #~ WARNING: SimTK::Vector_<double> [class]^M
    #~ > warning W1023: `Py++` will generate class wrapper - there are few^M
    #~ > functions that should be redefined in class wrapper. The functions^M
    #~ > are: getHelper, updHelper.
    mb.member_functions('getHelper').exclude()
    mb.member_functions('updHelper').exclude()
    # compile errors with Mat66
    mb.member_functions('isNumericallyEqual').exclude()
    mb.member_functions('isNumericallySymmetric').exclude()
    vec_string = mb.class_('vector<SimTK::String>')
    vec_string.include()
    #WARNING: SimTK::String * std::vector<SimTK::String, std::allocator<SimTK::String> >::_Ufill(SimTK::String * _Ptr, size_t _Count, SimTK::String const & _Val) [member function]
    #> compilation error W1050: The function returns "SimTK::String *" type.
    #> You have to specify a call policies.Be sure to take a look on `Py++`
    #> defined call policies
    vec_string.member_functions(lambda fn: fn.name.startswith('_')).exclude()

def expose_mats(module_builder):
    mb = module_builder
    for cls in mb.classes(lambda c: c.name.startswith('Mat')):
        # Zero argument methods
        # cls.member_functions('diag', arg_types=[], allow_empty=True).call_policies = return_internal_reference()
        # One argument methods
        # cls.member_operators('operator()', allow_empty=True).call_policies = return_internal_reference()
        # compile errors...
        cls.member_functions('elt', allow_empty=True).exclude()
        cls.member_functions('getElt', allow_empty=True).exclude()
        cls.member_functions('updElt', allow_empty=True).exclude()
        cls.member_functions('getAnyElt', allow_empty=True).exclude()
        cls.member_operators('operator()', allow_empty=True).exclude()
        cls.member_functions('lockNRows', allow_empty=True).exclude()
        cls.member_functions('lockNCols', allow_empty=True).exclude()
        cls.member_functions('unlockNRows', allow_empty=True).exclude()
        cls.member_functions('unlockNCols', allow_empty=True).exclude()
        # getContiguousScalarData() returns double*, probably not worth the trouble
        cls.member_functions('getContiguousScalarData', allow_empty=True).exclude()
        cls.member_functions('updContiguousScalarData', allow_empty=True).exclude()
    # avoid too many classes
    mb.member_functions('dropRow').exclude()
    mb.member_functions('dropCol').exclude()
    mb.member_functions('dropRowCol').exclude()

def expose_cnts(module_builder):
    mb = module_builder
    # SimTK CompositeNumericalTypes compile error
    # CNT Methods that sometimes are not present, or cause compile error
    for fn_name in ['getInfinity', 'negImag', 'real']: # zero argument versions
        mb.member_functions(fn_name, arg_types=[]).exclude()
    for fn_name in ['getData', 'updData', 'negate', 'updCastAwayNegatorIfAny']:
        for fn in mb.member_functions(fn_name, arg_types=[None]):
            fn.exclude()
    # CNT Methods that return an internal reference
    for fn_name in ['imag', 'positionalTranspose', 'real', 'transpose']:
        for fn in mb.member_functions(fn_name, arg_types=[None]):
            if declarations.is_reference(fn.return_type):
                fn.call_policies = return_internal_reference()
    for fn_name in ['conj', 'imag']:
        for fn in mb.member_functions(fn_name, arg_types=[]):
            if declarations.is_reference(fn.return_type):
                fn.call_policies = return_internal_reference()

doProfile = False
if doProfile:
    import cProfile
    cProfile.run("generate_simtkcommon_source()", "generate_simtkcommon_profile.log")
    exit(0)

if __name__ == "__main__":
    generate_simtkcommon_source()
