diff --git a/script/make_macros_py3.py b/script/make_macros_py3.py new file mode 100644 index 00000000..8e61a263 --- /dev/null +++ b/script/make_macros_py3.py @@ -0,0 +1,158 @@ +#! /usr/bin/env python + +# This file is based on script/make_macros.py and updated for 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 python 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 fprint(stream, *args): + fline = " ".join(map(str, args)) + stream.write(fline + "\n") + +def generate(stream, constants, length, script): + fprint(stream, '// This file was automatically generated by ' + script) + + fprint(stream, '') + fprint(stream, '#pragma once') + fprint(stream, '') + fprint(stream, '#ifndef BETTER_ENUMS_MACRO_FILE_H') + fprint(stream, '#define BETTER_ENUMS_MACRO_FILE_H') + + fprint(stream, '') + fprint(stream, '#define BETTER_ENUMS_PP_MAP(macro, data, ...) \\') + fprint(stream, ' BETTER_ENUMS_ID( \\') + fprint(stream, ' BETTER_ENUMS_APPLY( \\') + fprint(stream, ' BETTER_ENUMS_PP_MAP_VAR_COUNT, \\') + fprint(stream, ' BETTER_ENUMS_PP_COUNT(__VA_ARGS__)) \\') + fprint(stream, ' (macro, data, __VA_ARGS__))') + + fprint(stream, '') + fprint(stream, '#define BETTER_ENUMS_PP_MAP_VAR_COUNT(count) ' + \ + 'BETTER_ENUMS_M ## count') + + fprint(stream, '') + fprint(stream, '#define BETTER_ENUMS_APPLY(macro, ...) ' + \ + 'BETTER_ENUMS_ID(macro(__VA_ARGS__))') + + fprint(stream, '') + fprint(stream, '#define BETTER_ENUMS_ID(x) x') + + fprint(stream, '') + fprint(stream, '#define BETTER_ENUMS_M1(m, d, x) m(d,0,x)') + for index in range(2, constants + 1): + fprint(stream, '#define BETTER_ENUMS_M' + str(index) + \ + '(m,d,x,...) m(d,' + str(index - 1) + ',x) \\') + fprint(stream, ' BETTER_ENUMS_ID(BETTER_ENUMS_M' + \ + str(index - 1) + '(m,d,__VA_ARGS__))') + + fprint(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) + fprint(stream, '') + + fprint(stream, '') + fprint(stream, '#define BETTER_ENUMS_PP_COUNT(...) \\') + 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) + fprint(stream, '') + + fprint(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) + fprint(stream, '') + + fprint(stream, '') + fprint(stream, '#endif // #ifndef BETTER_ENUMS_MACRO_FILE_H') + +def print_err(*args): + err_msg= " ".join(str(arg) for arg in args) + sys.stderr.write(err_msg + "\n") + +if __name__ == '__main__': + if len(sys.argv) != 3: + print_err('Usage: ' + sys.argv[0] + ' CONSTANTS LENGTH > FILE') + print_err("") + print_err("Prints map macro definition to FILE.") + print_err("CONSTANTS is the number of constants to support.") + print_err("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)