Home >Backend Development >C++ >How Can Compile-Time Counters Be Implemented in C Without Using Global Variables or Modifiable State?
Despite the functional nature of template metaprogramming, global variables or modifiable state are not readily available to implement compile-time counters. However, as demonstrated below, namespace-scope functionality can be achieved with minimal use of templates.
Function lookup can be employed to track numeric state through the set of declared functions, as showcased in the following code:
template< size_t n > // Return a number through function lookup. struct cn // The function returns cn<n>. { char data[ n + 1 ]; }; // The caller uses (sizeof fn() - 1). template< typename id, size_t n, size_t acc > cn< acc > seen( id, cn< n >, cn< acc > ); // Default fallback case. #define counter_read( id ) \ ( sizeof seen( id(), cn< 1 >, cn< \ ( sizeof seen( id(), cn< 2 >, cn< \ ( sizeof seen( id(), cn< 4 >, cn< \ ( sizeof seen( id(), cn< 8 >, cn< \ ( sizeof seen( id(), cn< 16 >, cn< \ ( sizeof seen( id(), cn< 32 >, cn< 0 \ /* Add more as desired; trimmed for Stack Overflow code block. */ \ >() ).data - 1 ) \ >() ).data - 1 ) \ >() ).data - 1 ) \ >() ).data - 1 ) \ >() ).data - 1 ) \ >() ).data - 1 ) #define counter_inc( id ) \ cn< counter_read( id ) + 1 > \ seen( id, cn< ( counter_read( id ) + 1 ) & ~ counter_read( id ) >, \ cn< ( counter_read( id ) + 1 ) & counter_read( id ) > )
Quick Demo:
struct my_cnt {}; int const a = counter_read( my_cnt ); counter_inc( my_cnt ); counter_inc( my_cnt ); counter_inc( my_cnt ); counter_inc( my_cnt ); counter_inc( my_cnt ); int const b = counter_read( my_cnt ); counter_inc( my_cnt ); #include <iostream> int main() { std::cout << a << ' ' << b << '\n'; std::cout << counter_read( my_cnt ) << '\n'; }
C 11 Update:
Using C 11's constexpr, an updated version eliminates the use of sizeof:
#define COUNTER_READ_CRUMB( TAG, RANK, ACC ) counter_crumb( TAG(), constant_index< RANK >(), constant_index< ACC >() ) #define COUNTER_READ( TAG ) COUNTER_READ_CRUMB( TAG, 1, COUNTER_READ_CRUMB( TAG, 2, COUNTER_READ_CRUMB( TAG, 4, COUNTER_READ_CRUMB( TAG, 8, \ COUNTER_READ_CRUMB( TAG, 16, COUNTER_READ_CRUMB( TAG, 32, COUNTER_READ_CRUMB( TAG, 64, COUNTER_READ_CRUMB( TAG, 128, 0 ) ) ) ) ) ) ) #define COUNTER_INC( TAG ) \ constexpr \ constant_index< COUNTER_READ( TAG ) + 1 > \ counter_crumb( TAG, constant_index< ( COUNTER_READ( TAG ) + 1 ) & ~ COUNTER_READ( TAG ) >, \ constant_index< ( COUNTER_READ( TAG ) + 1 ) & COUNTER_READ( TAG ) > ) { return {}; } #define COUNTER_LINK_NAMESPACE( NS ) using NS::counter_crumb; template< std::size_t n > struct constant_index : std::integral_constant< std::size_t, n > {}; template< typename id, std::size_t rank, std::size_t acc > constexpr constant_index< acc > counter_crumb( id, constant_index< rank >, constant_index< acc > ) { return {}; } // found by ADL via constant_index
This eliminates the need for a separate namespace for each counter, making it more convenient to use in multiple namespaces.
The above is the detailed content of How Can Compile-Time Counters Be Implemented in C Without Using Global Variables or Modifiable State?. For more information, please follow other related articles on the PHP Chinese website!