back

Ifdefs

2022-06-11

This post is specific to using ifdefs in UniVerse BASIC but the logic holds for C and C++ and I imagine any language that has compile time directives.

One of the things I ended up doing for NovaShell was adding in conditional compilation. I wanted to use the same program for both D3 and UniVerse, I also wanted to use the same file to do Windows and Linux specific things like creating directories. This meant I needed to use ifdefs. I would basically set up a few define statements and then based off those defined symbols, the compiler would compile different parts of the program.

This ultimately meant I could have UniVerse specific code in it's own block and D3 specific code in another block. I can't just do this with a simple if statement, because some of the D3 code isn't valid under UniVerse and some of the UniVerse code isn't valid under D3.

I would have loved to have it be a simple if statement as then I could simply pass in the database type in at run time and run the specific code I need, however this doesn't work and I needed to use ifdefs to control which areas get compiled.

$DEFINE DATABASE.UV
$DEFINE PLATFORM.LINUX

This will add these two variables to the symbol table.

            $IFDEF PLATFORM.LINUX
                $IFDEF DATABASE.UV
                    NSH.ENTRY<1> = 'F'
                    NSH.ENTRY<2> = '/tmp/.nsh/'
                    NSH.ENTRY<3> = '/tmp/.nsh/'
                $ENDIF
                $IFDEF DATABASE.D3
                    NSH.ENTRY<1> = 'Q'
                    NSH.ENTRY<3> = 'UNIX:/tmp/.nsh/'
                $ENDIF
            $ENDIF
            $IFDEF PLATFORM.WINDOWS
                $IFDEF DATABASE.UV
                    NSH.ENTRY<1> = 'F'
                    NSH.ENTRY<2> = 'DOS:C:\.nsh\'
                    NSH.ENTRY<3> = 'DOS:C:\.nsh\'
                $ENDIF
                $IFDEF DATABASE.D3
                    NSH.ENTRY<1> = 'Q'
                    NSH.ENTRY<3> = 'DOS:C:\.nsh\'
                $ENDIF
            $ENDIF

This chunk of code has both runtime code and compile time code. At compile time, the compiler will use the ifdef to see if it should leave that code block in the file. Ifdef stands for "if defined". For the first case, the compiler is asking if the PLATFORM.LINUX symbol is defined then include the following chunk of code.

As you can see, you can nest the ifdefs. The next ifdef checks to see if the DATABASE.UV symbol exists. If it does, include that chunk of code. In this case, based of the previous define statements, we will ultimately only leave 3 lines of code out of a possible 30 lines of code.

I used this logic in a few different places to get the NovaShell to be cross platform. I could create two programs and remove the ifdefs as the ifdefs definitely add some noise now but so much code is shared between both, that I think this makes more sense.