Danny Robson
4050754ab4
We need over 300 arguments for some projects and I'm not going to code that shit by hand.
203 lines
6.4 KiB
Python
Executable File
203 lines
6.4 KiB
Python
Executable File
#!/usr/bin/python3
|
|
|
|
import itertools
|
|
|
|
|
|
###############################################################################
|
|
def chunks(values, size):
|
|
it = iter(values)
|
|
item = list(itertools.islice(it, size))
|
|
|
|
while item:
|
|
yield item
|
|
item = list(itertools.islice(it, size))
|
|
|
|
|
|
###############################################################################
|
|
# Construct a variadic macro of the form:
|
|
#
|
|
# ARITY_DISPATCH(_0001, _0002, NAME, ...) NAME
|
|
#
|
|
# Where the numbered arguments is equal to the predefined maximum arity.
|
|
def generate_dispatch(num):
|
|
values = itertools.chain(
|
|
("_%04u" % n for n in range (1, num+1)),
|
|
["NAME"]
|
|
)
|
|
|
|
yield "#define ARITY_DISPATCH(\\"
|
|
for group in chunks(values, 8):
|
|
yield ", ".join(group) + ", \\"
|
|
yield "...) NAME"
|
|
|
|
|
|
###############################################################################
|
|
# Construct a variadic macro of the form:
|
|
#
|
|
# define REDUCE_1_0002(F,X,Y) F(X,Y)
|
|
# define REDUCE_1_0003(F,X,Y,...) REDUCE_2_0002(F,F(X,Y),__VA_ARGS__)
|
|
# define REDUCE_1_0004(F,X,Y,...) REDUCE_2_0003(F,F(X,Y),__VA_ARGS__)
|
|
#
|
|
# and so on
|
|
#
|
|
# We do not provide a definition for REDUCE_1_0000 because it does not
|
|
# actually reduce anything. Instead we define it as a static_assert to
|
|
# simplify the general dispatch generation code.
|
|
#
|
|
# The macro is defined as if it were arity 1, because the dispatch generation
|
|
# code operates in terms of the tail call and offsets the dispatch arguments
|
|
# by the arity.
|
|
def generate_reduce(num):
|
|
yield '#define REDUCE_1_0000(...) STATIC_ASSERT("REDUCE requires at least two arguments")'
|
|
yield "#define REDUCE_1_0001(F,X,Y) F(X,Y)"
|
|
|
|
for n in range(2, num):
|
|
yield "#define REDUCE_1_%04u(F,X,Y,...) REDUCE_1_%04u(F,F(X,Y),__VA_ARGS__)" % (n, n - 1)
|
|
|
|
raise StopIteration
|
|
|
|
|
|
###############################################################################
|
|
# Generate a variadic macro of the form:
|
|
#
|
|
# define MAP_ARITY_0000(F,A,B) F(A,B)
|
|
# define MAP_ARITY_0001(F,A,B,...) F(X)(MAP_0000_0000(F,A,B,__VA_ARGS___)
|
|
# define MAP_ARITY_0002(F,A,B,...) F(X)(MAP_0000_0001(F,A,B,__VA_ARGS___)
|
|
#
|
|
# and so on...
|
|
#
|
|
# Note: the arguments A, B, etc are replaced with a quantity of arguments
|
|
# indicated by the `arity' parameter.
|
|
def generate_map(num, arity):
|
|
args = ",".join("ARG%02u" % n for n in range(arity))
|
|
if args:
|
|
args += ','
|
|
yield "#define MAP_%(arity)u_0001(F,%(args)sX) F(%(args)sX)" % { 'arity': arity, 'args': args }
|
|
|
|
for n in range(2, num):
|
|
yield "#define %(curr)s(F,%(args)sX,...) F(%(args)sX)%(next)s(F,%(args)s__VA_ARGS__)" % {
|
|
'curr': 'MAP_%(arity)u_%(index)04u' % { 'arity': arity, 'index': n },
|
|
'next': 'MAP_%(arity)u_%(index)04u' % { 'arity': arity, 'index': n - 1 },
|
|
'arity': arity,
|
|
'args': args,
|
|
}
|
|
|
|
raise StopIteration
|
|
|
|
|
|
##-----------------------------------------------------------------------------
|
|
def dispatch(name, num, arity):
|
|
yield "#define %(name)s%(arity)u(FUNC,...)\\" % { 'name': name, 'arity': arity }
|
|
yield "ARITY_DISPATCH(__VA_ARGS__,\\"
|
|
|
|
for group in chunks(range(num-arity,0,-1), 4):
|
|
yield ",".join (
|
|
"%(name)s_%(arity)u_%(n)04u" % {
|
|
'name': name,
|
|
'arity': arity,
|
|
'n': n
|
|
} for n in group
|
|
) + ",\\"
|
|
|
|
yield ', STATIC_ASSERT("invalid arity for %s")\\' % name
|
|
yield ')(FUNC,__VA_ARGS__)'
|
|
|
|
|
|
###############################################################################
|
|
def generate_argcount(num):
|
|
yield "#define VA_ARGS_COUNT(...)\\"
|
|
yield "ARITY_DISPATCH(__VA_ARGS__,\\"
|
|
|
|
literals = ",".join(str(n) for n in range(num,0,-1))
|
|
for group in chunks(range(num, 0, -1), 4):
|
|
yield ", ".join('% 4u' % g for g in group) + ',\\'
|
|
|
|
yield ', STATIC_ASSERT("invalid value for VA_ARGS_COUNT"))'
|
|
|
|
|
|
###############################################################################
|
|
import argparse
|
|
|
|
|
|
##-----------------------------------------------------------------------------
|
|
if __name__ == '__main__':
|
|
#logging.getLogger().setLevel(logging.INFO)
|
|
|
|
parser = argparse.ArgumentParser(description='Generate variadic macro functions')
|
|
parser.add_argument('dst', type=str, help='the output path for the header')
|
|
parser.add_argument('num', type=int, help='the maximum argument count')
|
|
|
|
args = parser.parse_args()
|
|
|
|
dst = open(args.dst, 'w')
|
|
num = args.num
|
|
arity = 3
|
|
|
|
dst.write("""\
|
|
#ifndef CRUFT_UTIL_PREPROCESSOR_HPP
|
|
#define CRUFT_UTIL_PREPROCESSOR_HPP
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// This is autogenerated code, do not modify. Instead, look at preprocessor.py
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Here be dragons. For the love of God, only use these macros for token
|
|
// pasting applications. Don't judge me...
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// Token concatenation wrapper that prevents macro expansion. You should not
|
|
// be using this outside of this header file (and probably not even then for
|
|
// the most part).
|
|
#define PASTE_DETAIL(x, y) x##y
|
|
|
|
// A token concatenation wrapper. You should prefer PASTE over PASTE2. This
|
|
// is defined purely so that PASTE (defined laterR) has something to give
|
|
// REDUCE1.
|
|
#define PASTE2(x,y) PASTE_DETAIL(x,y)
|
|
|
|
// Pair token concatenation with a trailing comma.
|
|
#define PASTE_LIST(x,y) PASTE2(x,y),
|
|
|
|
// Pair token concatenation, transforming into a namespace and type.
|
|
#define NAMESPACE_LIST(NS,KLASS) NS::KLASS,
|
|
|
|
|
|
#define STRINGIZE_DETAIL(x) #x
|
|
#define STRINGIZE(x) STRINGIZE_DETAIL(x)
|
|
#define STRINGIZE_LIST(x) STRINGIZE(x),
|
|
|
|
|
|
#define STATIC_ASSERT(MSG) static_assert(false, MSG);
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
""")
|
|
|
|
dst.write("#define MAX_VA_DISPATCH_ARGS %u\n" % num)
|
|
dst.write("#define MAX_VA_DISPATCH_ARITY %u\n" % arity)
|
|
|
|
lines = itertools.chain (
|
|
generate_dispatch(num),
|
|
generate_map(num, 0),
|
|
generate_map(num, 1),
|
|
generate_map(num, 2),
|
|
dispatch("MAP", num, 0),
|
|
dispatch("MAP", num, 1),
|
|
dispatch("MAP", num, 2),
|
|
generate_reduce(num),
|
|
dispatch("REDUCE", num, 1),
|
|
[ "#define PASTE(...) REDUCE1(PASTE2,__VA_ARGS__)" ],
|
|
generate_argcount(num)
|
|
)
|
|
|
|
for l in lines:
|
|
dst.write(l)
|
|
dst.write('\n')
|
|
|
|
dst.writelines("#endif\n")
|