#!/usr/bin/env python3 # 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. # You only need this script if you are developing enum.h, or run into a limit. # # This script generates the macros BETTER_ENUMS_PP_MAP and BETTER_ENUMS_ITERATE, # used internally by enum.h. These are already inlined into enum.h. # # BETTER_ENUMS_PP_MAP has a limit, which determines the maximum number of # constants an enum can have. By default, this limit is 64 constants. # # BETTER_ENUMS_ITERATE also has a limit. This one determines the maximum length # of the name of a constant that is followed by an initializer (" = 2") when # compiling an enum with constexpr _to_string function (i.e. usually, this limit # does not apply). By default, the limit is 23 characters (24 with the # obligatory null terminator). # # 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 python3 make_macros.py 512 128 > MACRO_FILE # 2. Build your code with an additional compiler flag: # - for gcc and clang, -DBETTER_ENUMS_MACRO_FILE='' # - for VC++, /DBETTER_ENUMS_MACRO_FILE='' # or use any other method of getting these macros defined. # 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._columns_left - 1) + '\\', file=self._stream) 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('// This file was automatically generated by ' + script, file=stream) print('', file=stream) print('#pragma once', file=stream) print('', file=stream) print('#ifndef BETTER_ENUMS_MACRO_FILE_H', file=stream) print('#define BETTER_ENUMS_MACRO_FILE_H', file=stream) print('', file=stream) print('#define BETTER_ENUMS_PP_MAP(macro, data, ...) \\', file=stream) print(' BETTER_ENUMS_ID( \\', file=stream) print(' BETTER_ENUMS_APPLY( \\', file=stream) print(' BETTER_ENUMS_PP_MAP_VAR_COUNT, \\', file=stream) print(' BETTER_ENUMS_PP_COUNT(__VA_ARGS__)) \\', file=stream) print(' (macro, data, __VA_ARGS__))', file=stream) print('', file=stream) print('#define BETTER_ENUMS_PP_MAP_VAR_COUNT(count) ' + 'BETTER_ENUMS_M ## count', file=stream) print('', file=stream) print('#define BETTER_ENUMS_APPLY(macro, ...) ' + 'BETTER_ENUMS_ID(macro(__VA_ARGS__))', file=stream) print('', file=stream) print('#define BETTER_ENUMS_ID(x) x', file=stream) print('', file=stream) print('#define BETTER_ENUMS_M1(m, d, x) m(d,0,x)', file=stream) for index in range(2, constants + 1): print('#define BETTER_ENUMS_M' + str(index) + '(m,d,x,...) m(d,' + str(index - 1) + ',x) \\', file=stream) print(' BETTER_ENUMS_ID(BETTER_ENUMS_M' + str(index - 1) + '(m,d,__VA_ARGS__))', file=stream) print('', file=stream) pp_count_impl_prefix = '#define BETTER_ENUMS_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('', file=stream) print('', file=stream) print('#define BETTER_ENUMS_PP_COUNT(...) \\', file=stream) pp_count_prefix = \ ' BETTER_ENUMS_ID(BETTER_ENUMS_PP_COUNT_IMPL(__VA_ARGS__,' stream.write(pp_count_prefix) pp_count = MultiLine(stream = stream, indent = 8, 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('', file=stream) print('', file=stream) iterate_prefix = '#define BETTER_ENUMS_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('', file=stream) print('', file=stream) print('#endif // #ifndef BETTER_ENUMS_MACRO_FILE_H', file=stream) if __name__ == '__main__': if len(sys.argv) != 3: print('Usage: ' + sys.argv[0] + ' CONSTANTS LENGTH > FILE', file=sys.stderr) print('', file=sys.stderr) print('Prints map macro definition to FILE.', file=sys.stderr) print('CONSTANTS is the number of constants to support.', file=sys.stderr) print('LENGTH is the maximum length of a constant name.', file=sys.stderr) sys.exit(1) generate(sys.stdout, int(sys.argv[1]), int(sys.argv[2]), os.path.basename(sys.argv[0])) sys.exit(0)