Clang format for #pragma

Formatting OpenMP and Intel SIMD pragma extensions is always a problem on clang-format and some other formatting tools. Many minor issues on formatting can be solved easily, like adding comments at the end of a line to force formatter break the line of argument lists, adding empty lines to group the variables that are aligned to each other, etc. However, clang-format keeps the pragma extension formatting wrong, i.e. #pragma omp. This article introduces the perfect solution for any formatting tool without patching the tools.

Existing Discussions

One solution is using the section on/off function(comments) to disable the formatting on #pragma omp, but it is ugly. The other solution is to patch the codes of clang-format, but it doesn’t enter the mainline.
https://stackoverflow.com/questions/24476165/indenting-preprocessor-directives-with-clang-format
https://stackoverflow.com/questions/31353748/how-could-i-indent-c-pragma-using-clang-format
https://segmentfault.com/q/1010000002980184
https://github.com/Chiel92/vim-autoformat/issues/64

 

The Perfect Solution

I found a pretty solution for any formatting tool to customize formatting rules like this. The solution is simply replace the word with another and format it, then replace it back. The bash command is:

# Replace "#pragma omp" by "//#pragma omp"
sed -i 's/#pragma omp/\/\/#pragma omp/g' ./main.c
# Do format
clang-format ./main.c
# Replace "// *#pragma omp" by "#pragma omp"
sed -i 's/\/\/ *#pragma omp/#pragma omp/g' ./main.c

 

Vim Scripts for Autoformat

To be more convenient, I wrote the following vim script to allow me use “=” to format a region of codes. The formatter I used in vim is Chiel92/Autoformat. I like this tool which is very powerful and yet flexible.

command! -nargs=? -range=% -complete=filetype -bar MAutoformat
            \ let mm_winview=winsaveview() |
            \ <line1>,<line2>s/#pragma omp/\/\/#pragma omp/e |
            \ <line1>,<line2>s/#pragma simd/\/\/#pragma simd/e |
            \ <line1>,<line2> Autoformat |
            \ <line1>,<line2>s/\/\/ *#pragma simd/#pragma simd/e |
            \ <line1>,<line2>s/\/\/ *#pragma omp/#pragma omp/e |
            \ call winrestview(mm_winview)

autocmd BufEnter *.c*,*.h,*.hpp exe 'vmap = :MAutoformat<CR>'
autocmd BufEnter *.c*,*.h,*.hpp exe 'nmap =G :.,$MAutoformat<CR>'
autocmd BufEnter *.c*,*.h,*.hpp exe 'nmap == :MAutoformat<CR>'
autocmd BufEnter *.c*,*.h,*.hpp exe 'nmap =% :.,/}/; Autoformat<CR>'
autocmd BufEnter *.c*,*.h,*.hpp exe 'nmap =<UP> :.-1,.MAutoformat<CR>'
autocmd BufEnter *.c*,*.h,*.hpp exe 'nmap =<DOWN> :.,.+1MAutoformat<CR>'

 

An Example

The magic trick is to let formatter treats the special pragma like comments, which align with the next line. The example flow is as following:

The original code with #pragma omp parallel for is formatted like this:

int foo(int* array)
{
#pragma omp parallel for
    for (int i = 0; i &amp;amp;amp;lt; 16; i++) {
        ...
    }
}

In my vim script, I first replace #pragma omp to //#pragma omp and then tell formatter to do the formatting. The pragma is treated as comment.

int foo(int* array)
{
    //#pragma omp parallel for
    for (int i = 0; i &amp;amp;amp;lt; 16; i++) {
        ...
    }
}

Finally, remove the additional slashes (only two slashes), magic!

int foo(int* array)
{
    #pragma omp parallel for
    for (int i = 0; i &amp;amp;amp;lt; 16; i++) {
        ...
    }
}

 

The Theory and Proves

This algorithm is safe for any kinds of situation, since I add two slashes and remove only two slashes with possible spaces added by formatter between // and #pragma omp. Let me give you some examples to prove this theory. We assume the formatter add extra space at the beginning of comments as the extreme case.

 

Written format of the case studies:

Original =>(Add slashes)
Before Formatting =>(Do format) After Formatting =>(Remove slashes)
Output

Case Studies:

Case 1: Normal case
#pragma omp =>
//#pragma omp => // #pragma omp =>
#pragma omp

Case 2: Commented case
//#pragma omp =>
////#pragma omp => // //#pragma omp =>
// #pragma omp

Case 3: String case
str="#pragma omp" =>
str="//#pragma omp" => str = "//#pragma omp" =>
str = "#pragma omp"

This technique can be further extended to some special cases. For example we can use {//XXX and }//XXX to create a code region that forces the codes inside to be indented.

P.S. Is there someone wants to help me fix the ugly vim script? I wish to have a simple array specifying the keywords of skipped pragma extension. It would be like my_array=(omp, simd, …)

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s