Ignore:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • src/AST/Pass.proto.hpp

    r04124c4 rf47f887  
    1 //
    2 // Cforall Version 1.0.0 Copyright (C) 2019 University of Waterloo
    3 //
    4 // The contents of this file are covered under the licence agreement in the
    5 // file "LICENCE" distributed with Cforall.
    6 //
    7 // Pass.impl.hpp --
    8 //
    9 // Author           : Thierry Delisle
    10 // Created On       : Thu May 09 15::37::05 2019
    11 // Last Modified By :
    12 // Last Modified On :
    13 // Update Count     :
    14 //
    15 
    161#pragma once
    172// IWYU pragma: private, include "Pass.hpp"
    183
    194namespace ast {
    20 template<typename pass_type>
    21 class Pass;
    22 
    23 namespace __pass {
    24         typedef std::function<void( void * )> cleanup_func_t;
    25         typedef std::function<void( cleanup_func_t, void * )> at_cleanup_t;
    26 
    27 
    28         // boolean reference that may be null
    29         // either refers to a boolean value or is null and returns true
    30         class bool_ref {
    31         public:
    32                 bool_ref() = default;
    33                 ~bool_ref() = default;
    34 
    35                 operator bool() { return m_ref ? *m_ref : true; }
    36                 bool operator=( bool val ) { assert(m_ref); return *m_ref = val; }
    37 
    38         private:
    39 
    40                 friend class visit_children_guard;
    41 
    42                 bool * set( bool * val ) {
    43                         bool * prev = m_ref;
    44                         m_ref = val;
    45                         return prev;
     5        template<typename pass_type>
     6        class Pass;
     7
     8        namespace __pass {
     9                typedef std::function<void( void * )> cleanup_func_t;
     10                typedef std::function<void( cleanup_func_t, void * )> at_cleanup_t;
     11
     12
     13                // boolean reference that may be null
     14                // either refers to a boolean value or is null and returns true
     15                class bool_ref {
     16                public:
     17                        bool_ref() = default;
     18                        ~bool_ref() = default;
     19
     20                        operator bool() { return m_ref ? *m_ref : true; }
     21                        bool operator=( bool val ) { assert(m_ref); return *m_ref = val; }
     22
     23                private:
     24
     25                        friend class visit_children_guard;
     26
     27                        bool * set( bool * val ) {
     28                                bool * prev = m_ref;
     29                                m_ref = val;
     30                                return prev;
     31                        }
     32
     33                        bool * m_ref = nullptr;
     34                };
     35
     36                // Implementation of the guard value
     37                // Created inside the visit scope
     38                class guard_value {
     39                public:
     40                        guard_value( at_cleanup_t * at_cleanup ) {
     41                                if( at_cleanup ) {
     42                                        *at_cleanup = [this]( cleanup_func_t && func, void* val ) {
     43                                                push( std::move( func ), val );
     44                                        };
     45                                }
     46                        }
     47
     48                        ~guard_value() {
     49                                while( !cleanups.empty() ) {
     50                                        auto& cleanup = cleanups.top();
     51                                        cleanup.func( cleanup.val );
     52                                        cleanups.pop();
     53                                }
     54                        }
     55
     56                        void push( cleanup_func_t && func, void* val ) {
     57                                cleanups.emplace( std::move(func), val );
     58                        }
     59
     60                private:
     61                        struct cleanup_t {
     62                                cleanup_func_t func;
     63                                void * val;
     64
     65                                cleanup_t( cleanup_func_t&& func, void * val ) : func(func), val(val) {}
     66                        };
     67
     68                        std::stack< cleanup_t > cleanups;
     69                };
     70
     71                // Guard structure implementation for whether or not children should be visited
     72                class visit_children_guard {
     73                public:
     74
     75                        visit_children_guard( bool_ref * ref )
     76                                : m_val ( true )
     77                                , m_prev( ref ? ref->set( &m_val ) : nullptr )
     78                                , m_ref ( ref )
     79                        {}
     80
     81                        ~visit_children_guard() {
     82                                if( m_ref ) {
     83                                        m_ref->set( m_prev );
     84                                }
     85                        }
     86
     87                        operator bool() { return m_val; }
     88
     89                private:
     90                        bool       m_val;
     91                        bool     * m_prev;
     92                        bool_ref * m_ref;
     93                };
     94
     95                //-------------------------------------------------------------------------------------------------------------------------------------------------------------------------
     96                // Deep magic (a.k.a template meta programming) to make the templated visitor work
     97                // Basically the goal is to make 2 previsit
     98                // 1 - Use when a pass implements a valid previsit. This uses overloading which means the any overload of
     99                //     'pass.previsit( node )' that compiles will be used for that node for that type
     100                //     This requires that this option only compile for passes that actually define an appropriate visit.
     101                //     SFINAE will make sure the compilation errors in this function don't halt the build.
     102                //     See http://en.cppreference.com/w/cpp/language/sfinae for details on SFINAE
     103                // 2 - Since the first implementation might not be specilizable, the second implementation exists and does nothing.
     104                //     This is needed only to eliminate the need for passes to specify any kind of handlers.
     105                //     The second implementation only works because it has a lower priority. This is due to the bogus last parameter.
     106                //     The second implementation takes a long while the first takes an int. Since the caller always passes an literal 0
     107                //     the first implementation takes priority in regards to overloading.
     108                //-------------------------------------------------------------------------------------------------------------------------------------------------------------------------
     109                // PreVisit : may mutate the pointer passed in if the node is mutated in the previsit call
     110                template<typename pass_t, typename node_t>
     111                static inline auto previsit( pass_t & pass, const node_t * & node, int ) -> decltype( pass.previsit( node ), void() ) {
     112                        node = pass.previsit( node );
     113                        assert(node);
    46114                }
    47115
    48                 bool * m_ref = nullptr;
    49         };
    50 
    51         // Implementation of the guard value
    52         // Created inside the visit scope
    53         class guard_value {
    54         public:
    55                 /// Push onto the cleanup
    56                 guard_value( at_cleanup_t * at_cleanup ) {
    57                         if( at_cleanup ) {
    58                                 *at_cleanup = [this]( cleanup_func_t && func, void* val ) {
    59                                         push( std::move( func ), val );
    60                                 };
    61                         }
     116                template<typename pass_t, typename node_t>
     117                static inline auto previsit( pass_t &, const node_t *, long ) {}
     118
     119                // PostVisit : never mutates the passed pointer but may return a different node
     120                template<typename pass_t, typename node_t>
     121                static inline auto postvisit( pass_t & pass, const node_t * node, int ) -> decltype( pass.postvisit( node ), (const node_t *)nullptr ) {
     122                        return pass.postvisit( node );
    62123                }
    63124
    64                 ~guard_value() {
    65                         while( !cleanups.empty() ) {
    66                                 auto& cleanup = cleanups.top();
    67                                 cleanup.func( cleanup.val );
    68                                 cleanups.pop();
    69                         }
    70                 }
    71 
    72                 void push( cleanup_func_t && func, void* val ) {
    73                         cleanups.emplace( std::move(func), val );
    74                 }
    75 
    76         private:
    77                 struct cleanup_t {
    78                         cleanup_func_t func;
    79                         void * val;
    80 
    81                         cleanup_t( cleanup_func_t&& func, void * val ) : func(func), val(val) {}
    82                 };
    83 
    84                 std::stack< cleanup_t > cleanups;
    85         };
    86 
    87         // Guard structure implementation for whether or not children should be visited
    88         class visit_children_guard {
    89         public:
    90 
    91                 visit_children_guard( bool_ref * ref )
    92                         : m_val ( true )
    93                         , m_prev( ref ? ref->set( &m_val ) : nullptr )
    94                         , m_ref ( ref )
    95                 {}
    96 
    97                 ~visit_children_guard() {
    98                         if( m_ref ) {
    99                                 m_ref->set( m_prev );
    100                         }
    101                 }
    102 
    103                 operator bool() { return m_val; }
    104 
    105         private:
    106                 bool       m_val;
    107                 bool     * m_prev;
    108                 bool_ref * m_ref;
    109         };
    110 
    111         //-------------------------------------------------------------------------------------------------------------------------------------------------------------------------
    112         // Deep magic (a.k.a template meta programming) to make the templated visitor work
    113         // Basically the goal is to make 2 previsit
    114         // 1 - Use when a pass implements a valid previsit. This uses overloading which means the any overload of
    115         //     'pass.previsit( node )' that compiles will be used for that node for that type
    116         //     This requires that this option only compile for passes that actually define an appropriate visit.
    117         //     SFINAE will make sure the compilation errors in this function don't halt the build.
    118         //     See http://en.cppreference.com/w/cpp/language/sfinae for details on SFINAE
    119         // 2 - Since the first implementation might not be specilizable, the second implementation exists and does nothing.
    120         //     This is needed only to eliminate the need for passes to specify any kind of handlers.
    121         //     The second implementation only works because it has a lower priority. This is due to the bogus last parameter.
    122         //     The second implementation takes a long while the first takes an int. Since the caller always passes an literal 0
    123         //     the first implementation takes priority in regards to overloading.
    124         //-------------------------------------------------------------------------------------------------------------------------------------------------------------------------
    125         // PreVisit : may mutate the pointer passed in if the node is mutated in the previsit call
    126         template<typename pass_t, typename node_t>
    127         static inline auto previsit( pass_t & pass, const node_t * & node, int ) -> decltype( pass.previsit( node ), void() ) {
    128                 node = pass.previsit( node );
    129                 assert(node);
    130         }
    131 
    132         template<typename pass_t, typename node_t>
    133         static inline auto previsit( pass_t &, const node_t *, long ) {}
    134 
    135         // PostVisit : never mutates the passed pointer but may return a different node
    136         template<typename pass_t, typename node_t>
    137         static inline auto postvisit( pass_t & pass, const node_t * node, int ) -> decltype( pass.postvisit( node ), (const node_t *)nullptr ) {
    138                 return pass.postvisit( node );
    139         }
    140 
    141         template<typename pass_t, typename node_t>
    142         static inline const node_t * postvisit( pass_t &, const node_t * node, long ) { return node; }
    143 
    144         //-------------------------------------------------------------------------------------------------------------------------------------------------------------------------
    145         // Deep magic (a.k.a template meta programming) continued
    146         // To make the templated visitor be more expressive, we allow 'accessories' : classes/structs the implementation can inherit
    147         // from in order to get extra functionallity for example
    148         // class ErrorChecker : WithShortCircuiting { ... };
    149         // Pass<ErrorChecker> checker;
    150         // this would define a pass that uses the templated visitor with the additionnal feature that it has short circuiting
    151         // Note that in all cases the accessories are not required but guarantee the requirements of the feature is matched
    152         //-------------------------------------------------------------------------------------------------------------------------------------------------------------------------
    153         // For several accessories, the feature is enabled by detecting that a specific field is present
    154         // Use a macro the encapsulate the logic of detecting a particular field
    155         // The type is not strictly enforced but does match the accessory
    156         #define FIELD_PTR( name, default_type ) \
    157         template< typename pass_t > \
    158         static inline auto name( pass_t & pass, int ) -> decltype( &pass.name ) { return &pass.name; } \
    159         \
    160         template< typename pass_t > \
    161         static inline default_type * name( pass_t &, long ) { return nullptr; }
    162 
    163         // List of fields and their expected types
    164         FIELD_PTR( env, const ast::TypeSubstitution )
    165         FIELD_PTR( stmtsToAddBefore, std::list< ast::ptr< ast::Stmt > > )
    166         FIELD_PTR( stmtsToAddAfter , std::list< ast::ptr< ast::Stmt > > )
    167         FIELD_PTR( declsToAddBefore, std::list< ast::ptr< ast::Decl > > )
    168         FIELD_PTR( declsToAddAfter , std::list< ast::ptr< ast::Decl > > )
    169         FIELD_PTR( visit_children, __pass::bool_ref )
    170         FIELD_PTR( at_cleanup, __pass::at_cleanup_t )
    171         FIELD_PTR( visitor, ast::Pass<pass_t> * const )
    172 
    173         // Remove the macro to make sure we don't clash
    174         #undef FIELD_PTR
    175 
    176         // Another feature of the templated visitor is that it calls beginScope()/endScope() for compound statement.
    177         // All passes which have such functions are assumed desire this behaviour
    178         // detect it using the same strategy
    179         namespace scope {
    180                 template<typename pass_t>
    181                 static inline auto enter( pass_t & pass, int ) -> decltype( pass.beginScope(), void() ) {
    182                         pass.beginScope();
    183                 }
    184 
    185                 template<typename pass_t>
    186                 static inline void enter( pass_t &, long ) {}
    187 
    188                 template<typename pass_t>
    189                 static inline auto leave( pass_t & pass, int ) -> decltype( pass.endScope(), void() ) {
    190                         pass.endScope();
    191                 }
    192 
    193                 template<typename pass_t>
    194                 static inline void leave( pass_t &, long ) {}
    195         };
    196 
    197         // Finally certain pass desire an up to date indexer automatically
    198         // detect the presence of a member name indexer and call all the members appropriately
    199         namespace indexer {
    200                 // Some simple scoping rules
    201                 template<typename pass_t>
    202                 static inline auto enter( pass_t & pass, int ) -> decltype( pass.indexer.enterScope(), void() ) {
    203                         pass.indexer.enterScope();
    204                 }
    205 
    206                 template<typename pass_t>
    207                 static inline auto enter( pass_t &, long ) {}
    208 
    209                 template<typename pass_t>
    210                 static inline auto leave( pass_t & pass, int ) -> decltype( pass.indexer.leaveScope(), void() ) {
    211                         pass.indexer.leaveScope();
    212                 }
    213 
    214                 template<typename pass_t>
    215                 static inline auto leave( pass_t &, long ) {}
    216 
    217                 // The indexer has 2 kind of functions mostly, 1 argument and 2 arguments
    218                 // Create macro to condense these common patterns
    219                 #define INDEXER_FUNC1( func, type ) \
    220                 template<typename pass_t> \
    221                 static inline auto func( pass_t & pass, int, type arg ) -> decltype( pass.indexer.func( arg ), void() ) {\
    222                         pass.indexer.func( arg ); \
    223                 } \
     125                template<typename pass_t, typename node_t>
     126                static inline const node_t * postvisit( pass_t &, const node_t * node, long ) { return node; }
     127
     128                //-------------------------------------------------------------------------------------------------------------------------------------------------------------------------
     129                // Deep magic (a.k.a template meta programming) continued
     130                // To make the templated visitor be more expressive, we allow 'accessories' : classes/structs the implementation can inherit
     131                // from in order to get extra functionallity for example
     132                // class ErrorChecker : WithShortCircuiting { ... };
     133                // Pass<ErrorChecker> checker;
     134                // this would define a pass that uses the templated visitor with the additionnal feature that it has short circuiting
     135                // Note that in all cases the accessories are not required but guarantee the requirements of the feature is matched
     136                //-------------------------------------------------------------------------------------------------------------------------------------------------------------------------
     137                // For several accessories, the feature is enabled by detecting that a specific field is present
     138                // Use a macro the encapsulate the logic of detecting a particular field
     139                // The type is not strictly enforced but does match the accessory
     140                #define FIELD_PTR( name, default_type ) \
     141                template< typename pass_t > \
     142                static inline auto name( pass_t & pass, int ) -> decltype( &pass.name ) { return &pass.name; } \
    224143                \
    225                 template<typename pass_t> \
    226                 static inline void func( pass_t &, long, type ) {}
    227 
    228                 #define INDEXER_FUNC2( func, type1, type2 ) \
    229                 template<typename pass_t> \
    230                 static inline auto func( pass_t & pass, int, type1 arg1, type2 arg2 ) -> decltype( pass.indexer.func( arg1, arg2 ), void () ) {\
    231                         pass.indexer.func( arg1, arg2 ); \
    232                 } \
     144                template< typename pass_t > \
     145                static inline default_type * name( pass_t &, long ) { return nullptr; }
     146
     147                // List of fields and their expected types
     148                FIELD_PTR( env, const ast::TypeSubstitution )
     149                FIELD_PTR( stmtsToAddBefore, std::list< ast::ptr< ast::Stmt > > )
     150                FIELD_PTR( stmtsToAddAfter , std::list< ast::ptr< ast::Stmt > > )
     151                FIELD_PTR( declsToAddBefore, std::list< ast::ptr< ast::Decl > > )
     152                FIELD_PTR( declsToAddAfter , std::list< ast::ptr< ast::Decl > > )
     153                FIELD_PTR( visit_children, __pass::bool_ref )
     154                FIELD_PTR( at_cleanup, __pass::at_cleanup_t )
     155                FIELD_PTR( visitor, ast::Pass<pass_t> * const )
     156
     157                // Remove the macro to make sure we don't clash
     158                #undef FIELD_PTR
     159
     160                // Another feature of the templated visitor is that it calls beginScope()/endScope() for compound statement.
     161                // All passes which have such functions are assumed desire this behaviour
     162                // detect it using the same strategy
     163                namespace scope {
     164                        template<typename pass_t>
     165                        static inline auto enter( pass_t & pass, int ) -> decltype( pass.beginScope(), void() ) {
     166                                pass.beginScope();
     167                        }
     168
     169                        template<typename pass_t>
     170                        static inline void enter( pass_t &, long ) {}
     171
     172                        template<typename pass_t>
     173                        static inline auto leave( pass_t & pass, int ) -> decltype( pass.endScope(), void() ) {
     174                                pass.endScope();
     175                        }
     176
     177                        template<typename pass_t>
     178                        static inline void leave( pass_t &, long ) {}
     179                };
     180
     181                // Finally certain pass desire an up to date indexer automatically
     182                // detect the presence of a member name indexer and call all the members appropriately
     183                namespace indexer {
     184                        // Some simple scoping rules
     185                        template<typename pass_t>
     186                        static inline auto enter( pass_t & pass, int ) -> decltype( pass.indexer.enterScope(), void() ) {
     187                                pass.indexer.enterScope();
     188                        }
     189
     190                        template<typename pass_t>
     191                        static inline auto enter( pass_t &, long ) {}
     192
     193                        template<typename pass_t>
     194                        static inline auto leave( pass_t & pass, int ) -> decltype( pass.indexer.leaveScope(), void() ) {
     195                                pass.indexer.leaveScope();
     196                        }
     197
     198                        template<typename pass_t>
     199                        static inline auto leave( pass_t &, long ) {}
     200
     201                        // The indexer has 2 kind of functions mostly, 1 argument and 2 arguments
     202                        // Create macro to condense these common patterns
     203                        #define INDEXER_FUNC1( func, type ) \
     204                        template<typename pass_t> \
     205                        static inline auto func( pass_t & pass, int, type arg ) -> decltype( pass.indexer.func( arg ), void() ) {\
     206                                pass.indexer.func( arg ); \
     207                        } \
    233208                        \
    234                 template<typename pass_t> \
    235                 static inline void func( pass_t &, long, type1, type2 ) {}
    236 
    237                 INDEXER_FUNC1( addId     , DeclWithType *  );
    238                 INDEXER_FUNC1( addType   , NamedTypeDecl * );
    239                 INDEXER_FUNC1( addStruct , StructDecl *    );
    240                 INDEXER_FUNC1( addEnum   , EnumDecl *      );
    241                 INDEXER_FUNC1( addUnion  , UnionDecl *     );
    242                 INDEXER_FUNC1( addTrait  , TraitDecl *     );
    243                 INDEXER_FUNC2( addWith   , std::list< Expression * > &, Node * );
    244 
    245                 // A few extra functions have more complicated behaviour, they are hand written
    246                 // template<typename pass_t>
    247                 // static inline auto addStructFwd( pass_t & pass, int, ast::StructDecl * decl ) -> decltype( pass.indexer.addStruct( decl ), void() ) {
    248                 //      ast::StructDecl * fwd = new ast::StructDecl( decl->location, decl->name );
    249                 //      fwd->parameters = decl->parameters;
    250                 //      pass.indexer.addStruct( fwd );
    251                 // }
    252 
    253                 // template<typename pass_t>
    254                 // static inline void addStructFwd( pass_t &, long, ast::StructDecl * ) {}
    255 
    256                 // template<typename pass_t>
    257                 // static inline auto addUnionFwd( pass_t & pass, int, ast::UnionDecl * decl ) -> decltype( pass.indexer.addUnion( decl ), void() ) {
    258                 //      UnionDecl * fwd = new UnionDecl( decl->name );
    259                 //      fwd->parameters = decl->parameters;
    260                 //      pass.indexer.addUnion( fwd );
    261                 // }
    262 
    263                 // template<typename pass_t>
    264                 // static inline void addUnionFwd( pass_t &, long, ast::UnionDecl * ) {}
    265 
    266                 // template<typename pass_t>
    267                 // static inline auto addStruct( pass_t & pass, int, const std::string & str ) -> decltype( pass.indexer.addStruct( str ), void() ) {
    268                 //      if ( ! pass.indexer.lookupStruct( str ) ) {
    269                 //              pass.indexer.addStruct( str );
    270                 //      }
    271                 // }
    272 
    273                 // template<typename pass_t>
    274                 // static inline void addStruct( pass_t &, long, const std::string & ) {}
    275 
    276                 // template<typename pass_t>
    277                 // static inline auto addUnion( pass_t & pass, int, const std::string & str ) -> decltype( pass.indexer.addUnion( str ), void() ) {
    278                 //      if ( ! pass.indexer.lookupUnion( str ) ) {
    279                 //              pass.indexer.addUnion( str );
    280                 //      }
    281                 // }
    282 
    283                 // template<typename pass_t>
    284                 // static inline void addUnion( pass_t &, long, const std::string & ) {}
    285 
    286                 #undef INDEXER_FUNC1
    287                 #undef INDEXER_FUNC2
     209                        template<typename pass_t> \
     210                        static inline void func( pass_t &, long, type ) {}
     211
     212                        #define INDEXER_FUNC2( func, type1, type2 ) \
     213                        template<typename pass_t> \
     214                        static inline auto func( pass_t & pass, int, type1 arg1, type2 arg2 ) -> decltype( pass.indexer.func( arg1, arg2 ), void () ) {\
     215                                pass.indexer.func( arg1, arg2 ); \
     216                        } \
     217                         \
     218                        template<typename pass_t> \
     219                        static inline void func( pass_t &, long, type1, type2 ) {}
     220
     221                        INDEXER_FUNC1( addId     , DeclarationWithType *       );
     222                        INDEXER_FUNC1( addType   , NamedTypeDecl *             );
     223                        INDEXER_FUNC1( addStruct , StructDecl *                );
     224                        INDEXER_FUNC1( addEnum   , EnumDecl *                  );
     225                        INDEXER_FUNC1( addUnion  , UnionDecl *                 );
     226                        INDEXER_FUNC1( addTrait  , TraitDecl *                 );
     227                        INDEXER_FUNC2( addWith   , std::list< Expression * > &, BaseSyntaxNode * );
     228
     229                        // A few extra functions have more complicated behaviour, they are hand written
     230                        template<typename pass_t>
     231                        static inline auto addStructFwd( pass_t & pass, int, ast::StructDecl * decl ) -> decltype( pass.indexer.addStruct( decl ), void() ) {
     232                                ast::StructDecl * fwd = new ast::StructDecl( decl->location, decl->name );
     233                                fwd->parameters = decl->parameters;
     234                                pass.indexer.addStruct( fwd );
     235                        }
     236
     237                        template<typename pass_t>
     238                        static inline void addStructFwd( pass_t &, long, ast::StructDecl * ) {}
     239
     240                        template<typename pass_t>
     241                        static inline auto addUnionFwd( pass_t & pass, int, ast::UnionDecl * decl ) -> decltype( pass.indexer.addUnion( decl ), void() ) {
     242                                UnionDecl * fwd = new UnionDecl( decl->name );
     243                                fwd->parameters = decl->parameters;
     244                                pass.indexer.addUnion( fwd );
     245                        }
     246
     247                        template<typename pass_t>
     248                        static inline void addUnionFwd( pass_t &, long, ast::UnionDecl * ) {}
     249
     250                        template<typename pass_t>
     251                        static inline auto addStruct( pass_t & pass, int, const std::string & str ) -> decltype( pass.indexer.addStruct( str ), void() ) {
     252                                if ( ! pass.indexer.lookupStruct( str ) ) {
     253                                        pass.indexer.addStruct( str );
     254                                }
     255                        }
     256
     257                        template<typename pass_t>
     258                        static inline void addStruct( pass_t &, long, const std::string & ) {}
     259
     260                        template<typename pass_t>
     261                        static inline auto addUnion( pass_t & pass, int, const std::string & str ) -> decltype( pass.indexer.addUnion( str ), void() ) {
     262                                if ( ! pass.indexer.lookupUnion( str ) ) {
     263                                        pass.indexer.addUnion( str );
     264                                }
     265                        }
     266
     267                        template<typename pass_t>
     268                        static inline void addUnion( pass_t &, long, const std::string & ) {}
     269
     270                        #undef INDEXER_FUNC1
     271                        #undef INDEXER_FUNC2
     272                };
    288273        };
    289274};
    290 };
Note: See TracChangeset for help on using the changeset viewer.