#!/usr/bin/awk -f # This software is under the GPL # inlsplit 0.1: this script performs the reverse of inljoin. # Usage: inlsplit [options] [file2.inl ...] # options: # -v noline=1 prevents output of preprocessor directives that # tell the C++ compiler to treat the .cc and .h files as the .inl # file # # This is a script to split a C++ class's implementation and interface. # It takes as input an .inl file, and yields a .cc and .h file (adding an # #ifdef include guard to the header). # What is an .inl file? I'm not sure because I stole the extension, but # for the purposes of this program, it is basically a .cc and .h joined # into 1 file. Functions whose definition belong in the .cc file are # denoted by suffixing a "@" to the prototype. The declaration will then # be put in the .h file, and the definition in the .cc file. An optional # @source directive specifies that all that follows should go in the .cc # file only. # # Example Class.inl file: # class Class { # int func1(char c)@ # { return b; } # int func2stayinline(int b) # { return b; } # }; # @source # #include "class.h" # # func1's definition will be placed in the .cc file, while # func2stayinline's definition stays in the .h file. BEGIN { progname = "inlsplit" extension = "\.inl$" } # we're done processing the file, write out the .cc and .h function endfile() { if (basename != "") { outfile = basename ".h" iguard = "_inl_" toupper(basename) "_H" print "#ifndef " iguard > outfile print "#define " iguard > outfile print data[outfile] > outfile print "#endif // header" > outfile delete data[outfile] outfile = basename ".cc" print data[outfile] > outfile delete data[outfile] } } function output(str) { data[outfile] = data[outfile] str "\n" } function output_pre(str) { data[outfile] = str "\n" data[outfile] } # every time we switch files, print out info to let the C++ compiler know # what line we're at in file.inl function changefile(file) { if (outfile != file) { outfile = file if (!noline) output(sprintf("# %d \"%s\"\n", FNR, FILENAME)) } } # we started a new file function newfile() { endfile() basename = FILENAME sub(extension, "", basename) changefile(basename ".h") printf("%s: %s -> %s.cc and %s.h\n", progname, FILENAME, basename, basename) } # if FNR==1 then we started a new file FNR==1 { newfile() } # get the class name /(class|struct)[ \t]+[A-Za-z0-9_]+/ { for (i=1; i <= NF; i++) { if ($i == "class" || $i == "struct") { classname = $(i+1) break } } } # ok, we want to split a function like so # file.inl: int func(params)@ # becomes... # file.h: int func(params); # file.cc: int class::func(params) # /@$/ && (outfile == (basename ".h")) { sub(/@$/, "") output($0 ";") changefile(basename ".cc") brace = 0 sub(/[A-Za-z0-9_]+[ \t]*\(/, classname "::&") # prepend class:: sub(/^[ \t]*/, "") # eat leading whitespace output($0) next } # everything after source goes into the .cc file # sadly this is somewhat of a hack. getline wants to eat all of my input, # when I only want it to read the current file to EOL. /^@source$/ && outfile == (basename ".h") { changefile(basename ".cc") tmp = "" thisfile = FILENAME while (getline && thisfile == FILENAME) { tmp = tmp $0 "\n" } output_pre(tmp) if (thisfile != FILENAME) newfile() } # keep track of braces so we know when the current function ends /{/ || /}/ { tmp = $0 while (tmp ~ /{/) { brace++ sub(/{/, "", tmp) } while (tmp ~ /}/) { brace-- sub(/}/, "", tmp) } if (brace==0) { output($0) changefile(basename ".h") next } } # everything else, just print // { output($0) } # finish off our last file END { endfile() }