vim: smart C/C++ boilerplate templates

A simple vim scriptie for those who are interested. It is triggered when new C/C++ files are created (e.g. via vim new-file.cxx), and fills it in with boilerplate unit code. What’s special about it is that it tries to find tips about that code in other files in that or parent directory.

The templates

The template used for .c/.cxx files is:

/* header-comment
 */

#ifdef HAVE_CONFIG_H
#	include "config.h"
#endif

While for .h/.hxx files the following is used:

/* header-comment
 */

#pragma once

#ifndef PREFIX_FN_H
#define PREFIX_FN_H 1

#endif /*PREFIX_FN_H*/

In order to simplify the work flow, the following magic is done:

  1. header-comment is actually copied from any other file with the same suffix,
  2. FN_H is substituted for actual filename,
  3. PREFIX is copied from any other file with the same suffix,
  4. if no file is found in the same directory as the new file, the scripts falls back to parent directory, and appends the subdirectory name to PREFIX.

The script

Just a dirty, quick vimrc. Feel free to improve:

function CTemplate()
  let fext = expand('%:e')
  let ucext = toupper(fext)
  let myprefix = substitute(toupper(expand('%')), '[^A-Z0-9]', '_', 'g')

  let head_done = 0
  let head_line = 0
  let prefix_done = 0

  if fext =~ '^h'
    call append(1, '#pragma once')
    call append(2, '')
    call append(3, '#ifndef @fn@')
    call append(4, '#define @fn@ 1')
    call append(5, '')
    call append(6, '#endif /*@fn@*/')
  else
    call append(1, '#ifdef HAVE_CONFIG_H')
    call append(2, '#  include "config.h"')
    call append(3, '#endif')
    let prefix_done = 1
  endif

  let mywd = expand("%:p:h")
  echo mywd
  let myfiles = glob(mywd . '/*.' . fext, 0, 1)
  let myfiles += glob(mywd . '/../*.' . fext, 0, 1)

  for myfile in myfiles
    if filereadable(myfile)
      for myline in readfile(myfile)
        if !head_done
          if head_line != 0
            call append(head_line, myline)
            if myline =~ '[*][/]'
              let head_done = 1
            else
              let head_line += 1
            endif
          endif

          if head_line == 0 && myline =~ '^[/][*]'
            call append(0, myline)
            let head_line += 1

            if myline =~ '[*][/]'
              let head_done = 1
            endif
          endif
        endif

        if !prefix_done && myline =~ '^#ifndef .*_'.ucext
          if myfile =~ '^../'
            let mydir = toupper(expand('%:p:h:t'))

            let myprefix = mydir . '_' . myprefix
          endif

          let myprefix = substitute(myline[8:],
            \ '[^_]*_[^_]*$', '', '') . myprefix
          let prefix_done = 1
        endif
      endfor
    endif

    if head_done && prefix_done
      break
    endif
  endfor

  if fext =~ '^h'
    exec '%s/@fn@/'.myprefix.'/'
  endif
endfunction

4 thoughts on “vim: smart C/C++ boilerplate templates”

  1. Quoting [17.6.4.3.2 Global names] from the C++ standard: “Each name that contains a double underscore _ _ or begins with an underscore followed by an uppercase letter (2.12) is reserved to the implementation for any use.” – Referring to your _PREFIX_FN_H identifier.

    1. Looking at the context, I’m not sure if that strictly applies to macros or to symbols only. In any case, using _FOO_BAR_H for header guards is more than common…

      1. A macro name is a global name, which is covered by the section I quoted. It may be common, but still goes against the language specification.

Leave a Reply to Michał Górny Cancel reply

Your email address will not be published.