better-enums/pp_map_gen.py
Anton Bachin f74de74604 Port to C++98 with variadic macros.
enum.h tries to automatically detect whether it is running with C++11 support.
If not, it emits alternative code that is supposed to work on compilers
supporting C++98 and variadic macros. This code is largely interface-compatible
with the C++11 code, with the following semantic differences:

- No compile-time stringization. This is done upon first use of a function other
  than to_integral.
- Implicit conversion to integral types. This is due to the lack of enum class
  support.
- The values _name, _names, _values are replaced with functions _name_, _names_,
  _values_.
2015-05-19 16:22:06 -05:00

141 lines
5.6 KiB
Python
Executable File

#! /usr/bin/env python
# This file is part of Better Enums, released under the BSD 2-clause license.
# See LICENSE for details, or visit http://github.com/aantron/better-enums.
# This script generates the macros _ENUM_PP_MAP and _ENUM_ITERATE, used
# internally by enum.h. These are already inlined into enum.h. You only need
# this script if you are developing enum.h, or run into a limit.
#
# _ENUM_PP_MAP has a limit, which determines the maximum number of constants an
# enum can have. By default, this limit is 64 constants.
#
# _ENUM_ITERATE also has a limit. This one determines the maximum length of the
# name of a constant that is followed by an explicit setting (" = 2"). By
# default, this is 23 (24 with the obligatory null terminator). Constants
# without explicit setting can have any length.
#
# If either of these limits is inadequate, you can still compile your code
# without changing enum.h. You need to generate an external macro file with
# definitions of these macros with relaxed limits, and tell enum.h to use the
# external macro file. Here is how this is done, supposing you want support for
# 512 constants of length up to 127 (128 with null terminator):
#
# 0. MACRO_FILE is the name of the external macro file. Make sure you put it
# somewhere in your include path.
# 1. Run python pp_map_gen.py 512 128 > MACRO_FILE
# 2. Build your code with an additional compiler flag:
# - for gcc and clang, -BETTER_ENUMS_MACRO_FILE='<MACRO_FILE>'
# - for VC++, /BETTER_ENUMS_MACRO_FILE='<MACRO_FILE>'
# or use any other method of getting these macros declared.
# 3. Compile your code. Your macro file should be included, and enum.h should
# happily work with whatever limits you chose.
import os
import sys
class MultiLine(object):
def __init__(self, stream, indent = 4, columns = 80, initial_column = 0):
self._columns_left = columns - initial_column
self._indent = indent
self._columns = columns
self._stream = stream
def write(self, token, last = False):
break_line = False
if last:
if len(token) > self._columns_left:
break_line = True
else:
if len(token) > self._columns_left - 1:
break_line = True
if break_line:
print >> self._stream, ' ' * (self._columns_left - 1) + '\\'
self._stream.write(' ' * self._indent)
self._columns_left = self._columns - self._indent
token = token.lstrip()
self._stream.write(token)
self._columns_left -= len(token)
def generate(stream, constants, length, script):
print >> stream, '// This file was automatically generated by ' + script
print >> stream, ''
print >> stream, '#pragma once'
print >> stream, ''
print >> stream, '#ifndef _BETTER_ENUM_MACRO_FILE_H_'
print >> stream, '#define _BETTER_ENUM_MACRO_FILE_H_'
print >> stream, ''
print >> stream, '#define _ENUM_PP_MAP(macro, data, ...) \\'
print >> stream, ' _ENUM_A(_ENUM_PP_MAP_VAR_COUNT, ' + \
'_ENUM_PP_COUNT(__VA_ARGS__)) \\'
print >> stream, ' (macro, data, __VA_ARGS__)'
print >> stream, ''
print >> stream, '#define _ENUM_PP_MAP_VAR_COUNT(count) ' + \
'_ENUM_M ## count'
print >> stream, ''
print >> stream, '#define _ENUM_A(macro, ...) macro(__VA_ARGS__)'
print >> stream, ''
print >> stream, '#define _ENUM_M1(m, d, x) _ENUM_A(m, d, 0, x)'
for index in range(2, constants + 1):
print >> stream, '#define _ENUM_M' + str(index) + \
'(m,d,x,...) _ENUM_A(m,d,' + str(index - 1) + \
',x) _ENUM_M' + str(index - 1) + \
'(m,d,__VA_ARGS__)'
print >> stream, ''
pp_count_impl_prefix = '#define _ENUM_PP_COUNT_IMPL(_1,'
stream.write(pp_count_impl_prefix)
pp_count_impl = MultiLine(stream = stream, indent = 4,
initial_column = len(pp_count_impl_prefix))
for index in range(2, constants + 1):
pp_count_impl.write(' _' + str(index) + ',')
pp_count_impl.write(' count,')
pp_count_impl.write(' ...)')
pp_count_impl.write(' count', last = True)
print >> stream, ''
print >> stream, ''
pp_count_prefix = \
'#define _ENUM_PP_COUNT(...) _ENUM_PP_COUNT_IMPL(__VA_ARGS__,'
stream.write(pp_count_prefix)
pp_count = MultiLine(stream = stream, indent = 4,
initial_column = len(pp_count_prefix))
for index in range(0, constants - 1):
pp_count.write(' ' + str(constants - index) + ',')
pp_count.write(' 1)', last = True)
print >> stream, ''
print >> stream, ''
iterate_prefix = '#define _ENUM_ITERATE(X, f, l)'
stream.write(iterate_prefix)
iterate = MultiLine(stream = stream, indent = 4,
initial_column = len(iterate_prefix))
for index in range(0, length):
iterate.write(' X(f, l, %i)' % index)
print >> stream, ''
print >> stream, ''
print >> stream, '#endif // #ifndef _BETTER_ENUM_MACRO_FILE_H_'
if __name__ == '__main__':
if len(sys.argv) != 3:
print >> sys.stderr, \
'Usage: ' + sys.argv[0] + ' CONSTANTS LENGTH > FILE'
print >> sys.stderr, ''
print >> sys.stderr, 'Prints map macro definition to FILE.'
print >> sys.stderr, 'CONSTANTS is the number of constants to support.'
print >> sys.stderr, 'LENGTH is the maximum length of a constant name.'
sys.exit(1)
generate(sys.stdout, int(sys.argv[1]), int(sys.argv[2]),
os.path.basename(sys.argv[0]))
sys.exit(0)